mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-14 02:58:08 +00:00
Merge remote-tracking branch 'origin/dev' into statsd_implem
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@ -41,6 +41,8 @@ afl-clang-lto++.8
|
|||||||
afl-cmin.8
|
afl-cmin.8
|
||||||
afl-cmin.bash.8
|
afl-cmin.bash.8
|
||||||
afl-fuzz.8
|
afl-fuzz.8
|
||||||
|
afl-c++.8
|
||||||
|
afl-cc.8
|
||||||
afl-gcc.8
|
afl-gcc.8
|
||||||
afl-g++.8
|
afl-g++.8
|
||||||
afl-gcc-fast.8
|
afl-gcc-fast.8
|
||||||
@ -51,8 +53,15 @@ afl-showmap.8
|
|||||||
afl-system-config.8
|
afl-system-config.8
|
||||||
afl-tmin.8
|
afl-tmin.8
|
||||||
afl-whatsup.8
|
afl-whatsup.8
|
||||||
|
afl-c++
|
||||||
|
afl-cc
|
||||||
|
afl-lto
|
||||||
|
afl-lto++
|
||||||
|
afl-lto++.8
|
||||||
|
afl-lto.8
|
||||||
qemu_mode/libcompcov/compcovtest
|
qemu_mode/libcompcov/compcovtest
|
||||||
qemu_mode/qemu-*
|
qemu_mode/qemu-*
|
||||||
|
qemu_mode/qemuafl
|
||||||
unicorn_mode/samples/*/\.test-*
|
unicorn_mode/samples/*/\.test-*
|
||||||
unicorn_mode/samples/*/output/
|
unicorn_mode/samples/*/output/
|
||||||
unicorn_mode/unicornafl
|
unicorn_mode/unicornafl
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -5,3 +5,6 @@
|
|||||||
[submodule "custom_mutators/Grammar-Mutator"]
|
[submodule "custom_mutators/Grammar-Mutator"]
|
||||||
path = custom_mutators/Grammar-Mutator
|
path = custom_mutators/Grammar-Mutator
|
||||||
url = https://github.com/AFLplusplus/Grammar-Mutator
|
url = https://github.com/AFLplusplus/Grammar-Mutator
|
||||||
|
[submodule "qemu_mode/qemuafl"]
|
||||||
|
path = qemu_mode/qemuafl
|
||||||
|
url = https://github.com/AFLplusplus/qemuafl.git
|
||||||
|
21
.travis.yml
21
.travis.yml
@ -6,26 +6,25 @@ branches:
|
|||||||
only:
|
only:
|
||||||
- stable
|
- stable
|
||||||
- dev
|
- dev
|
||||||
- llvm_merge
|
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# - os: linux # focal errors every run with a timeout while installing packages
|
- os: linux
|
||||||
# dist: focal
|
dist: focal
|
||||||
# env: NAME="focal-amd64" MODERN="yes" GCC="9"
|
env: NAME="focal-amd64" MODERN="yes" GCC="9"
|
||||||
- os: linux
|
- os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
env: NAME="bionic-amd64" MODERN="yes" GCC="7"
|
env: NAME="bionic-amd64" MODERN="yes" GCC="7"
|
||||||
- os: linux
|
- os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
env: NAME="xenial-amd64" MODERN="no" GCC="5" EXTRA="libtool-bin clang-6.0"
|
env: NAME="xenial-amd64" MODERN="no" GCC="5" EXTRA="libtool-bin clang-6.0"
|
||||||
- os: linux
|
# - os: linux # disabled: fatal: unable to access 'https://git.qemu.org/git/capstone/': gnutls_handshake() failed: Handshake failed
|
||||||
dist: trusty
|
# dist: trusty
|
||||||
env: NAME="trusty-amd64" MODERN="no" GCC="4.8"
|
# env: NAME="trusty-amd64" MODERN="no" GCC="4.8"
|
||||||
# - os: linux # until travis can fix this!
|
- os: linux # until travis can fix this!
|
||||||
# dist: xenial
|
dist: xenial
|
||||||
# arch: arm64
|
arch: arm64
|
||||||
# env: NAME="xenial-arm64" MODERN="no" GCC="5" EXTRA="libtool-bin clang-6.0" AFL_NO_X86="1" CPU_TARGET="aarch64"
|
env: NAME="xenial-arm64" MODERN="no" GCC="5" EXTRA="libtool-bin clang-6.0" AFL_NO_X86="1" CPU_TARGET="aarch64"
|
||||||
# - os: osx
|
# - os: osx
|
||||||
# osx_image: xcode11.2
|
# osx_image: xcode11.2
|
||||||
# env: NAME="osx" HOMEBREW_NO_ANALYTICS="1" LINK="http://releases.llvm.org/9.0.0/" NAME="clang+llvm-9.0.0-x86_64-darwin-apple"
|
# env: NAME="osx" HOMEBREW_NO_ANALYTICS="1" LINK="http://releases.llvm.org/9.0.0/" NAME="clang+llvm-9.0.0-x86_64-darwin-apple"
|
||||||
|
@ -101,7 +101,7 @@ cc_binary_host {
|
|||||||
],
|
],
|
||||||
|
|
||||||
srcs: [
|
srcs: [
|
||||||
"llvm_mode/afl-clang-fast.c",
|
"src/afl-cc.c",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ cc_binary_host {
|
|||||||
],
|
],
|
||||||
|
|
||||||
srcs: [
|
srcs: [
|
||||||
"llvm_mode/afl-clang-fast.c",
|
"src/afl-cc.c",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,6 +136,6 @@ cc_library_static {
|
|||||||
],
|
],
|
||||||
|
|
||||||
srcs: [
|
srcs: [
|
||||||
"llvm_mode/afl-llvm-rt.o.c",
|
"instrumentation/afl-llvm-rt.o.c",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
252
GNUmakefile
252
GNUmakefile
@ -24,30 +24,31 @@ BIN_PATH = $(PREFIX)/bin
|
|||||||
HELPER_PATH = $(PREFIX)/lib/afl
|
HELPER_PATH = $(PREFIX)/lib/afl
|
||||||
DOC_PATH = $(PREFIX)/share/doc/afl
|
DOC_PATH = $(PREFIX)/share/doc/afl
|
||||||
MISC_PATH = $(PREFIX)/share/afl
|
MISC_PATH = $(PREFIX)/share/afl
|
||||||
MAN_PATH = $(PREFIX)/share/man/man8
|
MAN_PATH = $(PREFIX)/man/man8
|
||||||
|
|
||||||
PROGNAME = afl
|
PROGNAME = afl
|
||||||
VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2)
|
VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2)
|
||||||
|
|
||||||
# PROGS intentionally omit afl-as, which gets installed elsewhere.
|
# PROGS intentionally omit afl-as, which gets installed elsewhere.
|
||||||
|
|
||||||
PROGS = afl-gcc afl-g++ afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
|
PROGS = afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
|
||||||
SH_PROGS = afl-plot afl-cmin afl-cmin.bash afl-whatsup afl-system-config
|
SH_PROGS = afl-plot afl-cmin afl-cmin.bash afl-whatsup afl-system-config
|
||||||
MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8) afl-as.8
|
MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8) afl-as.8
|
||||||
|
ASAN_OPTIONS=detect_leaks=0
|
||||||
|
|
||||||
ifeq "$(findstring android, $(shell $(CC) --version 2>/dev/null))" ""
|
ifeq "$(findstring android, $(shell $(CC) --version 2>/dev/null))" ""
|
||||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||||
CFLAGS_FLTO ?= -flto=full
|
CFLAGS_FLTO ?= -flto=full
|
||||||
else
|
else
|
||||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||||
CFLAGS_FLTO ?= -flto=thin
|
CFLAGS_FLTO ?= -flto=thin
|
||||||
else
|
else
|
||||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||||
CFLAGS_FLTO ?= -flto
|
CFLAGS_FLTO ?= -flto
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -fno-move-loop-invariants -fdisable-tree-cunrolli -x c - -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -fno-move-loop-invariants -fdisable-tree-cunrolli -x c - -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||||
SPECIAL_PERFORMANCE += -fno-move-loop-invariants -fdisable-tree-cunrolli
|
SPECIAL_PERFORMANCE += -fno-move-loop-invariants -fdisable-tree-cunrolli
|
||||||
@ -61,10 +62,7 @@ ifneq "$(shell uname)" "Darwin"
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
# OS X does not like _FORTIFY_SOURCE=2
|
# OS X does not like _FORTIFY_SOURCE=2
|
||||||
# _FORTIFY_SOURCE=2 does not like -O0
|
CFLAGS_OPT += -D_FORTIFY_SOURCE=2
|
||||||
ifndef DEBUG
|
|
||||||
CFLAGS_OPT += -D_FORTIFY_SOURCE=2
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq "$(shell uname)" "SunOS"
|
ifeq "$(shell uname)" "SunOS"
|
||||||
@ -206,10 +204,7 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq "$(filter Linux GNU%,$(shell uname))" ""
|
ifneq "$(filter Linux GNU%,$(shell uname))" ""
|
||||||
# _FORTIFY_SOURCE=2 does not like -O0
|
|
||||||
ifndef DEBUG
|
|
||||||
override CFLAGS += -D_FORTIFY_SOURCE=2
|
override CFLAGS += -D_FORTIFY_SOURCE=2
|
||||||
endif
|
|
||||||
LDFLAGS += -ldl -lrt
|
LDFLAGS += -ldl -lrt
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@ -223,11 +218,7 @@ ifneq "$(findstring NetBSD, $(shell uname))" ""
|
|||||||
LDFLAGS += -lpthread
|
LDFLAGS += -lpthread
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" ""
|
TEST_CC = afl-gcc
|
||||||
TEST_CC = afl-gcc
|
|
||||||
else
|
|
||||||
TEST_CC = afl-clang
|
|
||||||
endif
|
|
||||||
|
|
||||||
COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h
|
COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h
|
||||||
|
|
||||||
@ -277,28 +268,47 @@ ifdef TEST_MMAP
|
|||||||
LDFLAGS += -Wno-deprecated-declarations
|
LDFLAGS += -Wno-deprecated-declarations
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: test_x86 test_shm test_python ready $(PROGS) afl-as test_build all_done
|
.PHONY: all
|
||||||
|
all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_build all_done
|
||||||
|
|
||||||
man: afl-gcc all $(MANPAGES)
|
.PHONY: llvm
|
||||||
|
llvm:
|
||||||
|
-$(MAKE) -f GNUmakefile.llvm
|
||||||
|
@test -e afl-cc || { echo "[-] Compiling afl-cc failed. You seem not to have a working compiler." ; exit 1; }
|
||||||
|
|
||||||
|
.PHONY: gcc_plugin
|
||||||
|
gcc_plugin:
|
||||||
|
-$(MAKE) -f GNUmakefile.gcc_plugin
|
||||||
|
|
||||||
|
.PHONY: man
|
||||||
|
man: $(MANPAGES)
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: tests
|
||||||
|
|
||||||
|
.PHONY: tests
|
||||||
tests: source-only
|
tests: source-only
|
||||||
@cd test ; ./test-all.sh
|
@cd test ; ./test-all.sh
|
||||||
@rm -f test/errors
|
@rm -f test/errors
|
||||||
|
|
||||||
|
.PHONY: performance-tests
|
||||||
performance-tests: performance-test
|
performance-tests: performance-test
|
||||||
|
.PHONY: test-performance
|
||||||
test-performance: performance-test
|
test-performance: performance-test
|
||||||
|
|
||||||
|
.PHONY: performance-test
|
||||||
performance-test: source-only
|
performance-test: source-only
|
||||||
@cd test ; ./test-performance.sh
|
@cd test ; ./test-performance.sh
|
||||||
|
|
||||||
|
|
||||||
# hint: make targets are also listed in the top level README.md
|
# hint: make targets are also listed in the top level README.md
|
||||||
|
.PHONY: help
|
||||||
help:
|
help:
|
||||||
@echo "HELP --- the following make targets exist:"
|
@echo "HELP --- the following make targets exist:"
|
||||||
@echo "=========================================="
|
@echo "=========================================="
|
||||||
@echo "all: just the main afl++ binaries"
|
@echo "all: just the main afl++ binaries"
|
||||||
@echo "binary-only: everything for binary-only fuzzing: qemu_mode, unicorn_mode, libdislocator, libtokencap"
|
@echo "binary-only: everything for binary-only fuzzing: qemu_mode, unicorn_mode, libdislocator, libtokencap"
|
||||||
@echo "source-only: everything for source code fuzzing: llvm_mode, gcc_plugin, libdislocator, libtokencap"
|
@echo "source-only: everything for source code fuzzing: gcc_plugin, libdislocator, libtokencap"
|
||||||
@echo "distrib: everything (for both binary-only and source code fuzzing)"
|
@echo "distrib: everything (for both binary-only and source code fuzzing)"
|
||||||
@echo "man: creates simple man pages from the help option of the programs"
|
@echo "man: creates simple man pages from the help option of the programs"
|
||||||
@echo "install: installs everything you have compiled with the build option above"
|
@echo "install: installs everything you have compiled with the build option above"
|
||||||
@ -322,8 +332,8 @@ help:
|
|||||||
@echo "=========================================="
|
@echo "=========================================="
|
||||||
@echo e.g.: make ASAN_BUILD=1
|
@echo e.g.: make ASAN_BUILD=1
|
||||||
|
|
||||||
|
.PHONY: test_x86
|
||||||
ifndef AFL_NO_X86
|
ifndef AFL_NO_X86
|
||||||
|
|
||||||
test_x86:
|
test_x86:
|
||||||
@echo "[*] Checking for the default compiler cc..."
|
@echo "[*] Checking for the default compiler cc..."
|
||||||
@type $(CC) >/dev/null || ( echo; echo "Oops, looks like there is no compiler '"$(CC)"' in your path."; echo; echo "Don't panic! You can restart with '"$(_)" CC=<yourCcompiler>'."; echo; exit 1 )
|
@type $(CC) >/dev/null || ( echo; echo "Oops, looks like there is no compiler '"$(CC)"' in your path."; echo; echo "Don't panic! You can restart with '"$(_)" CC=<yourCcompiler>'."; echo; exit 1 )
|
||||||
@ -332,154 +342,134 @@ test_x86:
|
|||||||
@echo "[*] Checking for the ability to compile x86 code..."
|
@echo "[*] Checking for the ability to compile x86 code..."
|
||||||
@echo 'main() { __asm__("xorb %al, %al"); }' | $(CC) $(CFLAGS) -w -x c - -o .test1 || ( echo; echo "Oops, looks like your compiler can't generate x86 code."; echo; echo "Don't panic! You can use the LLVM or QEMU mode, but see docs/INSTALL first."; echo "(To ignore this error, set AFL_NO_X86=1 and try again.)"; echo; exit 1 )
|
@echo 'main() { __asm__("xorb %al, %al"); }' | $(CC) $(CFLAGS) -w -x c - -o .test1 || ( echo; echo "Oops, looks like your compiler can't generate x86 code."; echo; echo "Don't panic! You can use the LLVM or QEMU mode, but see docs/INSTALL first."; echo "(To ignore this error, set AFL_NO_X86=1 and try again.)"; echo; exit 1 )
|
||||||
@rm -f .test1
|
@rm -f .test1
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
test_x86:
|
test_x86:
|
||||||
@echo "[!] Note: skipping x86 compilation checks (AFL_NO_X86 set)."
|
@echo "[!] Note: skipping x86 compilation checks (AFL_NO_X86 set)."
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: test_shm
|
||||||
ifeq "$(SHMAT_OK)" "1"
|
ifeq "$(SHMAT_OK)" "1"
|
||||||
|
|
||||||
test_shm:
|
test_shm:
|
||||||
@echo "[+] shmat seems to be working."
|
@echo "[+] shmat seems to be working."
|
||||||
@rm -f .test2
|
@rm -f .test2
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
test_shm:
|
test_shm:
|
||||||
@echo "[-] shmat seems not to be working, switching to mmap implementation"
|
@echo "[-] shmat seems not to be working, switching to mmap implementation"
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: test_python
|
||||||
ifeq "$(PYTHON_OK)" "1"
|
ifeq "$(PYTHON_OK)" "1"
|
||||||
|
|
||||||
test_python:
|
test_python:
|
||||||
@rm -f .test 2> /dev/null
|
@rm -f .test 2> /dev/null
|
||||||
@echo "[+] $(PYTHON_VERSION) support seems to be working."
|
@echo "[+] $(PYTHON_VERSION) support seems to be working."
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
test_python:
|
test_python:
|
||||||
@echo "[-] You seem to need to install the package python3-dev, python2-dev or python-dev (and perhaps python[23]-apt), but it is optional so we continue"
|
@echo "[-] You seem to need to install the package python3-dev, python2-dev or python-dev (and perhaps python[23]-apt), but it is optional so we continue"
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: ready
|
||||||
ready:
|
ready:
|
||||||
@echo "[+] Everything seems to be working, ready to compile."
|
@echo "[+] Everything seems to be working, ready to compile."
|
||||||
|
|
||||||
afl-g++: afl-gcc
|
|
||||||
|
|
||||||
afl-gcc: src/afl-gcc.c $(COMM_HDR) | test_x86
|
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
|
||||||
set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $$i; done
|
|
||||||
|
|
||||||
afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
|
afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||||
ln -sf afl-as as
|
@ln -sf afl-as as
|
||||||
|
|
||||||
src/afl-performance.o : $(COMM_HDR) src/afl-performance.c include/hash.h
|
src/afl-performance.o : $(COMM_HDR) src/afl-performance.c include/hash.h
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) -Iinclude $(SPECIAL_PERFORMANCE) -O3 -fno-unroll-loops -c src/afl-performance.c -o src/afl-performance.o
|
$(CC) -Iinclude $(SPECIAL_PERFORMANCE) -O3 -fno-unroll-loops -c src/afl-performance.c -o src/afl-performance.o
|
||||||
|
|
||||||
src/afl-common.o : $(COMM_HDR) src/afl-common.c include/common.h
|
src/afl-common.o : $(COMM_HDR) src/afl-common.c include/common.h
|
||||||
$(CC) $(CFLAGS) $(CFLAGS_FLTO) $(CPPFLAGS) -c src/afl-common.c -o src/afl-common.o
|
$(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-common.c -o src/afl-common.o
|
||||||
|
|
||||||
src/afl-forkserver.o : $(COMM_HDR) src/afl-forkserver.c include/forkserver.h
|
src/afl-forkserver.o : $(COMM_HDR) src/afl-forkserver.c include/forkserver.h
|
||||||
$(CC) $(CFLAGS) $(CFLAGS_FLTO) $(CPPFLAGS) -c src/afl-forkserver.c -o src/afl-forkserver.o
|
$(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-forkserver.c -o src/afl-forkserver.o
|
||||||
|
|
||||||
src/afl-sharedmem.o : $(COMM_HDR) src/afl-sharedmem.c include/sharedmem.h
|
src/afl-sharedmem.o : $(COMM_HDR) src/afl-sharedmem.c include/sharedmem.h
|
||||||
$(CC) $(CFLAGS) $(CFLAGS_FLTO) $(CPPFLAGS) -c src/afl-sharedmem.c -o src/afl-sharedmem.o
|
$(CC) $(CFLAGS) $(CFLAGS_FLTO) -c src/afl-sharedmem.c -o src/afl-sharedmem.o
|
||||||
|
|
||||||
afl-fuzz: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o | test_x86
|
afl-fuzz: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o | test_x86
|
||||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) $(CPPFLAGS) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS)
|
$(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)
|
||||||
|
|
||||||
afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
|
afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86
|
afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o $(COMM_HDR) | test_x86
|
afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-performance.o -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
afl-gotcpu: src/afl-gotcpu.c src/afl-common.o $(COMM_HDR) | test_x86
|
afl-gotcpu: src/afl-gotcpu.c src/afl-common.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(CPPFLAGS) src/$@.c src/afl-common.o -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
.PHONY: document
|
||||||
|
document: afl-fuzz-document
|
||||||
|
|
||||||
# document all mutations and only do one run (use with only one input file!)
|
# document all mutations and only do one run (use with only one input file!)
|
||||||
document: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-performance.o | test_x86
|
afl-fuzz-document: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-performance.o | test_x86
|
||||||
$(CC) -D_DEBUG=\"1\" -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) $(CPPFLAGS) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.c src/afl-performance.o -o afl-fuzz-document $(PYFLAGS) $(LDFLAGS)
|
$(CC) -D_DEBUG=\"1\" -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.c src/afl-performance.o -o afl-fuzz-document $(PYFLAGS) $(LDFLAGS)
|
||||||
|
|
||||||
test/unittests/unit_maybe_alloc.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_maybe_alloc.c $(AFL_FUZZ_FILES)
|
test/unittests/unit_maybe_alloc.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_maybe_alloc.c $(AFL_FUZZ_FILES)
|
||||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_maybe_alloc.c -o test/unittests/unit_maybe_alloc.o
|
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_maybe_alloc.c -o test/unittests/unit_maybe_alloc.o
|
||||||
|
|
||||||
unit_maybe_alloc: test/unittests/unit_maybe_alloc.o
|
unit_maybe_alloc: test/unittests/unit_maybe_alloc.o
|
||||||
@$(CC) $(CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_maybe_alloc.o -o test/unittests/unit_maybe_alloc $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
@$(CC) $(CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_maybe_alloc.o -o test/unittests/unit_maybe_alloc $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||||
./test/unittests/unit_maybe_alloc
|
./test/unittests/unit_maybe_alloc
|
||||||
|
|
||||||
test/unittests/unit_hash.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_hash.c $(AFL_FUZZ_FILES) src/afl-performance.o
|
test/unittests/unit_hash.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_hash.c $(AFL_FUZZ_FILES) src/afl-performance.o
|
||||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_hash.c -o test/unittests/unit_hash.o
|
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_hash.c -o test/unittests/unit_hash.o
|
||||||
|
|
||||||
unit_hash: test/unittests/unit_hash.o src/afl-performance.o
|
unit_hash: test/unittests/unit_hash.o src/afl-performance.o
|
||||||
@$(CC) $(CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_hash $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
@$(CC) $(CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_hash $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||||
./test/unittests/unit_hash
|
./test/unittests/unit_hash
|
||||||
|
|
||||||
test/unittests/unit_rand.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_rand.c $(AFL_FUZZ_FILES) src/afl-performance.o
|
test/unittests/unit_rand.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_rand.c $(AFL_FUZZ_FILES) src/afl-performance.o
|
||||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_rand.c -o test/unittests/unit_rand.o
|
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_rand.c -o test/unittests/unit_rand.o
|
||||||
|
|
||||||
unit_rand: test/unittests/unit_rand.o src/afl-common.o src/afl-performance.o
|
unit_rand: test/unittests/unit_rand.o src/afl-common.o src/afl-performance.o
|
||||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_rand $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf $^ -o test/unittests/unit_rand $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||||
./test/unittests/unit_rand
|
./test/unittests/unit_rand
|
||||||
|
|
||||||
test/unittests/unit_list.o : $(COMM_HDR) include/list.h test/unittests/unit_list.c $(AFL_FUZZ_FILES)
|
test/unittests/unit_list.o : $(COMM_HDR) include/list.h test/unittests/unit_list.c $(AFL_FUZZ_FILES)
|
||||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_list.c -o test/unittests/unit_list.o
|
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_list.c -o test/unittests/unit_list.o
|
||||||
|
|
||||||
unit_list: test/unittests/unit_list.o
|
unit_list: test/unittests/unit_list.o
|
||||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_list.o -o test/unittests/unit_list $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_list.o -o test/unittests/unit_list $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||||
./test/unittests/unit_list
|
./test/unittests/unit_list
|
||||||
|
|
||||||
test/unittests/unit_preallocable.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_preallocable.c $(AFL_FUZZ_FILES)
|
test/unittests/unit_preallocable.o : $(COMM_HDR) include/alloc-inl.h test/unittests/unit_preallocable.c $(AFL_FUZZ_FILES)
|
||||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -c test/unittests/unit_preallocable.c -o test/unittests/unit_preallocable.o
|
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -c test/unittests/unit_preallocable.c -o test/unittests/unit_preallocable.o
|
||||||
|
|
||||||
unit_preallocable: test/unittests/unit_preallocable.o
|
unit_preallocable: test/unittests/unit_preallocable.o
|
||||||
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) $(CPPFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_preallocable.o -o test/unittests/unit_preallocable $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
@$(CC) $(CFLAGS) $(ASAN_CFLAGS) -Wl,--wrap=exit -Wl,--wrap=printf test/unittests/unit_preallocable.o -o test/unittests/unit_preallocable $(LDFLAGS) $(ASAN_LDFLAGS) -lcmocka
|
||||||
./test/unittests/unit_preallocable
|
./test/unittests/unit_preallocable
|
||||||
|
|
||||||
|
.PHONY: unit_clean
|
||||||
unit_clean:
|
unit_clean:
|
||||||
@rm -f ./test/unittests/unit_preallocable ./test/unittests/unit_list ./test/unittests/unit_maybe_alloc test/unittests/*.o
|
@rm -f ./test/unittests/unit_preallocable ./test/unittests/unit_list ./test/unittests/unit_maybe_alloc test/unittests/*.o
|
||||||
|
|
||||||
|
.PHONY: unit
|
||||||
ifneq "$(shell uname)" "Darwin"
|
ifneq "$(shell uname)" "Darwin"
|
||||||
|
unit: unit_maybe_alloc unit_preallocable unit_list unit_clean unit_rand unit_hash
|
||||||
unit: unit_maybe_alloc unit_preallocable unit_list unit_clean unit_rand unit_hash
|
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
unit:
|
unit:
|
||||||
@echo [-] unit tests are skipped on Darwin \(lacks GNU linker feature --wrap\)
|
@echo [-] unit tests are skipped on Darwin \(lacks GNU linker feature --wrap\)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: code-format
|
||||||
code-format:
|
code-format:
|
||||||
./.custom-format.py -i src/*.c
|
./.custom-format.py -i src/*.c
|
||||||
./.custom-format.py -i include/*.h
|
./.custom-format.py -i include/*.h
|
||||||
./.custom-format.py -i libdislocator/*.c
|
./.custom-format.py -i libdislocator/*.c
|
||||||
./.custom-format.py -i libtokencap/*.c
|
./.custom-format.py -i libtokencap/*.c
|
||||||
./.custom-format.py -i llvm_mode/*.c
|
./.custom-format.py -i instrumentation/*.h
|
||||||
./.custom-format.py -i llvm_mode/*.h
|
./.custom-format.py -i instrumentation/*.cc
|
||||||
./.custom-format.py -i llvm_mode/*.cc
|
./.custom-format.py -i instrumentation/*.c
|
||||||
./.custom-format.py -i gcc_plugin/*.c
|
./.custom-format.py -i custom_mutators/*/*.c*
|
||||||
@#./.custom-format.py -i gcc_plugin/*.h
|
|
||||||
./.custom-format.py -i gcc_plugin/*.cc
|
|
||||||
./.custom-format.py -i custom_mutators/*/*.c
|
|
||||||
@#./.custom-format.py -i custom_mutators/*/*.h # destroys input.h :-(
|
@#./.custom-format.py -i custom_mutators/*/*.h # destroys input.h :-(
|
||||||
./.custom-format.py -i examples/*/*.c
|
./.custom-format.py -i examples/*/*.c*
|
||||||
./.custom-format.py -i examples/*/*.h
|
./.custom-format.py -i examples/*/*.h
|
||||||
./.custom-format.py -i test/*.c
|
./.custom-format.py -i test/*.c
|
||||||
./.custom-format.py -i qemu_mode/patches/*.h
|
|
||||||
./.custom-format.py -i qemu_mode/libcompcov/*.c
|
./.custom-format.py -i qemu_mode/libcompcov/*.c
|
||||||
./.custom-format.py -i qemu_mode/libcompcov/*.cc
|
./.custom-format.py -i qemu_mode/libcompcov/*.cc
|
||||||
./.custom-format.py -i qemu_mode/libcompcov/*.h
|
./.custom-format.py -i qemu_mode/libcompcov/*.h
|
||||||
@ -489,38 +479,39 @@ code-format:
|
|||||||
./.custom-format.py -i *.c
|
./.custom-format.py -i *.c
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: test_build
|
||||||
ifndef AFL_NO_X86
|
ifndef AFL_NO_X86
|
||||||
|
test_build: afl-cc afl-as afl-showmap
|
||||||
test_build: afl-gcc afl-as afl-showmap
|
|
||||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||||
@unset AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_DEBUG=1 AFL_INST_RATIO=100 AFL_AS_FORCE_INSTRUMENT=1 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) 2>&1 | grep 'afl-as' >/dev/null || (echo "Oops, afl-as did not get called from "$(TEST_CC)". This is normally achieved by "$(CC)" honoring the -B option."; exit 1 )
|
@unset AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_DEBUG=1 AFL_INST_RATIO=100 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) 2>&1 | grep 'afl-as' >/dev/null || (echo "Oops, afl-as did not get called from "$(TEST_CC)". This is normally achieved by "$(CC)" honoring the -B option."; exit 1 )
|
||||||
ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
|
ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
|
||||||
echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
|
echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||||
@rm -f test-instr
|
@rm -f test-instr
|
||||||
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
|
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation 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
|
||||||
|
@echo
|
||||||
@echo "[+] All right, the instrumentation seems to be working!"
|
@echo "[+] All right, the instrumentation seems to be working!"
|
||||||
|
|
||||||
else
|
else
|
||||||
|
test_build: afl-cc afl-as afl-showmap
|
||||||
test_build: afl-gcc afl-as afl-showmap
|
|
||||||
@echo "[!] Note: skipping build tests (you may need to use LLVM or QEMU mode)."
|
@echo "[!] Note: skipping build tests (you may need to use LLVM or QEMU mode)."
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: all_done
|
||||||
all_done: test_build
|
all_done: test_build
|
||||||
@if [ ! "`type clang 2>/dev/null`" = "" ]; then echo "[+] LLVM users: see llvm_mode/README.md for a faster alternative to afl-gcc."; fi
|
@test -e afl-cc && echo "[+] Main compiler 'afl-cc' successfully built!" || { echo "[-] Main compiler 'afl-cc' failed to built, set up a working build environment first!" ; exit 1 ; }
|
||||||
|
@test -e cmplog-instructions-pass.so && echo "[+] LLVM mode for 'afl-cc' successfully built!" || echo "[-] LLVM mode for 'afl-cc' failed to built, likely you either have not llvm installed or you have not set LLVM_CONFIG pointing to e.g. llvm-config-11. See instrumenation/README.llvm.md how to do this. Highly recommended!"
|
||||||
|
@test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode for 'afl-cc' successfully built!" || echo "[-] LLVM LTO mode for 'afl-cc' failed to built, this would need LLVM 11+, see instrumentation/README.lto.md how to build it"
|
||||||
|
@test -e afl-gcc-pass.so && echo "[+] gcc_plugin for 'afl-cc' successfully built!" || echo "[-] gcc_plugin for 'afl-cc' failed to built, unless you really need it that is fine - or read instrumentation/README.gcc_plugin.md how to build it"
|
||||||
@echo "[+] All done! Be sure to review the README.md - it's pretty short and useful."
|
@echo "[+] All done! Be sure to review the README.md - it's pretty short and useful."
|
||||||
@if [ "`uname`" = "Darwin" ]; then printf "\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\n\n"; fi
|
@if [ "`uname`" = "Darwin" ]; then printf "\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\n\n"; fi
|
||||||
@! tty <&1 >/dev/null || printf "\033[0;30mNOTE: If you can read this, your terminal probably uses white background.\nThis will make the UI hard to read. See docs/status_screen.md for advice.\033[0m\n" 2>/dev/null
|
@! tty <&1 >/dev/null || printf "\033[0;30mNOTE: If you can read this, your terminal probably uses white background.\nThis will make the UI hard to read. See docs/status_screen.md for advice.\033[0m\n" 2>/dev/null
|
||||||
|
|
||||||
.NOTPARALLEL: clean all
|
.NOTPARALLEL: clean all
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-*
|
rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-* afl-gcc afl-g++ test/unittests/unit_hash test/unittests/unit_rand
|
||||||
rm -rf out_dir qemu_mode/qemu-3.1.1 *.dSYM */*.dSYM
|
-$(MAKE) -f GNUmakefile.llvm clean
|
||||||
-$(MAKE) -C llvm_mode clean
|
-$(MAKE) -f GNUmakefile.gcc_plugin clean
|
||||||
-$(MAKE) -C gcc_plugin clean
|
|
||||||
$(MAKE) -C libdislocator clean
|
$(MAKE) -C libdislocator clean
|
||||||
$(MAKE) -C libtokencap clean
|
$(MAKE) -C libtokencap clean
|
||||||
$(MAKE) -C examples/afl_network_proxy clean
|
$(MAKE) -C examples/afl_network_proxy clean
|
||||||
@ -528,30 +519,34 @@ clean:
|
|||||||
$(MAKE) -C examples/argv_fuzzing clean
|
$(MAKE) -C examples/argv_fuzzing clean
|
||||||
$(MAKE) -C qemu_mode/unsigaction clean
|
$(MAKE) -C qemu_mode/unsigaction clean
|
||||||
$(MAKE) -C qemu_mode/libcompcov clean
|
$(MAKE) -C qemu_mode/libcompcov clean
|
||||||
rm -rf qemu_mode/qemu-3.1.1
|
|
||||||
ifeq "$(IN_REPO)" "1"
|
ifeq "$(IN_REPO)" "1"
|
||||||
|
test -e qemu_mode/qemuafl/Makefile && $(MAKE) -C qemu_mode/qemuafl clean || true
|
||||||
test -e unicorn_mode/unicornafl/Makefile && $(MAKE) -C unicorn_mode/unicornafl clean || true
|
test -e unicorn_mode/unicornafl/Makefile && $(MAKE) -C unicorn_mode/unicornafl clean || true
|
||||||
else
|
else
|
||||||
rm -rf qemu_mode/qemu-3.1.1.tar.xz
|
rm -rf qemu_mode/qemuafl
|
||||||
rm -rf unicorn_mode/unicornafl
|
rm -rf unicorn_mode/unicornafl
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: deepclean
|
||||||
deepclean: clean
|
deepclean: clean
|
||||||
rm -rf qemu_mode/qemu-3.1.1.tar.xz
|
|
||||||
rm -rf unicorn_mode/unicornafl
|
rm -rf unicorn_mode/unicornafl
|
||||||
git reset --hard >/dev/null 2>&1 || true
|
rm -rf qemu_mode/qemuafl
|
||||||
|
# NEVER EVER ACTIVATE THAT!!!!! git reset --hard >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
.PHONY: distrib
|
||||||
distrib: all
|
distrib: all
|
||||||
-$(MAKE) -C llvm_mode
|
-$(MAKE) -f GNUmakefile.llvm
|
||||||
-$(MAKE) -C gcc_plugin
|
-$(MAKE) -f GNUmakefile.gcc_plugin
|
||||||
$(MAKE) -C libdislocator
|
$(MAKE) -C libdislocator
|
||||||
$(MAKE) -C libtokencap
|
$(MAKE) -C libtokencap
|
||||||
|
$(MAKE) -C examples/aflpp_driver
|
||||||
$(MAKE) -C examples/afl_network_proxy
|
$(MAKE) -C examples/afl_network_proxy
|
||||||
$(MAKE) -C examples/socket_fuzzing
|
$(MAKE) -C examples/socket_fuzzing
|
||||||
$(MAKE) -C examples/argv_fuzzing
|
$(MAKE) -C examples/argv_fuzzing
|
||||||
-cd qemu_mode && sh ./build_qemu_support.sh
|
-cd qemu_mode && sh ./build_qemu_support.sh
|
||||||
cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh
|
-cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh
|
||||||
|
|
||||||
|
.PHONY: binary-only
|
||||||
binary-only: all
|
binary-only: all
|
||||||
$(MAKE) -C libdislocator
|
$(MAKE) -C libdislocator
|
||||||
$(MAKE) -C libtokencap
|
$(MAKE) -C libtokencap
|
||||||
@ -559,22 +554,20 @@ binary-only: all
|
|||||||
$(MAKE) -C examples/socket_fuzzing
|
$(MAKE) -C examples/socket_fuzzing
|
||||||
$(MAKE) -C examples/argv_fuzzing
|
$(MAKE) -C examples/argv_fuzzing
|
||||||
-cd qemu_mode && sh ./build_qemu_support.sh
|
-cd qemu_mode && sh ./build_qemu_support.sh
|
||||||
cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh
|
-cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh
|
||||||
|
|
||||||
|
.PHONY: source-only
|
||||||
source-only: all
|
source-only: all
|
||||||
-$(MAKE) -C llvm_mode
|
-$(MAKE) -f GNUmakefile.llvm
|
||||||
-$(MAKE) -C gcc_plugin
|
-$(MAKE) -f GNUmakefile.gcc_plugin
|
||||||
$(MAKE) -C libdislocator
|
$(MAKE) -C libdislocator
|
||||||
$(MAKE) -C libtokencap
|
$(MAKE) -C libtokencap
|
||||||
@#$(MAKE) -C examples/afl_network_proxy
|
$(MAKE) -C examples/aflpp_driver
|
||||||
@#$(MAKE) -C examples/socket_fuzzing
|
|
||||||
@#$(MAKE) -C examples/argv_fuzzing
|
|
||||||
|
|
||||||
%.8: %
|
%.8: %
|
||||||
@echo .TH $* 8 $(BUILD_DATE) "afl++" > $@
|
@echo .TH $* 8 $(BUILD_DATE) "afl++" > $@
|
||||||
@echo .SH NAME >> $@
|
@echo .SH NAME >> $@
|
||||||
@printf "%s" ".B $* \- " >> $@
|
@echo .B $* >> $@
|
||||||
@./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> $@
|
|
||||||
@echo >> $@
|
@echo >> $@
|
||||||
@echo .SH SYNOPSIS >> $@
|
@echo .SH SYNOPSIS >> $@
|
||||||
@./$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> $@
|
@./$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> $@
|
||||||
@ -590,30 +583,29 @@ source-only: all
|
|||||||
@echo .SH LICENSE >> $@
|
@echo .SH LICENSE >> $@
|
||||||
@echo Apache License Version 2.0, January 2004 >> $@
|
@echo Apache License Version 2.0, January 2004 >> $@
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
install: all $(MANPAGES)
|
install: all $(MANPAGES)
|
||||||
install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
|
@install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
|
||||||
rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh
|
@rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh
|
||||||
|
@rm -f $${DESTDIR}$(BIN_PATH)/afl-as
|
||||||
|
@rm -f $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o
|
||||||
install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH)
|
install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH)
|
||||||
rm -f $${DESTDIR}$(BIN_PATH)/afl-as
|
@if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi
|
||||||
if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi
|
@if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi
|
||||||
if [ -f afl-gcc-fast ]; then set e; install -m 755 afl-gcc-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-gcc-fast $${DESTDIR}$(BIN_PATH)/afl-g++-fast; install -m 755 afl-gcc-pass.so afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f libtokencap.so ]; then set -e; install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH); fi
|
||||||
if [ -f afl-clang-fast ]; then $(MAKE) -C llvm_mode install; fi
|
@if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi
|
||||||
if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f afl-fuzz-document ]; then set -e; install -m 755 afl-fuzz-document $${DESTDIR}$(BIN_PATH); fi
|
||||||
if [ -f libtokencap.so ]; then set -e; install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C examples/socket_fuzzing install; fi
|
||||||
if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C examples/argv_fuzzing install; fi
|
||||||
if [ -f afl-fuzz-document ]; then set -e; install -m 755 afl-fuzz-document $${DESTDIR}$(BIN_PATH); fi
|
@if [ -f examples/afl_network_proxy/afl-network-server ]; then $(MAKE) -C examples/afl_network_proxy install; fi
|
||||||
if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C examples/socket_fuzzing install; fi
|
@if [ -f examples/aflpp_driver/libAFLDriver.a ]; then set -e; install -m 644 examples/aflpp_driver/libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi
|
||||||
if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C examples/argv_fuzzing install; fi
|
@if [ -f examples/aflpp_driver/libAFLQemuDriver.a ]; then set -e; install -m 644 examples/aflpp_driver/libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); fi
|
||||||
if [ -f examples/afl_network_proxy/afl-network-server ]; then $(MAKE) -C examples/afl_network_proxy install; fi
|
-$(MAKE) -f GNUmakefile.llvm install
|
||||||
if [ -f libAFLDriver.a ]; then install -m 644 libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi
|
-$(MAKE) -f GNUmakefile.gcc_plugin install
|
||||||
if [ -f libAFLQemuDriver.a ]; then install -m 644 libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); fi
|
ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc
|
||||||
|
ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-g++
|
||||||
set -e; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-g++
|
@mkdir -m 0755 -p ${DESTDIR}$(MAN_PATH)
|
||||||
set -e; if [ -f afl-clang-fast ] ; then ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang++ ; else ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang++; fi
|
|
||||||
|
|
||||||
mkdir -m 0755 -p ${DESTDIR}$(MAN_PATH)
|
|
||||||
install -m0644 *.8 ${DESTDIR}$(MAN_PATH)
|
install -m0644 *.8 ${DESTDIR}$(MAN_PATH)
|
||||||
|
|
||||||
install -m 755 afl-as $${DESTDIR}$(HELPER_PATH)
|
install -m 755 afl-as $${DESTDIR}$(HELPER_PATH)
|
||||||
ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as
|
ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as
|
||||||
install -m 644 docs/*.md $${DESTDIR}$(DOC_PATH)
|
install -m 644 docs/*.md $${DESTDIR}$(DOC_PATH)
|
||||||
|
@ -26,17 +26,17 @@ BIN_PATH ?= $(PREFIX)/bin
|
|||||||
DOC_PATH ?= $(PREFIX)/share/doc/afl
|
DOC_PATH ?= $(PREFIX)/share/doc/afl
|
||||||
MAN_PATH ?= $(PREFIX)/share/man/man8
|
MAN_PATH ?= $(PREFIX)/share/man/man8
|
||||||
|
|
||||||
VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2)
|
VERSION = $(shell grep '^$(HASH)define VERSION ' ./config.h | cut -d '"' -f2)
|
||||||
|
|
||||||
CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2
|
CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2
|
||||||
CFLAGS_SAFE := -Wall -I../include -Wno-pointer-sign \
|
CFLAGS_SAFE := -Wall -Iinclude -Wno-pointer-sign \
|
||||||
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
||||||
-DGCC_VERSION=\"$(GCCVER)\" -DGCC_BINDIR=\"$(GCCBINDIR)\" \
|
-DGCC_VERSION=\"$(GCCVER)\" -DGCC_BINDIR=\"$(GCCBINDIR)\" \
|
||||||
-Wno-unused-function
|
-Wno-unused-function
|
||||||
override CFLAGS += $(CFLAGS_SAFE)
|
override CFLAGS += $(CFLAGS_SAFE)
|
||||||
|
|
||||||
CXXFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2
|
CXXFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2
|
||||||
CXXEFLAGS := $(CXXFLAGS) -Wall
|
CXXEFLAGS := $(CXXFLAGS) -Wall -std=c++11
|
||||||
|
|
||||||
CC ?= gcc
|
CC ?= gcc
|
||||||
CXX ?= g++
|
CXX ?= g++
|
||||||
@ -51,6 +51,11 @@ ifeq "clang++" "$(CXX)"
|
|||||||
CXX = g++
|
CXX = g++
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq "$(findstring Foundation,$(shell $(CC) --version))" ""
|
||||||
|
CC = gcc
|
||||||
|
CXX = g++
|
||||||
|
endif
|
||||||
|
|
||||||
PLUGIN_FLAGS = -fPIC -fno-rtti -I"$(shell $(CC) -print-file-name=plugin)/include"
|
PLUGIN_FLAGS = -fPIC -fno-rtti -I"$(shell $(CC) -print-file-name=plugin)/include"
|
||||||
HASH=\#
|
HASH=\#
|
||||||
|
|
||||||
@ -80,91 +85,89 @@ ifeq "$(shell uname -s)" "SunOS"
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
PROGS = ../afl-gcc-fast ../afl-gcc-pass.so ../afl-gcc-rt.o
|
PROGS = ./afl-gcc-pass.so
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: test_shm test_deps $(PROGS) test_build all_done
|
||||||
|
|
||||||
all: test_shm test_deps $(PROGS) afl-gcc-fast.8 test_build all_done
|
.PHONY: test_shm
|
||||||
|
|
||||||
ifeq "$(SHMAT_OK)" "1"
|
ifeq "$(SHMAT_OK)" "1"
|
||||||
|
|
||||||
test_shm:
|
test_shm:
|
||||||
@echo "[+] shmat seems to be working."
|
@echo "[+] shmat seems to be working."
|
||||||
@rm -f .test2
|
@rm -f .test2
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
test_shm:
|
test_shm:
|
||||||
@echo "[-] shmat seems not to be working, switching to mmap implementation"
|
@echo "[-] shmat seems not to be working, switching to mmap implementation"
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: test_deps
|
||||||
test_deps:
|
test_deps:
|
||||||
@echo "[*] Checking for working '$(CC)'..."
|
@echo "[*] Checking for working '$(CC)'..."
|
||||||
@type $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
|
@command -v $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
|
||||||
# @echo "[*] Checking for gcc for plugin support..."
|
# @echo "[*] Checking for gcc for plugin support..."
|
||||||
# @$(CC) -v 2>&1 | grep -q -- --enable-plugin || ( echo "[-] Oops, this gcc has not been configured with plugin support."; exit 1 )
|
# @$(CC) -v 2>&1 | grep -q -- --enable-plugin || ( echo "[-] Oops, this gcc has not been configured with plugin support."; exit 1 )
|
||||||
@echo "[*] Checking for gcc plugin development header files..."
|
@echo "[*] Checking for gcc plugin development header files..."
|
||||||
@test -d `$(CC) -print-file-name=plugin`/include || ( echo "[-] Oops, can't find gcc header files. Be sure to install 'gcc-X-plugin-dev'."; exit 1 )
|
@test -d `$(CC) -print-file-name=plugin`/include || ( echo "[-] Oops, can't find gcc header files. Be sure to install 'gcc-X-plugin-dev'."; exit 1 )
|
||||||
@echo "[*] Checking for '../afl-showmap'..."
|
@echo "[*] Checking for './afl-showmap'..."
|
||||||
@test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 )
|
@test -f ./afl-showmap || ( echo "[-] Oops, can't find './afl-showmap'. Be sure to compile AFL first."; exit 1 )
|
||||||
@echo "[+] All set and ready to build."
|
@echo "[+] All set and ready to build."
|
||||||
|
|
||||||
afl-common.o: ../src/afl-common.c
|
afl-common.o: ./src/afl-common.c
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
../afl-gcc-fast: afl-gcc-fast.c afl-common.o | test_deps
|
./afl-gcc-pass.so: instrumentation/afl-gcc-pass.so.cc | test_deps
|
||||||
$(CC) -DAFL_GCC_CC=\"$(CC)\" -DAFL_GCC_CXX=\"$(CXX)\" $(CFLAGS) $(CPPFLAGS) $< afl-common.o -o $@ $(LDFLAGS)
|
|
||||||
ln -sf afl-gcc-fast ../afl-g++-fast
|
|
||||||
|
|
||||||
../afl-gcc-pass.so: afl-gcc-pass.so.cc | test_deps
|
|
||||||
$(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@
|
$(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@
|
||||||
|
ln -sf afl-cc afl-gcc-fast
|
||||||
|
ln -sf afl-cc afl-g++-fast
|
||||||
|
ln -sf afl-cc.8 afl-gcc-fast.8
|
||||||
|
ln -sf afl-cc.8 afl-g++-fast.8
|
||||||
|
|
||||||
../afl-gcc-rt.o: afl-gcc-rt.o.c | test_deps
|
.PHONY: test_build
|
||||||
$(CC) $(CFLAGS_SAFE) $(CPPFLAGS) -fPIC -c $< -o $@
|
|
||||||
|
|
||||||
test_build: $(PROGS)
|
test_build: $(PROGS)
|
||||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||||
unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) $(CPPFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ./afl-gcc-fast $(CFLAGS) $(CPPFLAGS) ./test-instr.c -o test-instr $(LDFLAGS)
|
||||||
# unset AFL_USE_ASAN AFL_USE_MSAN; AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr </dev/null
|
||||||
ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr0 ./test-instr </dev/null
|
echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||||
echo 1 | ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr1 ./test-instr
|
|
||||||
@rm -f test-instr
|
@rm -f test-instr
|
||||||
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
|
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation 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
|
||||||
@echo "[+] All right, the instrumentation seems to be working!"
|
@echo "[+] All right, the instrumentation seems to be working!"
|
||||||
|
|
||||||
|
.PHONY: all_done
|
||||||
all_done: test_build
|
all_done: test_build
|
||||||
@echo "[+] All done! You can now use '../afl-gcc-fast' to compile programs."
|
@echo "[+] All done! You can now use './afl-gcc-fast' to compile programs."
|
||||||
|
|
||||||
.NOTPARALLEL: clean
|
.NOTPARALLEL: clean
|
||||||
|
|
||||||
vpath % ..
|
vpath % ..
|
||||||
%.8: %
|
%.8: %
|
||||||
@echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ../$@
|
@echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ./$@
|
||||||
@echo .SH NAME >> ../$@
|
@echo .SH NAME >> ./$@
|
||||||
@echo .B $* >> ../$@
|
@echo .B $* >> ./$@
|
||||||
@echo >> ../$@
|
@echo >> ./$@
|
||||||
@echo .SH SYNOPSIS >> ../$@
|
@echo .SH SYNOPSIS >> ./$@
|
||||||
@../$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ../$@
|
@./$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ./$@
|
||||||
@echo >> ../$@
|
@echo >> ./$@
|
||||||
@echo .SH OPTIONS >> ../$@
|
@echo .SH OPTIONS >> ./$@
|
||||||
@echo .nf >> ../$@
|
@echo .nf >> ./$@
|
||||||
@../$* -h 2>&1 | tail -n +4 >> ../$@
|
@./$* -h 2>&1 | tail -n +4 >> ./$@
|
||||||
@echo >> ../$@
|
@echo >> ./$@
|
||||||
@echo .SH AUTHOR >> ../$@
|
@echo .SH AUTHOR >> ./$@
|
||||||
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> ../$@
|
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, 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 The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
|
||||||
@echo >> ../$@
|
@echo >> ./$@
|
||||||
@echo .SH LICENSE >> ../$@
|
@echo .SH LICENSE >> ./$@
|
||||||
@echo Apache License Version 2.0, January 2004 >> ../$@
|
@echo Apache License Version 2.0, January 2004 >> ./$@
|
||||||
ln -sf afl-gcc-fast.8 ../afl-g++-fast.8
|
ln -sf afl-cc.8 ./afl-g++-fast.8
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
install: all
|
install: all
|
||||||
install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH)
|
ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc-fast
|
||||||
install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH)
|
ln -sf afl-c++ $${DESTDIR}$(BIN_PATH)/afl-g++-fast
|
||||||
install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md
|
ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o
|
||||||
install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md
|
install -m 755 ./afl-gcc-pass.so $${DESTDIR}$(HELPER_PATH)
|
||||||
|
install -m 644 -T instrumentation/README.gcc_plugin.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2
|
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2
|
||||||
rm -f $(PROGS) afl-common.o ../afl-g++-fast ../afl-g*-fast.8
|
rm -f $(PROGS) afl-common.o ./afl-g++-fast ./afl-g*-fast.8 instrumentation/*.o
|
@ -26,10 +26,10 @@ DOC_PATH ?= $(PREFIX)/share/doc/afl
|
|||||||
MISC_PATH ?= $(PREFIX)/share/afl
|
MISC_PATH ?= $(PREFIX)/share/afl
|
||||||
MAN_PATH ?= $(PREFIX)/share/man/man8
|
MAN_PATH ?= $(PREFIX)/share/man/man8
|
||||||
|
|
||||||
VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2)
|
|
||||||
|
|
||||||
BUILD_DATE ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null || date -u -r "$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null || date -u "+%Y-%m-%d")
|
BUILD_DATE ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null || date -u -r "$(SOURCE_DATE_EPOCH)" "+%Y-%m-%d" 2>/dev/null || date -u "+%Y-%m-%d")
|
||||||
|
|
||||||
|
VERSION = $(shell grep '^$(HASH)define VERSION ' ./config.h | cut -d '"' -f2)
|
||||||
|
|
||||||
ifeq "$(shell uname)" "OpenBSD"
|
ifeq "$(shell uname)" "OpenBSD"
|
||||||
LLVM_CONFIG ?= $(BIN_PATH)/llvm-config
|
LLVM_CONFIG ?= $(BIN_PATH)/llvm-config
|
||||||
HAS_OPT = $(shell test -x $(BIN_PATH)/opt && echo 0 || echo 1)
|
HAS_OPT = $(shell test -x $(BIN_PATH)/opt && echo 0 || echo 1)
|
||||||
@ -41,6 +41,7 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' )
|
LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' )
|
||||||
|
LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//' )
|
||||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-3]|^19' && echo 1 || echo 0 )
|
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-3]|^19' && echo 1 || echo 0 )
|
||||||
LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 )
|
LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 )
|
||||||
LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]' && echo 1 || echo 0 )
|
LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]' && echo 1 || echo 0 )
|
||||||
@ -78,13 +79,13 @@ ifeq "$(LLVM_TOO_OLD)" "1"
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq "$(LLVM_HAVE_LTO)" "1"
|
ifeq "$(LLVM_HAVE_LTO)" "1"
|
||||||
$(info [+] llvm_mode detected llvm 11+, enabling afl-clang-lto LTO implementation)
|
$(info [+] llvm_mode detected llvm 11+, enabling afl-lto LTO implementation)
|
||||||
LLVM_LTO = 1
|
LLVM_LTO = 1
|
||||||
#TEST_MMAP = 1
|
#TEST_MMAP = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq "$(LLVM_LTO)" "0"
|
ifeq "$(LLVM_LTO)" "0"
|
||||||
$(info [+] llvm_mode detected llvm < 11, afl-clang-lto LTO will not be build.)
|
$(info [+] llvm_mode detected llvm < 11, afl-lto LTO will not be build.)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq "$(LLVM_APPLE_XCODE)" "1"
|
ifeq "$(LLVM_APPLE_XCODE)" "1"
|
||||||
@ -115,9 +116,9 @@ ifeq "$(shell test -e $(CC) || echo 1 )" "1"
|
|||||||
endif
|
endif
|
||||||
# llvm-config --bindir may not providing a valid path, so ...
|
# llvm-config --bindir may not providing a valid path, so ...
|
||||||
ifeq "$(shell test -e $(CXX) || echo 1 )" "1"
|
ifeq "$(shell test -e $(CXX) || echo 1 )" "1"
|
||||||
# however we must ensure that this is not a "CC=gcc make"
|
# however we must ensure that this is not a "CXX=g++ make"
|
||||||
ifeq "$(shell command -v $(CXX) 2> /dev/null)" ""
|
ifeq "$(shell command -v $(CXX) 2> /dev/null)" ""
|
||||||
# we do not have a valid CC variable so we try alternatives
|
# we do not have a valid CXX variable so we try alternatives
|
||||||
ifeq "$(shell test -e '$(BIN_DIR)/clang++' && echo 1)" "1"
|
ifeq "$(shell test -e '$(BIN_DIR)/clang++' && echo 1)" "1"
|
||||||
# we found one in the local install directory, lets use these
|
# we found one in the local install directory, lets use these
|
||||||
CXX = $(BIN_DIR)/clang++
|
CXX = $(BIN_DIR)/clang++
|
||||||
@ -164,12 +165,24 @@ endif
|
|||||||
# old. For these we need to use gcc/g++, so if we find REAL_CC and REAL_CXX
|
# old. For these we need to use gcc/g++, so if we find REAL_CC and REAL_CXX
|
||||||
# variable we override the compiler variables here
|
# variable we override the compiler variables here
|
||||||
ifneq "$(REAL_CC)" ""
|
ifneq "$(REAL_CC)" ""
|
||||||
CC = $(REAL_CC)
|
CC = $(REAL_CC)
|
||||||
endif
|
endif
|
||||||
ifneq "$(REAL_CXX)" ""
|
ifneq "$(REAL_CXX)" ""
|
||||||
CXX = $(REAL_CXX)
|
CXX = $(REAL_CXX)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
#
|
||||||
|
# Now it can happen that CC points to clang - but there is no clang on the
|
||||||
|
# system. Then we fall back to cc
|
||||||
|
#
|
||||||
|
ifeq "$(shell command -v $(CC) 2>/dev/null)" ""
|
||||||
|
CC = cc
|
||||||
|
endif
|
||||||
|
ifeq "$(shell command -v $(CXX) 2>/dev/null)" ""
|
||||||
|
CXX = c++
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
# After we set CC/CXX we can start makefile magic tests
|
# After we set CC/CXX we can start makefile magic tests
|
||||||
|
|
||||||
#ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
#ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||||
@ -220,15 +233,15 @@ endif
|
|||||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fdebug-prefix-map=$(CURDIR)=llvm_mode -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fdebug-prefix-map=$(CURDIR)=llvm_mode -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||||
AFL_CLANG_DEBUG_PREFIX = -fdebug-prefix-map="$(CURDIR)=llvm_mode"
|
AFL_CLANG_DEBUG_PREFIX = -fdebug-prefix-map="$(CURDIR)=llvm_mode"
|
||||||
else
|
else
|
||||||
AFL_CLANG_DEBUG_PREFIX = ""
|
AFL_CLANG_DEBUG_PREFIX =
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2
|
CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2
|
||||||
CFLAGS_SAFE := -Wall -g -Wno-pointer-sign -I ../include/ \
|
CFLAGS_SAFE := -Wall -g -Wno-pointer-sign -I ./include/ -I ./instrumentation/ \
|
||||||
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
||||||
-DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \
|
-DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \
|
||||||
-DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \
|
-DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \
|
||||||
-DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \
|
-Wno-deprecated -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \
|
||||||
-DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \
|
-DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \
|
||||||
-DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \
|
-DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \
|
||||||
-DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \
|
-DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \
|
||||||
@ -241,7 +254,7 @@ ifdef AFL_TRACE_PC
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
CXXFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2
|
CXXFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2
|
||||||
override CXXFLAGS += -Wall -g -I ../include/ \
|
override CXXFLAGS += -Wall -g -I ./include/ \
|
||||||
-DVERSION=\"$(VERSION)\" -Wno-variadic-macros
|
-DVERSION=\"$(VERSION)\" -Wno-variadic-macros
|
||||||
|
|
||||||
ifneq "$(shell $(LLVM_CONFIG) --includedir) 2> /dev/null" ""
|
ifneq "$(shell $(LLVM_CONFIG) --includedir) 2> /dev/null" ""
|
||||||
@ -283,7 +296,8 @@ ifeq "$(TEST_MMAP)" "1"
|
|||||||
LDFLAGS += -Wno-deprecated-declarations
|
LDFLAGS += -Wno-deprecated-declarations
|
||||||
endif
|
endif
|
||||||
|
|
||||||
PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-ld-lto ../afl-llvm-lto-instrumentlist.so ../afl-llvm-lto-instrumentation.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so ../cmplog-instructions-pass.so ../SanitizerCoverageLTO.so
|
PROGS_ALWAYS = ./afl-cc ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o
|
||||||
|
PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./libLLVMInsTrim.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so
|
||||||
|
|
||||||
# If prerequisites are not given, warn, do not build anything, and exit with code 0
|
# If prerequisites are not given, warn, do not build anything, and exit with code 0
|
||||||
ifeq "$(LLVMVER)" ""
|
ifeq "$(LLVMVER)" ""
|
||||||
@ -295,31 +309,31 @@ ifneq "$(LLVM_UNSUPPORTED)$(LLVM_APPLE_XCODE)" "00"
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq "$(NO_BUILD)" "1"
|
ifeq "$(NO_BUILD)" "1"
|
||||||
TARGETS = no_build
|
TARGETS = test_shm $(PROGS_ALWAYS) afl-cc.8
|
||||||
else
|
else
|
||||||
TARGETS = test_shm test_deps $(PROGS) afl-clang-fast.8 test_build all_done
|
TARGETS = test_shm test_deps $(PROGS) afl-cc.8 test_build all_done
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LLVM_MIN_4_0_1 = $(shell awk 'function tonum(ver, a) {split(ver,a,"."); return a[1]*1000000+a[2]*1000+a[3]} BEGIN { exit tonum(ARGV[1]) >= tonum(ARGV[2]) }' $(LLVMVER) 4.0.1; echo $$?)
|
LLVM_MIN_4_0_1 = $(shell awk 'function tonum(ver, a) {split(ver,a,"."); return a[1]*1000000+a[2]*1000+a[3]} BEGIN { exit tonum(ARGV[1]) >= tonum(ARGV[2]) }' $(LLVMVER) 4.0.1; echo $$?)
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
.PHONY: test_shm
|
||||||
ifeq "$(SHMAT_OK)" "1"
|
ifeq "$(SHMAT_OK)" "1"
|
||||||
|
|
||||||
test_shm:
|
test_shm:
|
||||||
@echo "[+] shmat seems to be working."
|
@echo "[+] shmat seems to be working."
|
||||||
@rm -f .test2
|
@rm -f .test2
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
test_shm:
|
test_shm:
|
||||||
@echo "[-] shmat seems not to be working, switching to mmap implementation"
|
@echo "[-] shmat seems not to be working, switching to mmap implementation"
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: no_build
|
||||||
no_build:
|
no_build:
|
||||||
@printf "%b\\n" "\\033[0;31mPrerequisites are not met, skipping build llvm_mode\\033[0m"
|
@printf "%b\\n" "\\033[0;31mPrerequisites are not met, skipping build llvm_mode\\033[0m"
|
||||||
|
|
||||||
|
.PHONY: test_deps
|
||||||
test_deps:
|
test_deps:
|
||||||
@echo "[*] Checking for working 'llvm-config'..."
|
@echo "[*] Checking for working 'llvm-config'..."
|
||||||
ifneq "$(LLVM_APPLE_XCODE)" "1"
|
ifneq "$(LLVM_APPLE_XCODE)" "1"
|
||||||
@ -333,148 +347,163 @@ ifneq "$(CLANGVER)" "$(LLVMVER)"
|
|||||||
else
|
else
|
||||||
@echo "[*] We have llvm-config version $(LLVMVER) with a clang version $(CLANGVER), good."
|
@echo "[*] We have llvm-config version $(LLVMVER) with a clang version $(CLANGVER), good."
|
||||||
endif
|
endif
|
||||||
@echo "[*] Checking for '../afl-showmap'..."
|
@echo "[*] Checking for './afl-showmap'..."
|
||||||
@test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 )
|
@test -f ./afl-showmap || ( echo "[-] Oops, can't find './afl-showmap'. Be sure to compile AFL first."; exit 1 )
|
||||||
@echo "[+] All set and ready to build."
|
@echo "[+] All set and ready to build."
|
||||||
|
|
||||||
afl-common.o: ../src/afl-common.c
|
instrumentation/afl-common.o: ./src/afl-common.c
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
../afl-clang-fast: afl-clang-fast.c afl-common.o | test_deps
|
./afl-cc: src/afl-cc.c instrumentation/afl-common.o | test_deps
|
||||||
$(CC) $(CLANG_CFL) $(CFLAGS) $(CPPFLAGS) $< afl-common.o -o $@ $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\"
|
$(CC) $(CLANG_CFL) $(CFLAGS) $(CPPFLAGS) $< instrumentation/afl-common.o -o $@ -DLLVM_MAJOR=$(LLVM_MAJOR) $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\"
|
||||||
ln -sf afl-clang-fast ../afl-clang-fast++
|
@ln -sf afl-cc ./afl-c++
|
||||||
|
@ln -sf afl-cc ./afl-gcc
|
||||||
|
@ln -sf afl-cc ./afl-g++
|
||||||
|
@ln -sf afl-cc ./afl-clang-fast
|
||||||
|
@ln -sf afl-cc ./afl-clang-fast++
|
||||||
ifneq "$(AFL_CLANG_FLTO)" ""
|
ifneq "$(AFL_CLANG_FLTO)" ""
|
||||||
ifeq "$(LLVM_LTO)" "1"
|
ifeq "$(LLVM_LTO)" "1"
|
||||||
ln -sf afl-clang-fast ../afl-clang-lto
|
@ln -sf afl-cc ./afl-clang-lto
|
||||||
ln -sf afl-clang-fast ../afl-clang-lto++
|
@ln -sf afl-cc ./afl-clang-lto++
|
||||||
|
@ln -sf afl-cc ./afl-lto
|
||||||
|
@ln -sf afl-cc ./afl-lto++
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
afl-llvm-common.o: afl-llvm-common.cc afl-llvm-common.h
|
instrumentation/afl-llvm-common.o: instrumentation/afl-llvm-common.cc instrumentation/afl-llvm-common.h
|
||||||
$(CXX) $(CFLAGS) $(CPPFLAGS) `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC -std=$(LLVM_STDCXX) -c $< -o $@
|
$(CXX) $(CFLAGS) $(CPPFLAGS) `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC -std=$(LLVM_STDCXX) -c $< -o $@
|
||||||
|
|
||||||
../libLLVMInsTrim.so: LLVMInsTrim.so.cc MarkNodes.cc afl-llvm-common.o | test_deps
|
./libLLVMInsTrim.so: instrumentation/LLVMInsTrim.so.cc instrumentation/MarkNodes.cc instrumentation/afl-llvm-common.o | test_deps
|
||||||
-$(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) afl-llvm-common.o
|
-$(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< instrumentation/MarkNodes.cc -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
|
|
||||||
../afl-llvm-pass.so: afl-llvm-pass.so.cc afl-llvm-common.o | test_deps
|
./afl-llvm-pass.so: instrumentation/afl-llvm-pass.so.cc instrumentation/afl-llvm-common.o | test_deps
|
||||||
ifeq "$(LLVM_MIN_4_0_1)" "0"
|
ifeq "$(LLVM_MIN_4_0_1)" "0"
|
||||||
$(info [!] N-gram branch coverage instrumentation is not available for llvm version $(LLVMVER))
|
$(info [!] N-gram branch coverage instrumentation is not available for llvm version $(LLVMVER))
|
||||||
endif
|
endif
|
||||||
$(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
$(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
|
|
||||||
../afl-llvm-lto-instrumentlist.so: afl-llvm-lto-instrumentlist.so.cc afl-llvm-common.o
|
./afl-llvm-lto-instrumentlist.so: instrumentation/afl-llvm-lto-instrumentlist.so.cc instrumentation/afl-llvm-common.o
|
||||||
ifeq "$(LLVM_LTO)" "1"
|
ifeq "$(LLVM_LTO)" "1"
|
||||||
$(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
$(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
../afl-ld-lto: afl-ld-lto.c
|
./afl-ld-lto: src/afl-ld-lto.c
|
||||||
ifeq "$(LLVM_LTO)" "1"
|
ifeq "$(LLVM_LTO)" "1"
|
||||||
$(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@
|
$(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
../SanitizerCoverageLTO.so: SanitizerCoverageLTO.so.cc
|
./SanitizerCoverageLTO.so: instrumentation/SanitizerCoverageLTO.so.cc
|
||||||
ifeq "$(LLVM_LTO)" "1"
|
ifeq "$(LLVM_LTO)" "1"
|
||||||
$(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
$(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
../afl-llvm-lto-instrumentation.so: afl-llvm-lto-instrumentation.so.cc afl-llvm-common.o
|
./afl-llvm-lto-instrumentation.so: instrumentation/afl-llvm-lto-instrumentation.so.cc instrumentation/afl-llvm-common.o
|
||||||
ifeq "$(LLVM_LTO)" "1"
|
ifeq "$(LLVM_LTO)" "1"
|
||||||
$(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
$(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto.o
|
$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto.o
|
||||||
@$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m64 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-64.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
|
@$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m64 -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto-64.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
|
||||||
@$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
|
@$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# laf
|
# laf
|
||||||
../split-switches-pass.so: split-switches-pass.so.cc afl-llvm-common.o | test_deps
|
./split-switches-pass.so: instrumentation/split-switches-pass.so.cc instrumentation/afl-llvm-common.o | test_deps
|
||||||
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
../compare-transform-pass.so: compare-transform-pass.so.cc afl-llvm-common.o | test_deps
|
./compare-transform-pass.so: instrumentation/compare-transform-pass.so.cc instrumentation/afl-llvm-common.o | test_deps
|
||||||
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
../split-compares-pass.so: split-compares-pass.so.cc afl-llvm-common.o | test_deps
|
./split-compares-pass.so: instrumentation/split-compares-pass.so.cc instrumentation/afl-llvm-common.o | test_deps
|
||||||
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
# /laf
|
# /laf
|
||||||
|
|
||||||
../cmplog-routines-pass.so: cmplog-routines-pass.cc afl-llvm-common.o | test_deps
|
./cmplog-routines-pass.so: instrumentation/cmplog-routines-pass.cc instrumentation/afl-llvm-common.o | test_deps
|
||||||
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
|
|
||||||
../cmplog-instructions-pass.so: cmplog-instructions-pass.cc afl-llvm-common.o | test_deps
|
./cmplog-instructions-pass.so: instrumentation/cmplog-instructions-pass.cc instrumentation/afl-llvm-common.o | test_deps
|
||||||
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o
|
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
|
|
||||||
|
afl-llvm-dict2file.so: instrumentation/afl-llvm-dict2file.so.cc instrumentation/afl-llvm-common.o | test_deps
|
||||||
|
$(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||||
|
|
||||||
|
.PHONY: document
|
||||||
document:
|
document:
|
||||||
$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt.o
|
$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -fPIC -c instrumentation/afl-compiler-rt.o.c -o ./afl-compiler-rt.o
|
||||||
@$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m32 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-32.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
@$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m32 -fPIC -c instrumentation/afl-compiler-rt.o.c -o ./afl-compiler-rt-32.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||||
@$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m64 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
@$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CPPFLAGS) $(CLANG_CFL) -O3 -Wno-unused-result -m64 -fPIC -c instrumentation/afl-compiler-rt.o.c -o ./afl-compiler-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
||||||
|
|
||||||
../afl-llvm-rt.o: afl-llvm-rt.o.c | test_deps
|
./afl-compiler-rt.o: instrumentation/afl-compiler-rt.o.c | test_deps
|
||||||
$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -fPIC -c $< -o $@
|
$(CC) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -fPIC -c $< -o $@
|
||||||
|
|
||||||
../afl-llvm-rt-32.o: afl-llvm-rt.o.c | test_deps
|
./afl-compiler-rt-32.o: instrumentation/afl-compiler-rt.o.c | test_deps
|
||||||
@printf "[*] Building 32-bit variant of the runtime (-m32)... "
|
@printf "[*] Building 32-bit variant of the runtime (-m32)... "
|
||||||
@$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
@$(CC) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; ln -sf afl-compiler-rt-32.o afl-llvm-rt-32.o; else echo "failed (that's fine)"; fi
|
||||||
|
|
||||||
../afl-llvm-rt-64.o: afl-llvm-rt.o.c | test_deps
|
./afl-compiler-rt-64.o: instrumentation/afl-compiler-rt.o.c | test_deps
|
||||||
@printf "[*] Building 64-bit variant of the runtime (-m64)... "
|
@printf "[*] Building 64-bit variant of the runtime (-m64)... "
|
||||||
@$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi
|
@$(CC) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; ln -sf afl-compiler-rt-64.o afl-llvm-rt-64.o; else echo "failed (that's fine)"; fi
|
||||||
|
|
||||||
|
.PHONY: test_build
|
||||||
test_build: $(PROGS)
|
test_build: $(PROGS)
|
||||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||||
unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_LLVM_LAF_ALL=1 ./afl-cc $(CFLAGS) $(CPPFLAGS) ./test-instr.c -o test-instr $(LDFLAGS)
|
||||||
ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
|
ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
|
||||||
echo 1 | ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr1 ./test-instr
|
echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||||
@rm -f test-instr
|
@rm -f test-instr
|
||||||
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
|
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation 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
|
||||||
@echo "[+] All right, the instrumentation seems to be working!"
|
@echo "[+] All right, the instrumentation seems to be working!"
|
||||||
|
|
||||||
|
.PHONY: all_done
|
||||||
all_done: test_build
|
all_done: test_build
|
||||||
@echo "[+] All done! You can now use '../afl-clang-fast' to compile programs."
|
@echo "[+] All done! You can now use './afl-cc' to compile programs."
|
||||||
|
|
||||||
.NOTPARALLEL: clean
|
.NOTPARALLEL: clean
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
install: all
|
install: all
|
||||||
install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
|
@install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
|
||||||
if [ -f ../afl-clang-fast -a -f ../libLLVMInsTrim.so -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../libLLVMInsTrim.so ../afl-llvm-pass.so ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f ./afl-cc ]; then set -e; install -m 755 ./afl-cc $${DESTDIR}$(BIN_PATH); ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-c++; fi
|
||||||
if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi
|
@rm -f $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt*.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt*.o
|
||||||
if [ -f ../afl-ld-lto ]; then set -e; install -m 755 ../afl-ld-lto $${DESTDIR}$(BIN_PATH); fi
|
@if [ -f ./afl-compiler-rt.o ]; then set -e; install -m 755 ./afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt.o ;fi
|
||||||
if [ -f ../afl-llvm-rt-32.o ]; then set -e; install -m 755 ../afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f ./afl-lto ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto++; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ./afl-llvm-lto-instrumentation.so ./afl-llvm-rt-lto*.o ./afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi
|
||||||
if [ -f ../afl-llvm-rt-64.o ]; then set -e; install -m 755 ../afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f ./afl-ld-lto ]; then set -e; install -m 755 ./afl-ld-lto $${DESTDIR}$(BIN_PATH); fi
|
||||||
if [ -f ../compare-transform-pass.so ]; then set -e; install -m 755 ../compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f ./afl-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-32.o ;fi
|
||||||
if [ -f ../split-compares-pass.so ]; then set -e; install -m 755 ../split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f ./afl-compiler-rt-64.o ]; then set -e; install -m 755 ./afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-64.o ; fi
|
||||||
if [ -f ../split-switches-pass.so ]; then set -e; install -m 755 ../split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f ./compare-transform-pass.so ]; then set -e; install -m 755 ./*.so $${DESTDIR}$(HELPER_PATH); fi
|
||||||
if [ -f ../cmplog-instructions-pass.so ]; then set -e; install -m 755 ../cmplog-*-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f ./compare-transform-pass.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-fast ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-fast++ ; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang++ ; fi
|
||||||
if [ -f ../SanitizerCoverageLTO.so ]; then set -e; install -m 755 ../SanitizerCoverageLTO.so $${DESTDIR}$(HELPER_PATH); fi
|
@if [ -f ./SanitizerCoverageLTO.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-lto++ ; fi
|
||||||
set -e; install -m 644 ../dynamic_list.txt $${DESTDIR}$(HELPER_PATH)
|
set -e; install -m 644 ./dynamic_list.txt $${DESTDIR}$(HELPER_PATH)
|
||||||
set -e; if [ -f ../afl-clang-fast ] ; then ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang++ ; else ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang++; fi
|
install -m 644 instrumentation/README.*.md $${DESTDIR}$(DOC_PATH)/
|
||||||
install -m 644 README.*.md $${DESTDIR}$(DOC_PATH)/
|
|
||||||
install -m 644 README.md $${DESTDIR}$(DOC_PATH)/README.llvm_mode.md
|
|
||||||
|
|
||||||
vpath % ..
|
vpath % ..
|
||||||
%.8: %
|
%.8: %
|
||||||
@echo .TH $* 8 $(BUILD_DATE) "afl++" > ../$@
|
@echo .TH $* 8 $(BUILD_DATE) "afl++" > ./$@
|
||||||
@echo .SH NAME >> ../$@
|
@echo .SH NAME >> ./$@
|
||||||
@echo -n ".B $* \- " >> ../$@
|
@printf "%s" ".B $* \- " >> ../$@
|
||||||
@../$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ../$@
|
@./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ../$@
|
||||||
@echo >> ../$@
|
@echo .B $* >> ./$@
|
||||||
@echo .SH SYNOPSIS >> ../$@
|
@echo >> ./$@
|
||||||
@../$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ../$@
|
@echo .SH SYNOPSIS >> ./$@
|
||||||
@echo >> ../$@
|
@./$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ./$@
|
||||||
@echo .SH OPTIONS >> ../$@
|
@echo >> ./$@
|
||||||
@echo .nf >> ../$@
|
@echo .SH OPTIONS >> ./$@
|
||||||
@../$* -h 2>&1 | tail -n +4 >> ../$@
|
@echo .nf >> ./$@
|
||||||
@echo >> ../$@
|
@./$* -h 2>&1 | tail -n +4 >> ./$@
|
||||||
@echo .SH AUTHOR >> ../$@
|
@echo >> ./$@
|
||||||
@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 .SH AUTHOR >> ./$@
|
||||||
@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>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> ./$@
|
||||||
@echo >> ../$@
|
@echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
|
||||||
@echo .SH LICENSE >> ../$@
|
@echo >> ./$@
|
||||||
@echo Apache License Version 2.0, January 2004 >> ../$@
|
@echo .SH LICENSE >> ./$@
|
||||||
ln -sf afl-clang-fast.8 ../afl-clang-fast++.8
|
@echo Apache License Version 2.0, January 2004 >> ./$@
|
||||||
|
@ln -sf afl-cc.8 ./afl-c++.8
|
||||||
ifneq "$(AFL_CLANG_FLTO)" ""
|
ifneq "$(AFL_CLANG_FLTO)" ""
|
||||||
ifeq "$(LLVM_LTO)" "1"
|
ifeq "$(LLVM_LTO)" "1"
|
||||||
ln -sf afl-clang-fast.8 ../afl-clang-lto.8
|
@ln -sf afl-cc.8 ./afl-clang-lto.8
|
||||||
ln -sf afl-clang-fast.8 ../afl-clang-lto++.8
|
@ln -sf afl-cc.8 ./afl-clang-lto++.8
|
||||||
|
@ln -sf afl-cc.8 ./afl-lto.8
|
||||||
|
@ln -sf afl-cc.8 ./afl-lto++.8
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* .test2 test-instr .test-instr0 .test-instr1 *.dwo
|
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* .test2 test-instr .test-instr0 .test-instr1 *.dwo
|
||||||
rm -f $(PROGS) afl-common.o ../afl-clang-fast++ ../afl-clang-lto ../afl-clang-lto++ ../afl-clang*.8 ../ld ../afl-ld ../afl-llvm-rt*.o
|
rm -f $(PROGS) afl-common.o ./afl-c++ ./afl-lto ./afl-lto++ ./afl-clang-lto* ./afl-clang-fast* ./afl-clang*.8 ./ld ./afl-ld ./afl-llvm-rt*.o instrumentation/*.o
|
194
README.md
194
README.md
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
Release Version: [2.68c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
Release Version: [2.67c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||||
|
|
||||||
Github Version: 3.00a
|
Github Version: 3.00a
|
||||||
|
|
||||||
@ -22,6 +22,28 @@
|
|||||||
afl++ is a superior fork to Google's afl - more speed, more and better
|
afl++ is a superior fork to Google's afl - more speed, more and better
|
||||||
mutations, more and better instrumentation, custom module support, etc.
|
mutations, more and better instrumentation, custom module support, etc.
|
||||||
|
|
||||||
|
## Major changes in afl++ 3.0
|
||||||
|
|
||||||
|
With afl++ 3.0 we introduced changes that break some previous afl and afl++
|
||||||
|
behaviours:
|
||||||
|
|
||||||
|
* There are no llvm_mode and gcc_plugin subdirectories anymore and there is
|
||||||
|
only one compiler: afl-cc. All previous compilers now symlink to this one
|
||||||
|
compiler. All instrumentation source code is now in the `instrumentation/`
|
||||||
|
folder.
|
||||||
|
* The gcc_plugin was replaced with a new version submitted by AdaCore, that
|
||||||
|
supports more features, thank you!
|
||||||
|
* qemu_mode got upgraded to QEMU 5.1, but to be able to build this a current
|
||||||
|
ninja build tool version and python3 setuptools are required.
|
||||||
|
qemu_mode also got new options like snapshotting, instrumenting specific
|
||||||
|
shared libraries, etc. and QEMU 5.1 supports more CPU targets so this is
|
||||||
|
worth it.
|
||||||
|
* When instrumenting targets, afl-cc will not supersede optimizations. This
|
||||||
|
allows to fuzz targets as same as they are built for debug or release.
|
||||||
|
* afl-fuzz' `-i` option now descends into subdirectories.
|
||||||
|
* afl-fuzz will skip over empty dictionaries and too-large test cases instead
|
||||||
|
of failing.
|
||||||
|
|
||||||
## Contents
|
## Contents
|
||||||
|
|
||||||
1. [Features](#important-features-of-afl)
|
1. [Features](#important-features-of-afl)
|
||||||
@ -39,31 +61,30 @@
|
|||||||
with laf-intel and redqueen, unicorn mode, gcc plugin, full *BSD, Solaris and
|
with laf-intel and redqueen, unicorn mode, gcc plugin, full *BSD, Solaris and
|
||||||
Android support and much, much, much more.
|
Android support and much, much, much more.
|
||||||
|
|
||||||
| Feature/Instrumentation | afl-gcc | llvm_mode | gcc_plugin | qemu_mode | unicorn_mode |
|
| Feature/Instrumentation | afl-gcc | llvm | gcc_plugin | qemu_mode | unicorn_mode |
|
||||||
| -------------------------|:-------:|:---------:|:----------:|:----------------:|:------------:|
|
| -------------------------|:-------:|:---------:|:----------:|:----------------:|:------------:|
|
||||||
| NeverZero | x86[_64]| x(1) | (2) | x | x |
|
| NeverZero | x86[_64]| x(1) | x | x | x |
|
||||||
| Persistent Mode | | x | x | x86[_64]/arm[64] | x |
|
| Persistent Mode | | x | x | x86[_64]/arm[64] | x |
|
||||||
| LAF-Intel / CompCov | | x | | x86[_64]/arm[64] | x86[_64]/arm |
|
| LAF-Intel / CompCov | | x | | x86[_64]/arm[64] | x86[_64]/arm |
|
||||||
| CmpLog | | x | | x86[_64]/arm[64] | |
|
| CmpLog | | x | | x86[_64]/arm[64] | |
|
||||||
| Selective Instrumentation| | x | x | (x)(3) | |
|
| Selective Instrumentation| | x | x | x | |
|
||||||
| Non-Colliding Coverage | | x(4) | | (x)(5) | |
|
| Non-Colliding Coverage | | x(4) | | (x)(5) | |
|
||||||
| InsTrim | | x | | | |
|
|
||||||
| Ngram prev_loc Coverage | | x(6) | | | |
|
| Ngram prev_loc Coverage | | x(6) | | | |
|
||||||
| Context Coverage | | x | | | |
|
| Context Coverage | | x(6) | | | |
|
||||||
| Auto Dictionary | | x(7) | | | |
|
| Auto Dictionary | | x(7) | | | |
|
||||||
| Snapshot LKM Support | | x | | (x)(5) | |
|
| Snapshot LKM Support | | x | x | (x)(5) | |
|
||||||
|
|
||||||
1. default for LLVM >= 9.0, env var for older version due an efficiency bug in llvm <= 8
|
1. default for LLVM >= 9.0, env var for older version due an efficiency bug in llvm <= 8
|
||||||
2. GCC creates non-performant code, hence it is disabled in gcc_plugin
|
2. GCC creates non-performant code, hence it is disabled in gcc_plugin
|
||||||
3. partially via AFL_CODE_START/AFL_CODE_END
|
3. (currently unassigned)
|
||||||
4. with pcguard mode and LTO mode for LLVM >= 11
|
4. with pcguard mode and LTO mode for LLVM >= 11
|
||||||
5. upcoming, development in the branch
|
5. upcoming, development in the branch
|
||||||
6. not compatible with LTO instrumentation and needs at least LLVM >= 4.1
|
6. not compatible with LTO instrumentation and needs at least LLVM >= 4.1
|
||||||
7. only in LTO mode with LLVM >= 11
|
7. automatic in LTO mode with LLVM >= 11, an extra pass for all LLVM version that writes to a file to use with afl-fuzz' `-x`
|
||||||
|
|
||||||
Among others, the following features and patches have been integrated:
|
Among others, the following features and patches have been integrated:
|
||||||
|
|
||||||
* NeverZero patch for afl-gcc, llvm_mode, qemu_mode and unicorn_mode which prevents a wrapping map value to zero, increases coverage
|
* NeverZero patch for afl-gcc, instrumentation, qemu_mode and unicorn_mode which prevents a wrapping map value to zero, increases coverage
|
||||||
* Persistent mode, deferred forkserver and in-memory fuzzing for qemu_mode
|
* Persistent mode, deferred forkserver and in-memory fuzzing for qemu_mode
|
||||||
* Unicorn mode which allows fuzzing of binaries from completely different platforms (integration provided by domenukk)
|
* Unicorn mode which allows fuzzing of binaries from completely different platforms (integration provided by domenukk)
|
||||||
* The new CmpLog instrumentation for LLVM and QEMU inspired by [Redqueen](https://www.syssec.ruhr-uni-bochum.de/media/emma/veroeffentlichungen/2018/12/17/NDSS19-Redqueen.pdf)
|
* The new CmpLog instrumentation for LLVM and QEMU inspired by [Redqueen](https://www.syssec.ruhr-uni-bochum.de/media/emma/veroeffentlichungen/2018/12/17/NDSS19-Redqueen.pdf)
|
||||||
@ -71,10 +92,7 @@
|
|||||||
* AFLfast's power schedules by Marcel Böhme: [https://github.com/mboehme/aflfast](https://github.com/mboehme/aflfast)
|
* AFLfast's power schedules by Marcel Böhme: [https://github.com/mboehme/aflfast](https://github.com/mboehme/aflfast)
|
||||||
* The MOpt mutator: [https://github.com/puppet-meteor/MOpt-AFL](https://github.com/puppet-meteor/MOpt-AFL)
|
* The MOpt mutator: [https://github.com/puppet-meteor/MOpt-AFL](https://github.com/puppet-meteor/MOpt-AFL)
|
||||||
* LLVM mode Ngram coverage by Adrian Herrera [https://github.com/adrianherrera/afl-ngram-pass](https://github.com/adrianherrera/afl-ngram-pass)
|
* LLVM mode Ngram coverage by Adrian Herrera [https://github.com/adrianherrera/afl-ngram-pass](https://github.com/adrianherrera/afl-ngram-pass)
|
||||||
* InsTrim, a CFG llvm_mode instrumentation implementation: [https://github.com/csienslab/instrim](https://github.com/csienslab/instrim)
|
* LAF-Intel/CompCov support for instrumentation, qemu_mode and unicorn_mode (with enhanced capabilities)
|
||||||
* C. Holler's afl-fuzz Python mutator module: [https://github.com/choller/afl](https://github.com/choller/afl)
|
|
||||||
* Custom mutator by a library (instead of Python) by kyakdan
|
|
||||||
* LAF-Intel/CompCov support for llvm_mode, qemu_mode and unicorn_mode (with enhanced capabilities)
|
|
||||||
* Radamsa and honggfuzz mutators (as custom mutators).
|
* Radamsa and honggfuzz mutators (as custom mutators).
|
||||||
* QBDI mode to fuzz android native libraries via Quarkslab's [QBDI](https://github.com/QBDI/QBDI) framework
|
* QBDI mode to fuzz android native libraries via Quarkslab's [QBDI](https://github.com/QBDI/QBDI) framework
|
||||||
* Frida and ptrace mode to fuzz binary-only libraries, etc.
|
* Frida and ptrace mode to fuzz binary-only libraries, etc.
|
||||||
@ -88,7 +106,7 @@
|
|||||||
send a mail to <afl-users+subscribe@googlegroups.com>.
|
send a mail to <afl-users+subscribe@googlegroups.com>.
|
||||||
|
|
||||||
See [docs/QuickStartGuide.md](docs/QuickStartGuide.md) if you don't have time to
|
See [docs/QuickStartGuide.md](docs/QuickStartGuide.md) if you don't have time to
|
||||||
read this file.
|
read this file - however this is not recommended!
|
||||||
|
|
||||||
## Branches
|
## Branches
|
||||||
|
|
||||||
@ -105,13 +123,14 @@
|
|||||||
|
|
||||||
## Help wanted
|
## Help wanted
|
||||||
|
|
||||||
We are happy to be part of [Google Summer of Code 2020](https://summerofcode.withgoogle.com/organizations/5100744400699392/)! :-)
|
We were happy to be part of [Google Summer of Code 2020](https://summerofcode.withgoogle.com/organizations/5100744400699392/)
|
||||||
|
and we will try to participate again in 2021!
|
||||||
|
|
||||||
We have several ideas we would like to see in AFL++ to make it even better.
|
We have several ideas we would like to see in AFL++ to make it even better.
|
||||||
However, we already work on so many things that we do not have the time for
|
However, we already work on so many things that we do not have the time for
|
||||||
all the big ideas.
|
all the big ideas.
|
||||||
|
|
||||||
This can be your way to support and contribute to AFL++ - extend it to
|
This can be your way to support and contribute to AFL++ - extend it to do
|
||||||
something cool.
|
something cool.
|
||||||
|
|
||||||
We have an idea list in [docs/ideas.md](docs/ideas.md).
|
We have an idea list in [docs/ideas.md](docs/ideas.md).
|
||||||
@ -132,7 +151,7 @@ This image is automatically generated when a push to the stable repo happens.
|
|||||||
You will find your target source code in /src in the container.
|
You will find your target source code in /src in the container.
|
||||||
|
|
||||||
If you want to build afl++ yourself you have many options.
|
If you want to build afl++ yourself you have many options.
|
||||||
The easiest is to build and install everything:
|
The easiest choice is to build and install everything:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo apt install build-essential libtool-bin python3-dev automake flex bison libglib2.0-dev libpixman-1-dev clang python3-setuptools llvm
|
sudo apt install build-essential libtool-bin python3-dev automake flex bison libglib2.0-dev libpixman-1-dev clang python3-setuptools llvm
|
||||||
@ -142,9 +161,9 @@ sudo make install
|
|||||||
It is recommended to install the newest available gcc, clang and llvm-dev
|
It is recommended to install the newest available gcc, clang and llvm-dev
|
||||||
possible in your distribution!
|
possible in your distribution!
|
||||||
|
|
||||||
Note that "make distrib" also builds llvm_mode, qemu_mode, unicorn_mode and
|
Note that "make distrib" also builds instrumentation, qemu_mode, unicorn_mode and
|
||||||
more. If you just want plain afl++ then do "make all", however compiling and
|
more. If you just want plain afl++ then do "make all", however compiling and
|
||||||
using at least llvm_mode is highly recommended for much better results -
|
using at least instrumentation is highly recommended for much better results -
|
||||||
hence in this case
|
hence in this case
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -156,7 +175,7 @@ These build targets exist:
|
|||||||
|
|
||||||
* all: just the main afl++ binaries
|
* all: just the main afl++ binaries
|
||||||
* binary-only: everything for binary-only fuzzing: qemu_mode, unicorn_mode, libdislocator, libtokencap
|
* binary-only: everything for binary-only fuzzing: qemu_mode, unicorn_mode, libdislocator, libtokencap
|
||||||
* source-only: everything for source code fuzzing: llvm_mode, libdislocator, libtokencap
|
* source-only: everything for source code fuzzing: instrumentation, libdislocator, libtokencap
|
||||||
* distrib: everything (for both binary-only and source code fuzzing)
|
* distrib: everything (for both binary-only and source code fuzzing)
|
||||||
* man: creates simple man pages from the help option of the programs
|
* man: creates simple man pages from the help option of the programs
|
||||||
* install: installs everything you have compiled with the build options above
|
* install: installs everything you have compiled with the build options above
|
||||||
@ -212,18 +231,19 @@ If you have a binary-only target please skip to [#Instrumenting binary-only apps
|
|||||||
|
|
||||||
Fuzzing source code is a three-step process.
|
Fuzzing source code is a three-step process.
|
||||||
|
|
||||||
1. compile the target with a special compiler that prepares the target to be
|
1. Compile the target with a special compiler that prepares the target to be
|
||||||
fuzzed efficiently. This step is called "instrumenting a target".
|
fuzzed efficiently. This step is called "instrumenting a target".
|
||||||
2. Prepare the fuzzing by selecting and optimizing the input corpus for the
|
2. Prepare the fuzzing by selecting and optimizing the input corpus for the
|
||||||
target.
|
target.
|
||||||
3. perform the fuzzing of the target by randomly mutating input and assessing
|
3. Perform the fuzzing of the target by randomly mutating input and assessing
|
||||||
if a generated input was processed in a new path in the target binary.
|
if a generated input was processed in a new path in the target binary.
|
||||||
|
|
||||||
### 1. Instrumenting that target
|
### 1. Instrumenting that target
|
||||||
|
|
||||||
#### a) Selecting the best afl++ compiler for instrumenting the target
|
#### a) Selecting the best afl++ compiler for instrumenting the target
|
||||||
|
|
||||||
afl++ comes with different compilers and instrumentation options.
|
afl++ comes with a central compiler `afl-cc` that incorporates various different
|
||||||
|
kinds of compiler targets and and instrumentation options.
|
||||||
The following evaluation flow will help you to select the best possible.
|
The following evaluation flow will help you to select the best possible.
|
||||||
|
|
||||||
It is highly recommended to have the newest llvm version possible installed,
|
It is highly recommended to have the newest llvm version possible installed,
|
||||||
@ -231,49 +251,62 @@ anything below 9 is not recommended.
|
|||||||
|
|
||||||
```
|
```
|
||||||
+--------------------------------+
|
+--------------------------------+
|
||||||
| clang/clang++ 11+ is available | --> use afl-clang-lto and afl-clang-lto++
|
| clang/clang++ 11+ is available | --> use LTO mode (afl-clang-lto/afl-clang-lto++)
|
||||||
+--------------------------------+ see [llvm/README.lto.md](llvm/README.lto.md)
|
+--------------------------------+ see [instrumentation/README.lto.md](instrumentation/README.lto.md)
|
||||||
|
|
|
|
||||||
| if not, or if the target fails with afl-clang-lto/++
|
| if not, or if the target fails with LTO afl-clang-lto/++
|
||||||
|
|
|
|
||||||
v
|
v
|
||||||
+---------------------------------+
|
+---------------------------------+
|
||||||
| clang/clang++ 3.3+ is available | --> use afl-clang-fast and afl-clang-fast++
|
| clang/clang++ 3.3+ is available | --> use LLVM mode (afl-clang-fast/afl-clang-fast++)
|
||||||
+---------------------------------+ see [llvm/README.md](llvm/README.md)
|
+---------------------------------+ see [instrumentation/README.md](instrumentation/README.md)
|
||||||
|
|
|
|
||||||
| if not, or if the target fails with afl-clang-fast/++
|
| if not, or if the target fails with LLVM afl-clang-fast/++
|
||||||
|
|
|
|
||||||
v
|
v
|
||||||
+--------------------------------+
|
+--------------------------------+
|
||||||
| if you want to instrument only | -> use afl-gcc-fast and afl-gcc-fast++
|
| if you want to instrument only | -> use GCC_PLUGIN mode (afl-gcc-fast/afl-g++-fast)
|
||||||
| parts of the target | see [gcc_plugin/README.md](gcc_plugin/README.md) and
|
| parts of the target | see [instrumentation/README.gcc_plugin.md](instrumentation/README.gcc_plugin.md) and
|
||||||
+--------------------------------+ [gcc_plugin/README.instrument_list.md](gcc_plugin/README.instrument_list.md)
|
+--------------------------------+ [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md)
|
||||||
|
|
|
|
||||||
| if not, or if you do not have a gcc with plugin support
|
| if not, or if you do not have a gcc with plugin support
|
||||||
|
|
|
|
||||||
v
|
v
|
||||||
use afl-gcc and afl-g++ (or afl-clang and afl-clang++)
|
use GCC mode (afl-gcc/afl-g++) (or afl-clang/afl-clang++ for clang)
|
||||||
```
|
```
|
||||||
|
|
||||||
Clickable README links for the chosen compiler:
|
Clickable README links for the chosen compiler:
|
||||||
|
|
||||||
* [afl-clang-lto](llvm/README.lto.md)
|
* [LTO mode - afl-clang-lto](instrumentation/README.lto.md)
|
||||||
* [afl-clang-fast](llvm/README.md)
|
* [LLVM mode - afl-clang-fast](instrumentation/README.md)
|
||||||
* [afl-gcc-fast](gcc_plugin/README.md)
|
* [GCC_PLUGIN mode - afl-gcc-fast](instrumentation/README.gcc_plugin.md)
|
||||||
* afl-gcc has no README as it has no features
|
* GCC mode (afl-gcc) has no README as it has no own features
|
||||||
|
|
||||||
|
You can select the mode for the afl-cc compiler by:
|
||||||
|
1. passing --afl-MODE command line options to the compiler via CFLAGS/CXXFLAGS/CPPFLAGS
|
||||||
|
2. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++,
|
||||||
|
afl-clang-fast, afl-clang-fast++, afl-clang-lto, afl-clang-lto++,
|
||||||
|
afl-gcc-fast, afl-g++-fast
|
||||||
|
3. using the environment variable AFL_CC_COMPILER with MODE
|
||||||
|
|
||||||
|
MODE can be one of: LTO (afl-clang-lto*), LLVM (afl-clang-fast*), GCC_PLUGIN
|
||||||
|
(afl-g*-fast) or GCC (afl-gcc/afl-g++).
|
||||||
|
|
||||||
|
Because no afl specific command-line options are accepted (beside the
|
||||||
|
--afl-MODE command), the compile-time tools make fairly broad use of environment
|
||||||
|
variables, which can be listed with `afl-cc -hh` or by reading [docs/env_variables.md](docs/env_variables.md).
|
||||||
|
|
||||||
#### b) Selecting instrumentation options
|
#### b) Selecting instrumentation options
|
||||||
|
|
||||||
The following options are available when you instrument with afl-clang-fast or
|
The following options are available when you instrument with LTO mode (afl-clang-fast/afl-clang-lto):
|
||||||
afl-clang-lto:
|
|
||||||
|
|
||||||
* Splitting integer, string, float and switch comparisons so afl++ can easier
|
* Splitting integer, string, float and switch comparisons so afl++ can easier
|
||||||
solve these. This is an important option if you do not have a very good
|
solve these. This is an important option if you do not have a very good
|
||||||
and large input corpus. This technique is called laf-intel or COMPCOV.
|
and large input corpus. This technique is called laf-intel or COMPCOV.
|
||||||
To use this set the following environment variable before compiling the
|
To use this set the following environment variable before compiling the
|
||||||
target: `export AFL_LLVM_LAF_ALL=1`
|
target: `export AFL_LLVM_LAF_ALL=1`
|
||||||
You can read more about this in [llvm/README.laf-intel.md](llvm/README.laf-intel.md)
|
You can read more about this in [instrumentation/README.laf-intel.md](instrumentation/README.laf-intel.md)
|
||||||
* A different technique (and usually a better than laf-intel) is to
|
* A different technique (and usually a better one than laf-intel) is to
|
||||||
instrument the target so that any compare values in the target are sent to
|
instrument the target so that any compare values in the target are sent to
|
||||||
afl++ which then tries to put these values into the fuzzing data at different
|
afl++ which then tries to put these values into the fuzzing data at different
|
||||||
locations. This technique is very fast and good - if the target does not
|
locations. This technique is very fast and good - if the target does not
|
||||||
@ -282,12 +315,13 @@ afl-clang-lto:
|
|||||||
If you want to use this technique, then you have to compile the target
|
If you want to use this technique, then you have to compile the target
|
||||||
twice, once specifically with/for this mode, and pass this binary to afl-fuzz
|
twice, once specifically with/for this mode, and pass this binary to afl-fuzz
|
||||||
via the `-c` parameter.
|
via the `-c` parameter.
|
||||||
Not that you can compile also just a cmplog binary and use that for both
|
Note that you can compile also just a cmplog binary and use that for both
|
||||||
however there will a performance penality.
|
however there will be a performance penality.
|
||||||
You can read more about this in [llvm_mode/README.cmplog.md](llvm_mode/README.cmplog.md)
|
You can read more about this in [instrumentation/README.cmplog.md](instrumentation/README.cmplog.md)
|
||||||
|
|
||||||
If you use afl-clang-fast, afl-clang-lto or afl-gcc-fast you have the option to
|
If you use LTO, LLVM or GCC_PLUGIN mode (afl-clang-fast/afl-clang-lto/afl-gcc-fast)
|
||||||
selectively only instrument parts of the target that you are interested in:
|
you have the option to selectively only instrument parts of the target that you
|
||||||
|
are interested in:
|
||||||
|
|
||||||
* To instrument only those parts of the target that you are interested in
|
* To instrument only those parts of the target that you are interested in
|
||||||
create a file with all the filenames of the source code that should be
|
create a file with all the filenames of the source code that should be
|
||||||
@ -299,29 +333,29 @@ selectively only instrument parts of the target that you are interested in:
|
|||||||
`export AFL_LLVM_DENYLIST=denylist.txt` - depending on if you want per
|
`export AFL_LLVM_DENYLIST=denylist.txt` - depending on if you want per
|
||||||
default to instrument unless noted (DENYLIST) or not perform instrumentation
|
default to instrument unless noted (DENYLIST) or not perform instrumentation
|
||||||
unless requested (ALLOWLIST).
|
unless requested (ALLOWLIST).
|
||||||
**NOTE:** In optimization functions might be inlined and then not match!
|
**NOTE:** During optimization functions might be inlined and then would not match!
|
||||||
see [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md)
|
See [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md)
|
||||||
For afl-clang-fast > 6.0 or if PCGUARD instrumentation is used then use the
|
For afl-clang-fast > 6.0 or if PCGUARD instrumentation is used then use the
|
||||||
llvm sancov allow-list feature: [http://clang.llvm.org/docs/SanitizerCoverage.html](http://clang.llvm.org/docs/SanitizerCoverage.html)
|
llvm sancov allow-list feature: [http://clang.llvm.org/docs/SanitizerCoverage.html](http://clang.llvm.org/docs/SanitizerCoverage.html)
|
||||||
The llvm sancov format works with the allowlist/denylist feature of afl++
|
The llvm sancov format works with the allowlist/denylist feature of afl++
|
||||||
however afl++ is more flexible in the format.
|
however afl++'s format is more flexible.
|
||||||
|
|
||||||
There are many more options and modes available however these are most of the
|
There are many more options and modes available however these are most of the
|
||||||
time less effective. See:
|
time less effective. See:
|
||||||
* [llvm_mode/README.ctx.md](llvm_mode/README.ctx.md)
|
* [instrumentation/README.ctx.md](instrumentation/README.ctx.md)
|
||||||
* [llvm_mode/README.ngram.md](llvm_mode/README.ngram.md)
|
* [instrumentation/README.ngram.md](instrumentation/README.ngram.md)
|
||||||
* [llvm_mode/README.instrim.md](llvm_mode/README.instrim.md)
|
* [instrumentation/README.instrim.md](instrumentation/README.instrim.md)
|
||||||
|
|
||||||
afl++ employs never zero counting in its bitmap. You can read more about this
|
afl++ performs "never zero" counting in its bitmap. You can read more about this
|
||||||
here:
|
here:
|
||||||
* [llvm_mode/README.neverzero.md](llvm_mode/README.neverzero.md)
|
* [instrumentation/README.neverzero.md](instrumentation/README.neverzero.md)
|
||||||
|
|
||||||
#### c) Modify the target
|
#### c) Modify the target
|
||||||
|
|
||||||
If the target has features that make fuzzing more difficult, e.g.
|
If the target has features that make fuzzing more difficult, e.g.
|
||||||
checksums, HMAC, etc. then modify the source code so that this is
|
checksums, HMAC, etc. then modify the source code so that this is
|
||||||
removed.
|
removed.
|
||||||
This can even be done for productional source code be eliminating
|
This can even be done for operational source code by eliminating
|
||||||
these checks within this specific defines:
|
these checks within this specific defines:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -332,37 +366,46 @@ these checks within this specific defines:
|
|||||||
#endif
|
#endif
|
||||||
```
|
```
|
||||||
|
|
||||||
|
All afl++ compilers will set this preprocessor definition automatically.
|
||||||
|
|
||||||
#### d) Instrument the target
|
#### d) Instrument the target
|
||||||
|
|
||||||
In this step the target source code is compiled so that it can be fuzzed.
|
In this step the target source code is compiled so that it can be fuzzed.
|
||||||
|
|
||||||
Basically you have to tell the target build system that the selected afl++
|
Basically you have to tell the target build system that the selected afl++
|
||||||
compiler is used. Also - if possible - you should always configure the
|
compiler is used. Also - if possible - you should always configure the
|
||||||
build system that the target is compiled statically and not dynamically.
|
build system such that the target is compiled statically and not dynamically.
|
||||||
How to do this is described below.
|
How to do this is described below.
|
||||||
|
|
||||||
Then build the target. (Usually with `make`)
|
Then build the target. (Usually with `make`)
|
||||||
|
|
||||||
|
**NOTE**: sometimes configure and build systems are fickle and do not like
|
||||||
|
stderr output (and think this means a test failure) - which is something
|
||||||
|
afl++ like to do to show statistics. It is recommended to disable them via
|
||||||
|
`export AFL_QUIET=1`.
|
||||||
|
|
||||||
##### configure
|
##### configure
|
||||||
|
|
||||||
For `configure` build systems this is usually done by:
|
For `configure` build systems this is usually done by:
|
||||||
`CC=afl-clang-fast CXX=afl-clang-fast++ ./configure --disable-shared`
|
`CC=afl-clang-fast CXX=afl-clang-fast++ ./configure --disable-shared`
|
||||||
|
|
||||||
Note that if you are using the (better) afl-clang-lto compiler you also have to
|
Note that if you are using the (better) afl-clang-lto compiler you also have to
|
||||||
set AR to llvm-ar[-VERSION] and RANLIB to llvm-ranlib[-VERSION] - as it is
|
set AR to llvm-ar[-VERSION] and RANLIB to llvm-ranlib[-VERSION] - as is
|
||||||
described in [llvm/README.lto.md](llvm/README.lto.md)
|
described in [instrumentation/README.lto.md](instrumentation/README.lto.md).
|
||||||
|
|
||||||
##### cmake
|
##### cmake
|
||||||
|
|
||||||
For `configure` build systems this is usually done by:
|
For `cmake` build systems this is usually done by:
|
||||||
`mkdir build; cd build; CC=afl-clang-fast CXX=afl-clang-fast++ cmake ..`
|
`mkdir build; cmake -DCMAKE_C_COMPILERC=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ ..`
|
||||||
|
|
||||||
Some cmake scripts require something like `-DCMAKE_CC=... -DCMAKE_CXX=...`
|
|
||||||
or `-DCMAKE_C_COMPILER=... DCMAKE_CPP_COMPILER=...` instead.
|
|
||||||
|
|
||||||
Note that if you are using the (better) afl-clang-lto compiler you also have to
|
Note that if you are using the (better) afl-clang-lto compiler you also have to
|
||||||
set AR to llvm-ar[-VERSION] and RANLIB to llvm-ranlib[-VERSION] - as it is
|
set AR to llvm-ar[-VERSION] and RANLIB to llvm-ranlib[-VERSION] - as is
|
||||||
described in [llvm/README.lto.md](llvm/README.lto.md)
|
described in [instrumentation/README.lto.md](instrumentation/README.lto.md).
|
||||||
|
|
||||||
|
##### meson
|
||||||
|
|
||||||
|
For meson you have to set the afl++ compiler with the very first command!
|
||||||
|
`CC=afl-cc CXX=afl-c++ meson`
|
||||||
|
|
||||||
##### other build systems or if configure/cmake didn't work
|
##### other build systems or if configure/cmake didn't work
|
||||||
|
|
||||||
@ -370,7 +413,7 @@ Sometimes cmake and configure do not pick up the afl++ compiler, or the
|
|||||||
ranlib/ar that is needed - because this was just not foreseen by the developer
|
ranlib/ar that is needed - because this was just not foreseen by the developer
|
||||||
of the target. Or they have non-standard options. Figure out if there is a
|
of the target. Or they have non-standard options. Figure out if there is a
|
||||||
non-standard way to set this, otherwise set up the build normally and edit the
|
non-standard way to set this, otherwise set up the build normally and edit the
|
||||||
generated build environment afterwards manually to point to the right compiler
|
generated build environment afterwards manually to point it to the right compiler
|
||||||
(and/or ranlib and ar).
|
(and/or ranlib and ar).
|
||||||
|
|
||||||
#### d) Better instrumentation
|
#### d) Better instrumentation
|
||||||
@ -383,12 +426,12 @@ This requires the usage of afl-clang-lto or afl-clang-fast.
|
|||||||
This is the so-called `persistent mode`, which is much, much faster but
|
This is the so-called `persistent mode`, which is much, much faster but
|
||||||
requires that you code a source file that is specifically calling the target
|
requires that you code a source file that is specifically calling the target
|
||||||
functions that you want to fuzz, plus a few specific afl++ functions around
|
functions that you want to fuzz, plus a few specific afl++ functions around
|
||||||
it. See [llvm_mode/README.persistent_mode.md](llvm_mode/README.persistent_mode.md) for details.
|
it. See [instrumentation/README.persistent_mode.md](instrumentation/README.persistent_mode.md) for details.
|
||||||
|
|
||||||
Basically if you do not fuzz a target in persistent mode then you are just
|
Basically if you do not fuzz a target in persistent mode then you are just
|
||||||
doing it for a hobby and not professionally :-)
|
doing it for a hobby and not professionally :-)
|
||||||
|
|
||||||
### 2. Preparing the fuzzing
|
### 2. Preparing the fuzzing campaign
|
||||||
|
|
||||||
As you fuzz the target with mutated input, having as diverse inputs for the
|
As you fuzz the target with mutated input, having as diverse inputs for the
|
||||||
target as possible improves the efficiency a lot.
|
target as possible improves the efficiency a lot.
|
||||||
@ -401,7 +444,7 @@ reported bugs, test suites, random downloads from the internet, unit test
|
|||||||
case data - from all kind of PNG software.
|
case data - from all kind of PNG software.
|
||||||
|
|
||||||
If the input format is not known, you can also modify a target program to write
|
If the input format is not known, you can also modify a target program to write
|
||||||
away normal data it receives and processes to a file and use these.
|
normal data it receives and processes to a file and use these.
|
||||||
|
|
||||||
#### b) Making the input corpus unique
|
#### b) Making the input corpus unique
|
||||||
|
|
||||||
@ -415,7 +458,7 @@ the run afl-cmin like this:
|
|||||||
`afl-cmin -i INPUTS -o INPUTS_UNIQUE -- bin/target -d @@`
|
`afl-cmin -i INPUTS -o INPUTS_UNIQUE -- bin/target -d @@`
|
||||||
Note that the INPUTFILE argument that the target program would read from has to be set as `@@`.
|
Note that the INPUTFILE argument that the target program would read from has to be set as `@@`.
|
||||||
|
|
||||||
If the target reads from stdin instead, just omit the `@@` as this is the
|
If the target reads from stdin instead, just omit the `@@` as this is the
|
||||||
default.
|
default.
|
||||||
|
|
||||||
#### c) Minimizing all corpus files
|
#### c) Minimizing all corpus files
|
||||||
@ -432,7 +475,7 @@ for i in *; do
|
|||||||
done
|
done
|
||||||
```
|
```
|
||||||
|
|
||||||
This can also be parallelized, e.g. with `parallel`
|
This step can also be parallelized, e.g. with `parallel`
|
||||||
|
|
||||||
#### Done!
|
#### Done!
|
||||||
|
|
||||||
@ -456,7 +499,7 @@ before the start of afl-fuzz as this improves performance by a x2 speed increase
|
|||||||
|
|
||||||
#### a) Running afl-fuzz
|
#### a) Running afl-fuzz
|
||||||
|
|
||||||
Before to do even a test run of afl-fuzz execute `sudo afl-system-config` (on
|
Before you do even a test run of afl-fuzz execute `sudo afl-system-config` (on
|
||||||
the host if you execute afl-fuzz in a docker container). This reconfigures the
|
the host if you execute afl-fuzz in a docker container). This reconfigures the
|
||||||
system for optimal speed - which afl-fuzz checks and bails otherwise.
|
system for optimal speed - which afl-fuzz checks and bails otherwise.
|
||||||
Set `export AFL_SKIP_CPUFREQ=1` for afl-fuzz to skip this check if you cannot
|
Set `export AFL_SKIP_CPUFREQ=1` for afl-fuzz to skip this check if you cannot
|
||||||
@ -588,7 +631,7 @@ then terminate it. The main node will pick it up and make it available to the
|
|||||||
other secondary nodes over time. Set `export AFL_NO_AFFINITY=1` if you have no
|
other secondary nodes over time. Set `export AFL_NO_AFFINITY=1` if you have no
|
||||||
free core.
|
free core.
|
||||||
|
|
||||||
Note that you in nearly all cases you can never reach full coverage. A lot of
|
Note that you in nearly all cases can never reach full coverage. A lot of
|
||||||
functionality is usually behind options that were not activated or fuzz e.g.
|
functionality is usually behind options that were not activated or fuzz e.g.
|
||||||
if you fuzz a library to convert image formats and your target is the png to
|
if you fuzz a library to convert image formats and your target is the png to
|
||||||
tiff API then you will not touch any of the other library APIs and features.
|
tiff API then you will not touch any of the other library APIs and features.
|
||||||
@ -607,7 +650,7 @@ switch or honggfuzz.
|
|||||||
|
|
||||||
#### f) Improve the speed!
|
#### f) Improve the speed!
|
||||||
|
|
||||||
* Use [persistent mode](llvm_mode/README.persistent_mode.md) (x2-x20 speed increase)
|
* Use [persistent mode](instrumentation/README.persistent_mode.md) (x2-x20 speed increase)
|
||||||
* If you do not use shmem persistent mode, use `AFL_TMPDIR` to point the input file on a tempfs location, see [docs/env_variables.md](docs/env_variables.md)
|
* If you do not use shmem persistent mode, use `AFL_TMPDIR` to point the input file on a tempfs location, see [docs/env_variables.md](docs/env_variables.md)
|
||||||
* Linux: Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase)
|
* Linux: Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase)
|
||||||
* Linux: Improve kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system more insecure)
|
* Linux: Improve kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system more insecure)
|
||||||
@ -1035,7 +1078,6 @@ without feedback, bug reports, or patches from:
|
|||||||
Andrea Biondo Vincent Le Garrec
|
Andrea Biondo Vincent Le Garrec
|
||||||
Khaled Yakdan Kuang-che Wu
|
Khaled Yakdan Kuang-che Wu
|
||||||
Josephine Calliotte Konrad Welc
|
Josephine Calliotte Konrad Welc
|
||||||
Thomas Rooijakkers
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Thank you!
|
Thank you!
|
||||||
|
@ -12,9 +12,7 @@ git submodule init
|
|||||||
git submodule update
|
git submodule update
|
||||||
```
|
```
|
||||||
|
|
||||||
otherwise just checkout the repository here with either
|
otherwise just use the script: `grammar_mutator/build_grammar_mutator.sh`
|
||||||
`git clone https://github.com/AFLplusplus/Grammar-Mutator` or
|
|
||||||
`svn co https://github.com/AFLplusplus/Grammar-Mutator`.
|
|
||||||
|
|
||||||
Read the [Grammar-Mutator/README.md](Grammar-Mutator/README.md) on how to use
|
Read the [Grammar-Mutator/README.md](Grammar-Mutator/README.md) on how to use
|
||||||
it.
|
it.
|
||||||
|
6
custom_mutators/grammar_mutator/README.md
Normal file
6
custom_mutators/grammar_mutator/README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Grammar-Mutator
|
||||||
|
|
||||||
|
This is just a stub directory that will clone the real grammar mutator
|
||||||
|
directory.
|
||||||
|
|
||||||
|
Execute `./build_grammar_mutator.sh` to set everything up.
|
17
custom_mutators/grammar_mutator/build_grammar_mutator.sh
Executable file
17
custom_mutators/grammar_mutator/build_grammar_mutator.sh
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test -d Grammar-Mutator || git clone --depth=1 https://github.com/AFLplusplus/Grammar-Mutator
|
||||||
|
|
||||||
|
cd Grammar-Mutator || exit 1
|
||||||
|
git stash ; git pull
|
||||||
|
|
||||||
|
wget -c https://www.antlr.org/download/antlr-4.8-complete.jar
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo "All successfully prepared!"
|
||||||
|
echo "To build for your grammar just do:"
|
||||||
|
echo " cd Grammar_Mutator"
|
||||||
|
echo " make GRAMMAR_FILE=/path/to/your/grammar"
|
||||||
|
echo "You will find a JSON and RUBY grammar in Grammar_Mutator/grammars to play with."
|
||||||
|
echo
|
@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
CFLAGS = -O3 -funroll-loops -fPIC -Wl,-Bsymbolic
|
CFLAGS = -O3 -funroll-loops -fPIC -Wl,-Bsymbolic
|
||||||
|
|
||||||
all: honggfuzz.so
|
all: honggfuzz-mutator.so
|
||||||
|
|
||||||
honggfuzz.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c
|
honggfuzz-mutator.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c
|
||||||
$(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz.so honggfuzz.c mangle.c ../../src/afl-performance.c
|
$(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz-mutator.so honggfuzz.c mangle.c ../../src/afl-performance.c
|
||||||
|
|
||||||
update:
|
update:
|
||||||
@# seriously? --unlink is a dud option? sigh ...
|
@# seriously? --unlink is a dud option? sigh ...
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
# custum mutator: honggfuzz mangle
|
# custum mutator: honggfuzz mangle
|
||||||
|
|
||||||
this is the very good honggfuzz mutator in mangle.c as a custom mutator
|
this is the honggfuzz mutator in mangle.c as a custom mutator
|
||||||
module for afl++. It is the original mangle.c, mangle.h and honggfuzz.h
|
module for afl++. It is the original mangle.c, mangle.h and honggfuzz.h
|
||||||
with a lot of mocking around it :-)
|
with a lot of mocking around it :-)
|
||||||
|
|
||||||
just type `make` to build
|
just type `make` to build
|
||||||
|
|
||||||
```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/honggfuzz/honggfuzz.so afl-fuzz ...```
|
```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/honggfuzz/honggfuzz-mutator.so afl-fuzz ...```
|
||||||
|
|
||||||
> Original repository: https://github.com/google/honggfuzz
|
> Original repository: https://github.com/google/honggfuzz
|
||||||
> Source commit: d0fbcb0373c32436b8fb922e6937da93b17291f5
|
> Source commit: d0fbcb0373c32436b8fb922e6937da93b17291f5
|
||||||
|
35
custom_mutators/libfuzzer/FuzzerBuiltins.h
Normal file
35
custom_mutators/libfuzzer/FuzzerBuiltins.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//===- FuzzerBuiltins.h - Internal header for builtins ----------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Wrapper functions and marcos around builtin functions.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_BUILTINS_H
|
||||||
|
#define LLVM_FUZZER_BUILTINS_H
|
||||||
|
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
|
||||||
|
#if !LIBFUZZER_MSVC
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define GET_CALLER_PC() __builtin_return_address(0)
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
inline uint8_t Bswap(uint8_t x) { return x; }
|
||||||
|
inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
|
||||||
|
inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
|
||||||
|
inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
|
||||||
|
|
||||||
|
inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); }
|
||||||
|
inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); }
|
||||||
|
inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); }
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // !LIBFUZZER_MSVC
|
||||||
|
#endif // LLVM_FUZZER_BUILTINS_H
|
72
custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h
Normal file
72
custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
//===- FuzzerBuiltinsMSVC.h - Internal header for builtins ------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Wrapper functions and marcos that use intrinsics instead of builtin functions
|
||||||
|
// which cannot be compiled by MSVC.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_BUILTINS_MSVC_H
|
||||||
|
#define LLVM_FUZZER_BUILTINS_MSVC_H
|
||||||
|
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
|
||||||
|
#if LIBFUZZER_MSVC
|
||||||
|
#include <intrin.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
// __builtin_return_address() cannot be compiled with MSVC. Use the equivalent
|
||||||
|
// from <intrin.h>
|
||||||
|
#define GET_CALLER_PC() _ReturnAddress()
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
inline uint8_t Bswap(uint8_t x) { return x; }
|
||||||
|
// Use alternatives to __builtin functions from <stdlib.h> and <intrin.h> on
|
||||||
|
// Windows since the builtins are not supported by MSVC.
|
||||||
|
inline uint16_t Bswap(uint16_t x) { return _byteswap_ushort(x); }
|
||||||
|
inline uint32_t Bswap(uint32_t x) { return _byteswap_ulong(x); }
|
||||||
|
inline uint64_t Bswap(uint64_t x) { return _byteswap_uint64(x); }
|
||||||
|
|
||||||
|
// The functions below were mostly copied from
|
||||||
|
// compiler-rt/lib/builtins/int_lib.h which defines the __builtin functions used
|
||||||
|
// outside of Windows.
|
||||||
|
inline uint32_t Clzll(uint64_t X) {
|
||||||
|
unsigned long LeadZeroIdx = 0;
|
||||||
|
|
||||||
|
#if !defined(_M_ARM) && !defined(_M_X64)
|
||||||
|
// Scan the high 32 bits.
|
||||||
|
if (_BitScanReverse(&LeadZeroIdx, static_cast<unsigned long>(X >> 32)))
|
||||||
|
return static_cast<int>(63 - (LeadZeroIdx + 32)); // Create a bit offset from the MSB.
|
||||||
|
// Scan the low 32 bits.
|
||||||
|
if (_BitScanReverse(&LeadZeroIdx, static_cast<unsigned long>(X)))
|
||||||
|
return static_cast<int>(63 - LeadZeroIdx);
|
||||||
|
|
||||||
|
#else
|
||||||
|
if (_BitScanReverse64(&LeadZeroIdx, X)) return 63 - LeadZeroIdx;
|
||||||
|
#endif
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t Clz(uint32_t X) {
|
||||||
|
unsigned long LeadZeroIdx = 0;
|
||||||
|
if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx;
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int Popcountll(unsigned long long X) {
|
||||||
|
#if !defined(_M_ARM) && !defined(_M_X64)
|
||||||
|
return __popcnt(X) + __popcnt(X >> 32);
|
||||||
|
#else
|
||||||
|
return __popcnt64(X);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LIBFUZER_MSVC
|
||||||
|
#endif // LLVM_FUZZER_BUILTINS_MSVC_H
|
178
custom_mutators/libfuzzer/FuzzerCommand.h
Normal file
178
custom_mutators/libfuzzer/FuzzerCommand.h
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// FuzzerCommand represents a command to run in a subprocess. It allows callers
|
||||||
|
// to manage command line arguments and output and error streams.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_COMMAND_H
|
||||||
|
#define LLVM_FUZZER_COMMAND_H
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
class Command final {
|
||||||
|
public:
|
||||||
|
// This command line flag is used to indicate that the remaining command line
|
||||||
|
// is immutable, meaning this flag effectively marks the end of the mutable
|
||||||
|
// argument list.
|
||||||
|
static inline const char *ignoreRemainingArgs() {
|
||||||
|
return "-ignore_remaining_args=1";
|
||||||
|
}
|
||||||
|
|
||||||
|
Command() : CombinedOutAndErr(false) {}
|
||||||
|
|
||||||
|
explicit Command(const Vector<std::string> &ArgsToAdd)
|
||||||
|
: Args(ArgsToAdd), CombinedOutAndErr(false) {}
|
||||||
|
|
||||||
|
explicit Command(const Command &Other)
|
||||||
|
: Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr),
|
||||||
|
OutputFile(Other.OutputFile) {}
|
||||||
|
|
||||||
|
Command &operator=(const Command &Other) {
|
||||||
|
Args = Other.Args;
|
||||||
|
CombinedOutAndErr = Other.CombinedOutAndErr;
|
||||||
|
OutputFile = Other.OutputFile;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Command() {}
|
||||||
|
|
||||||
|
// Returns true if the given Arg is present in Args. Only checks up to
|
||||||
|
// "-ignore_remaining_args=1".
|
||||||
|
bool hasArgument(const std::string &Arg) const {
|
||||||
|
auto i = endMutableArgs();
|
||||||
|
return std::find(Args.begin(), i, Arg) != i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets all of the current command line arguments, **including** those after
|
||||||
|
// "-ignore-remaining-args=1".
|
||||||
|
const Vector<std::string> &getArguments() const { return Args; }
|
||||||
|
|
||||||
|
// Adds the given argument before "-ignore_remaining_args=1", or at the end
|
||||||
|
// if that flag isn't present.
|
||||||
|
void addArgument(const std::string &Arg) {
|
||||||
|
Args.insert(endMutableArgs(), Arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds all given arguments before "-ignore_remaining_args=1", or at the end
|
||||||
|
// if that flag isn't present.
|
||||||
|
void addArguments(const Vector<std::string> &ArgsToAdd) {
|
||||||
|
Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes the given argument from the command argument list. Ignores any
|
||||||
|
// occurrences after "-ignore_remaining_args=1", if present.
|
||||||
|
void removeArgument(const std::string &Arg) {
|
||||||
|
auto i = endMutableArgs();
|
||||||
|
Args.erase(std::remove(Args.begin(), i, Arg), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like hasArgument, but checks for "-[Flag]=...".
|
||||||
|
bool hasFlag(const std::string &Flag) const {
|
||||||
|
std::string Arg("-" + Flag + "=");
|
||||||
|
auto IsMatch = [&](const std::string &Other) {
|
||||||
|
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
|
||||||
|
};
|
||||||
|
return std::any_of(Args.begin(), endMutableArgs(), IsMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the value of the first instance of a given flag, or an empty string
|
||||||
|
// if the flag isn't present. Ignores any occurrences after
|
||||||
|
// "-ignore_remaining_args=1", if present.
|
||||||
|
std::string getFlagValue(const std::string &Flag) const {
|
||||||
|
std::string Arg("-" + Flag + "=");
|
||||||
|
auto IsMatch = [&](const std::string &Other) {
|
||||||
|
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
|
||||||
|
};
|
||||||
|
auto i = endMutableArgs();
|
||||||
|
auto j = std::find_if(Args.begin(), i, IsMatch);
|
||||||
|
std::string result;
|
||||||
|
if (j != i) {
|
||||||
|
result = j->substr(Arg.length());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like AddArgument, but adds "-[Flag]=[Value]".
|
||||||
|
void addFlag(const std::string &Flag, const std::string &Value) {
|
||||||
|
addArgument("-" + Flag + "=" + Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like RemoveArgument, but removes "-[Flag]=...".
|
||||||
|
void removeFlag(const std::string &Flag) {
|
||||||
|
std::string Arg("-" + Flag + "=");
|
||||||
|
auto IsMatch = [&](const std::string &Other) {
|
||||||
|
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
|
||||||
|
};
|
||||||
|
auto i = endMutableArgs();
|
||||||
|
Args.erase(std::remove_if(Args.begin(), i, IsMatch), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns whether the command's stdout is being written to an output file.
|
||||||
|
bool hasOutputFile() const { return !OutputFile.empty(); }
|
||||||
|
|
||||||
|
// Returns the currently set output file.
|
||||||
|
const std::string &getOutputFile() const { return OutputFile; }
|
||||||
|
|
||||||
|
// Configures the command to redirect its output to the name file.
|
||||||
|
void setOutputFile(const std::string &FileName) { OutputFile = FileName; }
|
||||||
|
|
||||||
|
// Returns whether the command's stderr is redirected to stdout.
|
||||||
|
bool isOutAndErrCombined() const { return CombinedOutAndErr; }
|
||||||
|
|
||||||
|
// Sets whether to redirect the command's stderr to its stdout.
|
||||||
|
void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; }
|
||||||
|
|
||||||
|
// Returns a string representation of the command. On many systems this will
|
||||||
|
// be the equivalent command line.
|
||||||
|
std::string toString() const {
|
||||||
|
std::stringstream SS;
|
||||||
|
for (auto arg : getArguments())
|
||||||
|
SS << arg << " ";
|
||||||
|
if (hasOutputFile())
|
||||||
|
SS << ">" << getOutputFile() << " ";
|
||||||
|
if (isOutAndErrCombined())
|
||||||
|
SS << "2>&1 ";
|
||||||
|
std::string result = SS.str();
|
||||||
|
if (!result.empty())
|
||||||
|
result = result.substr(0, result.length() - 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Command(Command &&Other) = delete;
|
||||||
|
Command &operator=(Command &&Other) = delete;
|
||||||
|
|
||||||
|
Vector<std::string>::iterator endMutableArgs() {
|
||||||
|
return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<std::string>::const_iterator endMutableArgs() const {
|
||||||
|
return std::find(Args.begin(), Args.end(), ignoreRemainingArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The command arguments. Args[0] is the command name.
|
||||||
|
Vector<std::string> Args;
|
||||||
|
|
||||||
|
// True indicates stderr is redirected to stdout.
|
||||||
|
bool CombinedOutAndErr;
|
||||||
|
|
||||||
|
// If not empty, stdout is redirected to the named file.
|
||||||
|
std::string OutputFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_COMMAND_H
|
581
custom_mutators/libfuzzer/FuzzerCorpus.h
Normal file
581
custom_mutators/libfuzzer/FuzzerCorpus.h
Normal file
@ -0,0 +1,581 @@
|
|||||||
|
//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// fuzzer::InputCorpus
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_CORPUS
|
||||||
|
#define LLVM_FUZZER_CORPUS
|
||||||
|
|
||||||
|
#include "FuzzerDataFlowTrace.h"
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerRandom.h"
|
||||||
|
#include "FuzzerSHA1.h"
|
||||||
|
#include "FuzzerTracePC.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <numeric>
|
||||||
|
#include <random>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
struct InputInfo {
|
||||||
|
Unit U; // The actual input data.
|
||||||
|
std::chrono::microseconds TimeOfUnit;
|
||||||
|
uint8_t Sha1[kSHA1NumBytes]; // Checksum.
|
||||||
|
// Number of features that this input has and no smaller input has.
|
||||||
|
size_t NumFeatures = 0;
|
||||||
|
size_t Tmp = 0; // Used by ValidateFeatureSet.
|
||||||
|
// Stats.
|
||||||
|
size_t NumExecutedMutations = 0;
|
||||||
|
size_t NumSuccessfullMutations = 0;
|
||||||
|
bool NeverReduce = false;
|
||||||
|
bool MayDeleteFile = false;
|
||||||
|
bool Reduced = false;
|
||||||
|
bool HasFocusFunction = false;
|
||||||
|
Vector<uint32_t> UniqFeatureSet;
|
||||||
|
Vector<uint8_t> DataFlowTraceForFocusFunction;
|
||||||
|
// Power schedule.
|
||||||
|
bool NeedsEnergyUpdate = false;
|
||||||
|
double Energy = 0.0;
|
||||||
|
size_t SumIncidence = 0;
|
||||||
|
Vector<std::pair<uint32_t, uint16_t>> FeatureFreqs;
|
||||||
|
|
||||||
|
// Delete feature Idx and its frequency from FeatureFreqs.
|
||||||
|
bool DeleteFeatureFreq(uint32_t Idx) {
|
||||||
|
if (FeatureFreqs.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Binary search over local feature frequencies sorted by index.
|
||||||
|
auto Lower = std::lower_bound(FeatureFreqs.begin(), FeatureFreqs.end(),
|
||||||
|
std::pair<uint32_t, uint16_t>(Idx, 0));
|
||||||
|
|
||||||
|
if (Lower != FeatureFreqs.end() && Lower->first == Idx) {
|
||||||
|
FeatureFreqs.erase(Lower);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign more energy to a high-entropy seed, i.e., that reveals more
|
||||||
|
// information about the globally rare features in the neighborhood of the
|
||||||
|
// seed. Since we do not know the entropy of a seed that has never been
|
||||||
|
// executed we assign fresh seeds maximum entropy and let II->Energy approach
|
||||||
|
// the true entropy from above. If ScalePerExecTime is true, the computed
|
||||||
|
// entropy is scaled based on how fast this input executes compared to the
|
||||||
|
// average execution time of inputs. The faster an input executes, the more
|
||||||
|
// energy gets assigned to the input.
|
||||||
|
void UpdateEnergy(size_t GlobalNumberOfFeatures, bool ScalePerExecTime,
|
||||||
|
std::chrono::microseconds AverageUnitExecutionTime) {
|
||||||
|
Energy = 0.0;
|
||||||
|
SumIncidence = 0;
|
||||||
|
|
||||||
|
// Apply add-one smoothing to locally discovered features.
|
||||||
|
for (auto F : FeatureFreqs) {
|
||||||
|
size_t LocalIncidence = F.second + 1;
|
||||||
|
Energy -= LocalIncidence * logl(LocalIncidence);
|
||||||
|
SumIncidence += LocalIncidence;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply add-one smoothing to locally undiscovered features.
|
||||||
|
// PreciseEnergy -= 0; // since logl(1.0) == 0)
|
||||||
|
SumIncidence += (GlobalNumberOfFeatures - FeatureFreqs.size());
|
||||||
|
|
||||||
|
// Add a single locally abundant feature apply add-one smoothing.
|
||||||
|
size_t AbdIncidence = NumExecutedMutations + 1;
|
||||||
|
Energy -= AbdIncidence * logl(AbdIncidence);
|
||||||
|
SumIncidence += AbdIncidence;
|
||||||
|
|
||||||
|
// Normalize.
|
||||||
|
if (SumIncidence != 0)
|
||||||
|
Energy = (Energy / SumIncidence) + logl(SumIncidence);
|
||||||
|
|
||||||
|
if (ScalePerExecTime) {
|
||||||
|
// Scaling to favor inputs with lower execution time.
|
||||||
|
uint32_t PerfScore = 100;
|
||||||
|
if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 10)
|
||||||
|
PerfScore = 10;
|
||||||
|
else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 4)
|
||||||
|
PerfScore = 25;
|
||||||
|
else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 2)
|
||||||
|
PerfScore = 50;
|
||||||
|
else if (TimeOfUnit.count() * 3 > AverageUnitExecutionTime.count() * 4)
|
||||||
|
PerfScore = 75;
|
||||||
|
else if (TimeOfUnit.count() * 4 < AverageUnitExecutionTime.count())
|
||||||
|
PerfScore = 300;
|
||||||
|
else if (TimeOfUnit.count() * 3 < AverageUnitExecutionTime.count())
|
||||||
|
PerfScore = 200;
|
||||||
|
else if (TimeOfUnit.count() * 2 < AverageUnitExecutionTime.count())
|
||||||
|
PerfScore = 150;
|
||||||
|
|
||||||
|
Energy *= PerfScore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the frequency of the feature Idx.
|
||||||
|
void UpdateFeatureFrequency(uint32_t Idx) {
|
||||||
|
NeedsEnergyUpdate = true;
|
||||||
|
|
||||||
|
// The local feature frequencies is an ordered vector of pairs.
|
||||||
|
// If there are no local feature frequencies, push_back preserves order.
|
||||||
|
// Set the feature frequency for feature Idx32 to 1.
|
||||||
|
if (FeatureFreqs.empty()) {
|
||||||
|
FeatureFreqs.push_back(std::pair<uint32_t, uint16_t>(Idx, 1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary search over local feature frequencies sorted by index.
|
||||||
|
auto Lower = std::lower_bound(FeatureFreqs.begin(), FeatureFreqs.end(),
|
||||||
|
std::pair<uint32_t, uint16_t>(Idx, 0));
|
||||||
|
|
||||||
|
// If feature Idx32 already exists, increment its frequency.
|
||||||
|
// Otherwise, insert a new pair right after the next lower index.
|
||||||
|
if (Lower != FeatureFreqs.end() && Lower->first == Idx) {
|
||||||
|
Lower->second++;
|
||||||
|
} else {
|
||||||
|
FeatureFreqs.insert(Lower, std::pair<uint32_t, uint16_t>(Idx, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EntropicOptions {
|
||||||
|
bool Enabled;
|
||||||
|
size_t NumberOfRarestFeatures;
|
||||||
|
size_t FeatureFrequencyThreshold;
|
||||||
|
bool ScalePerExecTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InputCorpus {
|
||||||
|
static const uint32_t kFeatureSetSize = 1 << 21;
|
||||||
|
static const uint8_t kMaxMutationFactor = 20;
|
||||||
|
static const size_t kSparseEnergyUpdates = 100;
|
||||||
|
|
||||||
|
size_t NumExecutedMutations = 0;
|
||||||
|
|
||||||
|
EntropicOptions Entropic;
|
||||||
|
|
||||||
|
public:
|
||||||
|
InputCorpus(const std::string &OutputCorpus, EntropicOptions Entropic)
|
||||||
|
: Entropic(Entropic), OutputCorpus(OutputCorpus) {
|
||||||
|
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
|
||||||
|
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
|
||||||
|
}
|
||||||
|
~InputCorpus() {
|
||||||
|
for (auto II : Inputs)
|
||||||
|
delete II;
|
||||||
|
}
|
||||||
|
size_t size() const { return Inputs.size(); }
|
||||||
|
size_t SizeInBytes() const {
|
||||||
|
size_t Res = 0;
|
||||||
|
for (auto II : Inputs)
|
||||||
|
Res += II->U.size();
|
||||||
|
return Res;
|
||||||
|
}
|
||||||
|
size_t NumActiveUnits() const {
|
||||||
|
size_t Res = 0;
|
||||||
|
for (auto II : Inputs)
|
||||||
|
Res += !II->U.empty();
|
||||||
|
return Res;
|
||||||
|
}
|
||||||
|
size_t MaxInputSize() const {
|
||||||
|
size_t Res = 0;
|
||||||
|
for (auto II : Inputs)
|
||||||
|
Res = std::max(Res, II->U.size());
|
||||||
|
return Res;
|
||||||
|
}
|
||||||
|
void IncrementNumExecutedMutations() { NumExecutedMutations++; }
|
||||||
|
|
||||||
|
size_t NumInputsThatTouchFocusFunction() {
|
||||||
|
return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) {
|
||||||
|
return II->HasFocusFunction;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NumInputsWithDataFlowTrace() {
|
||||||
|
return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) {
|
||||||
|
return !II->DataFlowTraceForFocusFunction.empty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const { return Inputs.empty(); }
|
||||||
|
const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; }
|
||||||
|
InputInfo *AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile,
|
||||||
|
bool HasFocusFunction, bool NeverReduce,
|
||||||
|
std::chrono::microseconds TimeOfUnit,
|
||||||
|
const Vector<uint32_t> &FeatureSet,
|
||||||
|
const DataFlowTrace &DFT, const InputInfo *BaseII) {
|
||||||
|
assert(!U.empty());
|
||||||
|
if (FeatureDebug)
|
||||||
|
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
|
||||||
|
Inputs.push_back(new InputInfo());
|
||||||
|
InputInfo &II = *Inputs.back();
|
||||||
|
II.U = U;
|
||||||
|
II.NumFeatures = NumFeatures;
|
||||||
|
II.NeverReduce = NeverReduce;
|
||||||
|
II.TimeOfUnit = TimeOfUnit;
|
||||||
|
II.MayDeleteFile = MayDeleteFile;
|
||||||
|
II.UniqFeatureSet = FeatureSet;
|
||||||
|
II.HasFocusFunction = HasFocusFunction;
|
||||||
|
// Assign maximal energy to the new seed.
|
||||||
|
II.Energy = RareFeatures.empty() ? 1.0 : log(RareFeatures.size());
|
||||||
|
II.SumIncidence = RareFeatures.size();
|
||||||
|
II.NeedsEnergyUpdate = false;
|
||||||
|
std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end());
|
||||||
|
ComputeSHA1(U.data(), U.size(), II.Sha1);
|
||||||
|
auto Sha1Str = Sha1ToString(II.Sha1);
|
||||||
|
Hashes.insert(Sha1Str);
|
||||||
|
if (HasFocusFunction)
|
||||||
|
if (auto V = DFT.Get(Sha1Str))
|
||||||
|
II.DataFlowTraceForFocusFunction = *V;
|
||||||
|
// This is a gross heuristic.
|
||||||
|
// Ideally, when we add an element to a corpus we need to know its DFT.
|
||||||
|
// But if we don't, we'll use the DFT of its base input.
|
||||||
|
if (II.DataFlowTraceForFocusFunction.empty() && BaseII)
|
||||||
|
II.DataFlowTraceForFocusFunction = BaseII->DataFlowTraceForFocusFunction;
|
||||||
|
DistributionNeedsUpdate = true;
|
||||||
|
PrintCorpus();
|
||||||
|
// ValidateFeatureSet();
|
||||||
|
return &II;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug-only
|
||||||
|
void PrintUnit(const Unit &U) {
|
||||||
|
if (!FeatureDebug) return;
|
||||||
|
for (uint8_t C : U) {
|
||||||
|
if (C != 'F' && C != 'U' && C != 'Z')
|
||||||
|
C = '.';
|
||||||
|
Printf("%c", C);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug-only
|
||||||
|
void PrintFeatureSet(const Vector<uint32_t> &FeatureSet) {
|
||||||
|
if (!FeatureDebug) return;
|
||||||
|
Printf("{");
|
||||||
|
for (uint32_t Feature: FeatureSet)
|
||||||
|
Printf("%u,", Feature);
|
||||||
|
Printf("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug-only
|
||||||
|
void PrintCorpus() {
|
||||||
|
if (!FeatureDebug) return;
|
||||||
|
Printf("======= CORPUS:\n");
|
||||||
|
int i = 0;
|
||||||
|
for (auto II : Inputs) {
|
||||||
|
if (std::find(II->U.begin(), II->U.end(), 'F') != II->U.end()) {
|
||||||
|
Printf("[%2d] ", i);
|
||||||
|
Printf("%s sz=%zd ", Sha1ToString(II->Sha1).c_str(), II->U.size());
|
||||||
|
PrintUnit(II->U);
|
||||||
|
Printf(" ");
|
||||||
|
PrintFeatureSet(II->UniqFeatureSet);
|
||||||
|
Printf("\n");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Replace(InputInfo *II, const Unit &U) {
|
||||||
|
assert(II->U.size() > U.size());
|
||||||
|
Hashes.erase(Sha1ToString(II->Sha1));
|
||||||
|
DeleteFile(*II);
|
||||||
|
ComputeSHA1(U.data(), U.size(), II->Sha1);
|
||||||
|
Hashes.insert(Sha1ToString(II->Sha1));
|
||||||
|
II->U = U;
|
||||||
|
II->Reduced = true;
|
||||||
|
DistributionNeedsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); }
|
||||||
|
bool HasUnit(const std::string &H) { return Hashes.count(H); }
|
||||||
|
InputInfo &ChooseUnitToMutate(Random &Rand) {
|
||||||
|
InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)];
|
||||||
|
assert(!II.U.empty());
|
||||||
|
return II;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputInfo &ChooseUnitToCrossOverWith(Random &Rand, bool UniformDist) {
|
||||||
|
if (!UniformDist) {
|
||||||
|
return ChooseUnitToMutate(Rand);
|
||||||
|
}
|
||||||
|
InputInfo &II = *Inputs[Rand(Inputs.size())];
|
||||||
|
assert(!II.U.empty());
|
||||||
|
return II;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns an index of random unit from the corpus to mutate.
|
||||||
|
size_t ChooseUnitIdxToMutate(Random &Rand) {
|
||||||
|
UpdateCorpusDistribution(Rand);
|
||||||
|
size_t Idx = static_cast<size_t>(CorpusDistribution(Rand));
|
||||||
|
assert(Idx < Inputs.size());
|
||||||
|
return Idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintStats() {
|
||||||
|
for (size_t i = 0; i < Inputs.size(); i++) {
|
||||||
|
const auto &II = *Inputs[i];
|
||||||
|
Printf(" [% 3zd %s] sz: % 5zd runs: % 5zd succ: % 5zd focus: %d\n", i,
|
||||||
|
Sha1ToString(II.Sha1).c_str(), II.U.size(),
|
||||||
|
II.NumExecutedMutations, II.NumSuccessfullMutations, II.HasFocusFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintFeatureSet() {
|
||||||
|
for (size_t i = 0; i < kFeatureSetSize; i++) {
|
||||||
|
if(size_t Sz = GetFeature(i))
|
||||||
|
Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz);
|
||||||
|
}
|
||||||
|
Printf("\n\t");
|
||||||
|
for (size_t i = 0; i < Inputs.size(); i++)
|
||||||
|
if (size_t N = Inputs[i]->NumFeatures)
|
||||||
|
Printf(" %zd=>%zd ", i, N);
|
||||||
|
Printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteFile(const InputInfo &II) {
|
||||||
|
if (!OutputCorpus.empty() && II.MayDeleteFile)
|
||||||
|
RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteInput(size_t Idx) {
|
||||||
|
InputInfo &II = *Inputs[Idx];
|
||||||
|
DeleteFile(II);
|
||||||
|
Unit().swap(II.U);
|
||||||
|
II.Energy = 0.0;
|
||||||
|
II.NeedsEnergyUpdate = false;
|
||||||
|
DistributionNeedsUpdate = true;
|
||||||
|
if (FeatureDebug)
|
||||||
|
Printf("EVICTED %zd\n", Idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddRareFeature(uint32_t Idx) {
|
||||||
|
// Maintain *at least* TopXRarestFeatures many rare features
|
||||||
|
// and all features with a frequency below ConsideredRare.
|
||||||
|
// Remove all other features.
|
||||||
|
while (RareFeatures.size() > Entropic.NumberOfRarestFeatures &&
|
||||||
|
FreqOfMostAbundantRareFeature > Entropic.FeatureFrequencyThreshold) {
|
||||||
|
|
||||||
|
// Find most and second most abbundant feature.
|
||||||
|
uint32_t MostAbundantRareFeatureIndices[2] = {RareFeatures[0],
|
||||||
|
RareFeatures[0]};
|
||||||
|
size_t Delete = 0;
|
||||||
|
for (size_t i = 0; i < RareFeatures.size(); i++) {
|
||||||
|
uint32_t Idx2 = RareFeatures[i];
|
||||||
|
if (GlobalFeatureFreqs[Idx2] >=
|
||||||
|
GlobalFeatureFreqs[MostAbundantRareFeatureIndices[0]]) {
|
||||||
|
MostAbundantRareFeatureIndices[1] = MostAbundantRareFeatureIndices[0];
|
||||||
|
MostAbundantRareFeatureIndices[0] = Idx2;
|
||||||
|
Delete = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove most abundant rare feature.
|
||||||
|
RareFeatures[Delete] = RareFeatures.back();
|
||||||
|
RareFeatures.pop_back();
|
||||||
|
|
||||||
|
for (auto II : Inputs) {
|
||||||
|
if (II->DeleteFeatureFreq(MostAbundantRareFeatureIndices[0]))
|
||||||
|
II->NeedsEnergyUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set 2nd most abundant as the new most abundant feature count.
|
||||||
|
FreqOfMostAbundantRareFeature =
|
||||||
|
GlobalFeatureFreqs[MostAbundantRareFeatureIndices[1]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add rare feature, handle collisions, and update energy.
|
||||||
|
RareFeatures.push_back(Idx);
|
||||||
|
GlobalFeatureFreqs[Idx] = 0;
|
||||||
|
for (auto II : Inputs) {
|
||||||
|
II->DeleteFeatureFreq(Idx);
|
||||||
|
|
||||||
|
// Apply add-one smoothing to this locally undiscovered feature.
|
||||||
|
// Zero energy seeds will never be fuzzed and remain zero energy.
|
||||||
|
if (II->Energy > 0.0) {
|
||||||
|
II->SumIncidence += 1;
|
||||||
|
II->Energy += logl(II->SumIncidence) / II->SumIncidence;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DistributionNeedsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) {
|
||||||
|
assert(NewSize);
|
||||||
|
Idx = Idx % kFeatureSetSize;
|
||||||
|
uint32_t OldSize = GetFeature(Idx);
|
||||||
|
if (OldSize == 0 || (Shrink && OldSize > NewSize)) {
|
||||||
|
if (OldSize > 0) {
|
||||||
|
size_t OldIdx = SmallestElementPerFeature[Idx];
|
||||||
|
InputInfo &II = *Inputs[OldIdx];
|
||||||
|
assert(II.NumFeatures > 0);
|
||||||
|
II.NumFeatures--;
|
||||||
|
if (II.NumFeatures == 0)
|
||||||
|
DeleteInput(OldIdx);
|
||||||
|
} else {
|
||||||
|
NumAddedFeatures++;
|
||||||
|
if (Entropic.Enabled)
|
||||||
|
AddRareFeature((uint32_t)Idx);
|
||||||
|
}
|
||||||
|
NumUpdatedFeatures++;
|
||||||
|
if (FeatureDebug)
|
||||||
|
Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize);
|
||||||
|
SmallestElementPerFeature[Idx] = Inputs.size();
|
||||||
|
InputSizesPerFeature[Idx] = NewSize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment frequency of feature Idx globally and locally.
|
||||||
|
void UpdateFeatureFrequency(InputInfo *II, size_t Idx) {
|
||||||
|
uint32_t Idx32 = Idx % kFeatureSetSize;
|
||||||
|
|
||||||
|
// Saturated increment.
|
||||||
|
if (GlobalFeatureFreqs[Idx32] == 0xFFFF)
|
||||||
|
return;
|
||||||
|
uint16_t Freq = GlobalFeatureFreqs[Idx32]++;
|
||||||
|
|
||||||
|
// Skip if abundant.
|
||||||
|
if (Freq > FreqOfMostAbundantRareFeature ||
|
||||||
|
std::find(RareFeatures.begin(), RareFeatures.end(), Idx32) ==
|
||||||
|
RareFeatures.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update global frequencies.
|
||||||
|
if (Freq == FreqOfMostAbundantRareFeature)
|
||||||
|
FreqOfMostAbundantRareFeature++;
|
||||||
|
|
||||||
|
// Update local frequencies.
|
||||||
|
if (II)
|
||||||
|
II->UpdateFeatureFrequency(Idx32);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t NumFeatures() const { return NumAddedFeatures; }
|
||||||
|
size_t NumFeatureUpdates() const { return NumUpdatedFeatures; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static const bool FeatureDebug = false;
|
||||||
|
|
||||||
|
size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; }
|
||||||
|
|
||||||
|
void ValidateFeatureSet() {
|
||||||
|
if (FeatureDebug)
|
||||||
|
PrintFeatureSet();
|
||||||
|
for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++)
|
||||||
|
if (GetFeature(Idx))
|
||||||
|
Inputs[SmallestElementPerFeature[Idx]]->Tmp++;
|
||||||
|
for (auto II: Inputs) {
|
||||||
|
if (II->Tmp != II->NumFeatures)
|
||||||
|
Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures);
|
||||||
|
assert(II->Tmp == II->NumFeatures);
|
||||||
|
II->Tmp = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the probability distribution for the units in the corpus.
|
||||||
|
// Must be called whenever the corpus or unit weights are changed.
|
||||||
|
//
|
||||||
|
// Hypothesis: inputs that maximize information about globally rare features
|
||||||
|
// are interesting.
|
||||||
|
void UpdateCorpusDistribution(Random &Rand) {
|
||||||
|
// Skip update if no seeds or rare features were added/deleted.
|
||||||
|
// Sparse updates for local change of feature frequencies,
|
||||||
|
// i.e., randomly do not skip.
|
||||||
|
if (!DistributionNeedsUpdate &&
|
||||||
|
(!Entropic.Enabled || Rand(kSparseEnergyUpdates)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DistributionNeedsUpdate = false;
|
||||||
|
|
||||||
|
size_t N = Inputs.size();
|
||||||
|
assert(N);
|
||||||
|
Intervals.resize(N + 1);
|
||||||
|
Weights.resize(N);
|
||||||
|
std::iota(Intervals.begin(), Intervals.end(), 0);
|
||||||
|
|
||||||
|
std::chrono::microseconds AverageUnitExecutionTime(0);
|
||||||
|
for (auto II : Inputs) {
|
||||||
|
AverageUnitExecutionTime += II->TimeOfUnit;
|
||||||
|
}
|
||||||
|
AverageUnitExecutionTime /= N;
|
||||||
|
|
||||||
|
bool VanillaSchedule = true;
|
||||||
|
if (Entropic.Enabled) {
|
||||||
|
for (auto II : Inputs) {
|
||||||
|
if (II->NeedsEnergyUpdate && II->Energy != 0.0) {
|
||||||
|
II->NeedsEnergyUpdate = false;
|
||||||
|
II->UpdateEnergy(RareFeatures.size(), Entropic.ScalePerExecTime,
|
||||||
|
AverageUnitExecutionTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < N; i++) {
|
||||||
|
|
||||||
|
if (Inputs[i]->NumFeatures == 0) {
|
||||||
|
// If the seed doesn't represent any features, assign zero energy.
|
||||||
|
Weights[i] = 0.;
|
||||||
|
} else if (Inputs[i]->NumExecutedMutations / kMaxMutationFactor >
|
||||||
|
NumExecutedMutations / Inputs.size()) {
|
||||||
|
// If the seed was fuzzed a lot more than average, assign zero energy.
|
||||||
|
Weights[i] = 0.;
|
||||||
|
} else {
|
||||||
|
// Otherwise, simply assign the computed energy.
|
||||||
|
Weights[i] = Inputs[i]->Energy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If energy for all seeds is zero, fall back to vanilla schedule.
|
||||||
|
if (Weights[i] > 0.0)
|
||||||
|
VanillaSchedule = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VanillaSchedule) {
|
||||||
|
for (size_t i = 0; i < N; i++)
|
||||||
|
Weights[i] = Inputs[i]->NumFeatures
|
||||||
|
? (i + 1) * (Inputs[i]->HasFocusFunction ? 1000 : 1)
|
||||||
|
: 0.;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FeatureDebug) {
|
||||||
|
for (size_t i = 0; i < N; i++)
|
||||||
|
Printf("%zd ", Inputs[i]->NumFeatures);
|
||||||
|
Printf("SCORE\n");
|
||||||
|
for (size_t i = 0; i < N; i++)
|
||||||
|
Printf("%f ", Weights[i]);
|
||||||
|
Printf("Weights\n");
|
||||||
|
}
|
||||||
|
CorpusDistribution = std::piecewise_constant_distribution<double>(
|
||||||
|
Intervals.begin(), Intervals.end(), Weights.begin());
|
||||||
|
}
|
||||||
|
std::piecewise_constant_distribution<double> CorpusDistribution;
|
||||||
|
|
||||||
|
Vector<double> Intervals;
|
||||||
|
Vector<double> Weights;
|
||||||
|
|
||||||
|
std::unordered_set<std::string> Hashes;
|
||||||
|
Vector<InputInfo*> Inputs;
|
||||||
|
|
||||||
|
size_t NumAddedFeatures = 0;
|
||||||
|
size_t NumUpdatedFeatures = 0;
|
||||||
|
uint32_t InputSizesPerFeature[kFeatureSetSize];
|
||||||
|
uint32_t SmallestElementPerFeature[kFeatureSetSize];
|
||||||
|
|
||||||
|
bool DistributionNeedsUpdate = true;
|
||||||
|
uint16_t FreqOfMostAbundantRareFeature = 0;
|
||||||
|
uint16_t GlobalFeatureFreqs[kFeatureSetSize] = {};
|
||||||
|
Vector<uint32_t> RareFeatures;
|
||||||
|
|
||||||
|
std::string OutputCorpus;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_CORPUS
|
60
custom_mutators/libfuzzer/FuzzerCrossOver.cpp
Normal file
60
custom_mutators/libfuzzer/FuzzerCrossOver.cpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Cross over test inputs.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerMutate.h"
|
||||||
|
#include "FuzzerRandom.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out.
|
||||||
|
size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1,
|
||||||
|
const uint8_t *Data2, size_t Size2,
|
||||||
|
uint8_t *Out, size_t MaxOutSize) {
|
||||||
|
|
||||||
|
assert(Size1 || Size2);
|
||||||
|
MaxOutSize = Rand(MaxOutSize) + 1;
|
||||||
|
size_t OutPos = 0;
|
||||||
|
size_t Pos1 = 0;
|
||||||
|
size_t Pos2 = 0;
|
||||||
|
size_t * InPos = &Pos1;
|
||||||
|
size_t InSize = Size1;
|
||||||
|
const uint8_t *Data = Data1;
|
||||||
|
bool CurrentlyUsingFirstData = true;
|
||||||
|
while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) {
|
||||||
|
|
||||||
|
// Merge a part of Data into Out.
|
||||||
|
size_t OutSizeLeft = MaxOutSize - OutPos;
|
||||||
|
if (*InPos < InSize) {
|
||||||
|
|
||||||
|
size_t InSizeLeft = InSize - *InPos;
|
||||||
|
size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft);
|
||||||
|
size_t ExtraSize = Rand(MaxExtraSize) + 1;
|
||||||
|
memcpy(Out + OutPos, Data + *InPos, ExtraSize);
|
||||||
|
OutPos += ExtraSize;
|
||||||
|
(*InPos) += ExtraSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the other input data on the next iteration.
|
||||||
|
InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1;
|
||||||
|
InSize = CurrentlyUsingFirstData ? Size2 : Size1;
|
||||||
|
Data = CurrentlyUsingFirstData ? Data2 : Data1;
|
||||||
|
CurrentlyUsingFirstData = !CurrentlyUsingFirstData;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return OutPos;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
344
custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp
Normal file
344
custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
//===- FuzzerDataFlowTrace.cpp - DataFlowTrace ---*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// fuzzer::DataFlowTrace
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "FuzzerDataFlowTrace.h"
|
||||||
|
|
||||||
|
#include "FuzzerCommand.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerRandom.h"
|
||||||
|
#include "FuzzerSHA1.h"
|
||||||
|
#include "FuzzerUtil.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <numeric>
|
||||||
|
#include <queue>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
static const char *kFunctionsTxt = "functions.txt";
|
||||||
|
|
||||||
|
bool BlockCoverage::AppendCoverage(const std::string &S) {
|
||||||
|
|
||||||
|
std::stringstream SS(S);
|
||||||
|
return AppendCoverage(SS);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coverage lines have this form:
|
||||||
|
// CN X Y Z T
|
||||||
|
// where N is the number of the function, T is the total number of instrumented
|
||||||
|
// BBs, and X,Y,Z, if present, are the indecies of covered BB.
|
||||||
|
// BB #0, which is the entry block, is not explicitly listed.
|
||||||
|
bool BlockCoverage::AppendCoverage(std::istream &IN) {
|
||||||
|
|
||||||
|
std::string L;
|
||||||
|
while (std::getline(IN, L, '\n')) {
|
||||||
|
|
||||||
|
if (L.empty()) continue;
|
||||||
|
std::stringstream SS(L.c_str() + 1);
|
||||||
|
size_t FunctionId = 0;
|
||||||
|
SS >> FunctionId;
|
||||||
|
if (L[0] == 'F') {
|
||||||
|
|
||||||
|
FunctionsWithDFT.insert(FunctionId);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (L[0] != 'C') continue;
|
||||||
|
Vector<uint32_t> CoveredBlocks;
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
uint32_t BB = 0;
|
||||||
|
SS >> BB;
|
||||||
|
if (!SS) break;
|
||||||
|
CoveredBlocks.push_back(BB);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CoveredBlocks.empty()) return false;
|
||||||
|
uint32_t NumBlocks = CoveredBlocks.back();
|
||||||
|
CoveredBlocks.pop_back();
|
||||||
|
for (auto BB : CoveredBlocks)
|
||||||
|
if (BB >= NumBlocks) return false;
|
||||||
|
auto It = Functions.find(FunctionId);
|
||||||
|
auto &Counters =
|
||||||
|
It == Functions.end()
|
||||||
|
? Functions.insert({FunctionId, Vector<uint32_t>(NumBlocks)})
|
||||||
|
.first->second
|
||||||
|
: It->second;
|
||||||
|
|
||||||
|
if (Counters.size() != NumBlocks) return false; // wrong number of blocks.
|
||||||
|
|
||||||
|
Counters[0]++;
|
||||||
|
for (auto BB : CoveredBlocks)
|
||||||
|
Counters[BB]++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign weights to each function.
|
||||||
|
// General principles:
|
||||||
|
// * any uncovered function gets weight 0.
|
||||||
|
// * a function with lots of uncovered blocks gets bigger weight.
|
||||||
|
// * a function with a less frequently executed code gets bigger weight.
|
||||||
|
Vector<double> BlockCoverage::FunctionWeights(size_t NumFunctions) const {
|
||||||
|
|
||||||
|
Vector<double> Res(NumFunctions);
|
||||||
|
for (auto It : Functions) {
|
||||||
|
|
||||||
|
auto FunctionID = It.first;
|
||||||
|
auto Counters = It.second;
|
||||||
|
assert(FunctionID < NumFunctions);
|
||||||
|
auto &Weight = Res[FunctionID];
|
||||||
|
// Give higher weight if the function has a DFT.
|
||||||
|
Weight = FunctionsWithDFT.count(FunctionID) ? 1000. : 1;
|
||||||
|
// Give higher weight to functions with less frequently seen basic blocks.
|
||||||
|
Weight /= SmallestNonZeroCounter(Counters);
|
||||||
|
// Give higher weight to functions with the most uncovered basic blocks.
|
||||||
|
Weight *= NumberOfUncoveredBlocks(Counters) + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFlowTrace::ReadCoverage(const std::string &DirPath) {
|
||||||
|
|
||||||
|
Vector<SizedFile> Files;
|
||||||
|
GetSizedFilesFromDir(DirPath, &Files);
|
||||||
|
for (auto &SF : Files) {
|
||||||
|
|
||||||
|
auto Name = Basename(SF.File);
|
||||||
|
if (Name == kFunctionsTxt) continue;
|
||||||
|
if (!CorporaHashes.count(Name)) continue;
|
||||||
|
std::ifstream IF(SF.File);
|
||||||
|
Coverage.AppendCoverage(IF);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DFTStringAppendToVector(Vector<uint8_t> * DFT,
|
||||||
|
const std::string &DFTString) {
|
||||||
|
|
||||||
|
assert(DFT->size() == DFTString.size());
|
||||||
|
for (size_t I = 0, Len = DFT->size(); I < Len; I++)
|
||||||
|
(*DFT)[I] = DFTString[I] == '1';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// converts a string of '0' and '1' into a Vector<uint8_t>
|
||||||
|
static Vector<uint8_t> DFTStringToVector(const std::string &DFTString) {
|
||||||
|
|
||||||
|
Vector<uint8_t> DFT(DFTString.size());
|
||||||
|
DFTStringAppendToVector(&DFT, DFTString);
|
||||||
|
return DFT;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ParseError(const char *Err, const std::string &Line) {
|
||||||
|
|
||||||
|
Printf("DataFlowTrace: parse error: %s: Line: %s\n", Err, Line.c_str());
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(metzman): replace std::string with std::string_view for
|
||||||
|
// better performance. Need to figure our how to use string_view on Windows.
|
||||||
|
static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum,
|
||||||
|
std::string *DFTString) {
|
||||||
|
|
||||||
|
if (!Line.empty() && Line[0] != 'F') return false; // Ignore coverage.
|
||||||
|
size_t SpacePos = Line.find(' ');
|
||||||
|
if (SpacePos == std::string::npos)
|
||||||
|
return ParseError("no space in the trace line", Line);
|
||||||
|
if (Line.empty() || Line[0] != 'F')
|
||||||
|
return ParseError("the trace line doesn't start with 'F'", Line);
|
||||||
|
*FunctionNum = std::atol(Line.c_str() + 1);
|
||||||
|
const char *Beg = Line.c_str() + SpacePos + 1;
|
||||||
|
const char *End = Line.c_str() + Line.size();
|
||||||
|
assert(Beg < End);
|
||||||
|
size_t Len = End - Beg;
|
||||||
|
for (size_t I = 0; I < Len; I++) {
|
||||||
|
|
||||||
|
if (Beg[I] != '0' && Beg[I] != '1')
|
||||||
|
return ParseError("the trace should contain only 0 or 1", Line);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
*DFTString = Beg;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction,
|
||||||
|
Vector<SizedFile> &CorporaFiles, Random &Rand) {
|
||||||
|
|
||||||
|
if (DirPath.empty()) return false;
|
||||||
|
Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str());
|
||||||
|
Vector<SizedFile> Files;
|
||||||
|
GetSizedFilesFromDir(DirPath, &Files);
|
||||||
|
std::string L;
|
||||||
|
size_t FocusFuncIdx = SIZE_MAX;
|
||||||
|
Vector<std::string> FunctionNames;
|
||||||
|
|
||||||
|
// Collect the hashes of the corpus files.
|
||||||
|
for (auto &SF : CorporaFiles)
|
||||||
|
CorporaHashes.insert(Hash(FileToVector(SF.File)));
|
||||||
|
|
||||||
|
// Read functions.txt
|
||||||
|
std::ifstream IF(DirPlusFile(DirPath, kFunctionsTxt));
|
||||||
|
size_t NumFunctions = 0;
|
||||||
|
while (std::getline(IF, L, '\n')) {
|
||||||
|
|
||||||
|
FunctionNames.push_back(L);
|
||||||
|
NumFunctions++;
|
||||||
|
if (*FocusFunction == L) FocusFuncIdx = NumFunctions - 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NumFunctions) return false;
|
||||||
|
|
||||||
|
if (*FocusFunction == "auto") {
|
||||||
|
|
||||||
|
// AUTOFOCUS works like this:
|
||||||
|
// * reads the coverage data from the DFT files.
|
||||||
|
// * assigns weights to functions based on coverage.
|
||||||
|
// * chooses a random function according to the weights.
|
||||||
|
ReadCoverage(DirPath);
|
||||||
|
auto Weights = Coverage.FunctionWeights(NumFunctions);
|
||||||
|
Vector<double> Intervals(NumFunctions + 1);
|
||||||
|
std::iota(Intervals.begin(), Intervals.end(), 0);
|
||||||
|
auto Distribution = std::piecewise_constant_distribution<double>(
|
||||||
|
Intervals.begin(), Intervals.end(), Weights.begin());
|
||||||
|
FocusFuncIdx = static_cast<size_t>(Distribution(Rand));
|
||||||
|
*FocusFunction = FunctionNames[FocusFuncIdx];
|
||||||
|
assert(FocusFuncIdx < NumFunctions);
|
||||||
|
Printf("INFO: AUTOFOCUS: %zd %s\n", FocusFuncIdx,
|
||||||
|
FunctionNames[FocusFuncIdx].c_str());
|
||||||
|
for (size_t i = 0; i < NumFunctions; i++) {
|
||||||
|
|
||||||
|
if (!Weights[i]) continue;
|
||||||
|
Printf(" [%zd] W %g\tBB-tot %u\tBB-cov %u\tEntryFreq %u:\t%s\n", i,
|
||||||
|
Weights[i], Coverage.GetNumberOfBlocks(i),
|
||||||
|
Coverage.GetNumberOfCoveredBlocks(i), Coverage.GetCounter(i, 0),
|
||||||
|
FunctionNames[i].c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Read traces.
|
||||||
|
size_t NumTraceFiles = 0;
|
||||||
|
size_t NumTracesWithFocusFunction = 0;
|
||||||
|
for (auto &SF : Files) {
|
||||||
|
|
||||||
|
auto Name = Basename(SF.File);
|
||||||
|
if (Name == kFunctionsTxt) continue;
|
||||||
|
if (!CorporaHashes.count(Name)) continue; // not in the corpus.
|
||||||
|
NumTraceFiles++;
|
||||||
|
// Printf("=== %s\n", Name.c_str());
|
||||||
|
std::ifstream IF(SF.File);
|
||||||
|
while (std::getline(IF, L, '\n')) {
|
||||||
|
|
||||||
|
size_t FunctionNum = 0;
|
||||||
|
std::string DFTString;
|
||||||
|
if (ParseDFTLine(L, &FunctionNum, &DFTString) &&
|
||||||
|
FunctionNum == FocusFuncIdx) {
|
||||||
|
|
||||||
|
NumTracesWithFocusFunction++;
|
||||||
|
|
||||||
|
if (FunctionNum >= NumFunctions)
|
||||||
|
return ParseError("N is greater than the number of functions", L);
|
||||||
|
Traces[Name] = DFTStringToVector(DFTString);
|
||||||
|
// Print just a few small traces.
|
||||||
|
if (NumTracesWithFocusFunction <= 3 && DFTString.size() <= 16)
|
||||||
|
Printf("%s => |%s|\n", Name.c_str(), std::string(DFTString).c_str());
|
||||||
|
break; // No need to parse the following lines.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Printf(
|
||||||
|
"INFO: DataFlowTrace: %zd trace files, %zd functions, "
|
||||||
|
"%zd traces with focus function\n",
|
||||||
|
NumTraceFiles, NumFunctions, NumTracesWithFocusFunction);
|
||||||
|
return NumTraceFiles > 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath,
|
||||||
|
const Vector<SizedFile> &CorporaFiles) {
|
||||||
|
|
||||||
|
Printf("INFO: collecting data flow: bin: %s dir: %s files: %zd\n",
|
||||||
|
DFTBinary.c_str(), DirPath.c_str(), CorporaFiles.size());
|
||||||
|
if (CorporaFiles.empty()) {
|
||||||
|
|
||||||
|
Printf("ERROR: can't collect data flow without corpus provided.");
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static char DFSanEnv[] = "DFSAN_OPTIONS=warn_unimplemented=0";
|
||||||
|
putenv(DFSanEnv);
|
||||||
|
MkDir(DirPath);
|
||||||
|
for (auto &F : CorporaFiles) {
|
||||||
|
|
||||||
|
// For every input F we need to collect the data flow and the coverage.
|
||||||
|
// Data flow collection may fail if we request too many DFSan tags at once.
|
||||||
|
// So, we start from requesting all tags in range [0,Size) and if that fails
|
||||||
|
// we then request tags in [0,Size/2) and [Size/2, Size), and so on.
|
||||||
|
// Function number => DFT.
|
||||||
|
auto OutPath = DirPlusFile(DirPath, Hash(FileToVector(F.File)));
|
||||||
|
std::unordered_map<size_t, Vector<uint8_t>> DFTMap;
|
||||||
|
std::unordered_set<std::string> Cov;
|
||||||
|
Command Cmd;
|
||||||
|
Cmd.addArgument(DFTBinary);
|
||||||
|
Cmd.addArgument(F.File);
|
||||||
|
Cmd.addArgument(OutPath);
|
||||||
|
Printf("CMD: %s\n", Cmd.toString().c_str());
|
||||||
|
ExecuteCommand(Cmd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write functions.txt if it's currently empty or doesn't exist.
|
||||||
|
auto FunctionsTxtPath = DirPlusFile(DirPath, kFunctionsTxt);
|
||||||
|
if (FileToString(FunctionsTxtPath).empty()) {
|
||||||
|
|
||||||
|
Command Cmd;
|
||||||
|
Cmd.addArgument(DFTBinary);
|
||||||
|
Cmd.setOutputFile(FunctionsTxtPath);
|
||||||
|
ExecuteCommand(Cmd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
135
custom_mutators/libfuzzer/FuzzerDataFlowTrace.h
Normal file
135
custom_mutators/libfuzzer/FuzzerDataFlowTrace.h
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
//===- FuzzerDataFlowTrace.h - Internal header for the Fuzzer ---*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// fuzzer::DataFlowTrace; reads and handles a data-flow trace.
|
||||||
|
//
|
||||||
|
// A data flow trace is generated by e.g. dataflow/DataFlow.cpp
|
||||||
|
// and is stored on disk in a separate directory.
|
||||||
|
//
|
||||||
|
// The trace dir contains a file 'functions.txt' which lists function names,
|
||||||
|
// oner per line, e.g.
|
||||||
|
// ==> functions.txt <==
|
||||||
|
// Func2
|
||||||
|
// LLVMFuzzerTestOneInput
|
||||||
|
// Func1
|
||||||
|
//
|
||||||
|
// All other files in the dir are the traces, see dataflow/DataFlow.cpp.
|
||||||
|
// The name of the file is sha1 of the input used to generate the trace.
|
||||||
|
//
|
||||||
|
// Current status:
|
||||||
|
// the data is parsed and the summary is printed, but the data is not yet
|
||||||
|
// used in any other way.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_DATA_FLOW_TRACE
|
||||||
|
#define LLVM_FUZZER_DATA_FLOW_TRACE
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath,
|
||||||
|
const Vector<SizedFile> &CorporaFiles);
|
||||||
|
|
||||||
|
class BlockCoverage {
|
||||||
|
public:
|
||||||
|
bool AppendCoverage(std::istream &IN);
|
||||||
|
bool AppendCoverage(const std::string &S);
|
||||||
|
|
||||||
|
size_t NumCoveredFunctions() const { return Functions.size(); }
|
||||||
|
|
||||||
|
uint32_t GetCounter(size_t FunctionId, size_t BasicBlockId) {
|
||||||
|
auto It = Functions.find(FunctionId);
|
||||||
|
if (It == Functions.end()) return 0;
|
||||||
|
const auto &Counters = It->second;
|
||||||
|
if (BasicBlockId < Counters.size())
|
||||||
|
return Counters[BasicBlockId];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetNumberOfBlocks(size_t FunctionId) {
|
||||||
|
auto It = Functions.find(FunctionId);
|
||||||
|
if (It == Functions.end()) return 0;
|
||||||
|
const auto &Counters = It->second;
|
||||||
|
return Counters.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetNumberOfCoveredBlocks(size_t FunctionId) {
|
||||||
|
auto It = Functions.find(FunctionId);
|
||||||
|
if (It == Functions.end()) return 0;
|
||||||
|
const auto &Counters = It->second;
|
||||||
|
uint32_t Result = 0;
|
||||||
|
for (auto Cnt: Counters)
|
||||||
|
if (Cnt)
|
||||||
|
Result++;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<double> FunctionWeights(size_t NumFunctions) const;
|
||||||
|
void clear() { Functions.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
typedef Vector<uint32_t> CoverageVector;
|
||||||
|
|
||||||
|
uint32_t NumberOfCoveredBlocks(const CoverageVector &Counters) const {
|
||||||
|
uint32_t Res = 0;
|
||||||
|
for (auto Cnt : Counters)
|
||||||
|
if (Cnt)
|
||||||
|
Res++;
|
||||||
|
return Res;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t NumberOfUncoveredBlocks(const CoverageVector &Counters) const {
|
||||||
|
return Counters.size() - NumberOfCoveredBlocks(Counters);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SmallestNonZeroCounter(const CoverageVector &Counters) const {
|
||||||
|
assert(!Counters.empty());
|
||||||
|
uint32_t Res = Counters[0];
|
||||||
|
for (auto Cnt : Counters)
|
||||||
|
if (Cnt)
|
||||||
|
Res = Min(Res, Cnt);
|
||||||
|
assert(Res);
|
||||||
|
return Res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function ID => vector of counters.
|
||||||
|
// Each counter represents how many input files trigger the given basic block.
|
||||||
|
std::unordered_map<size_t, CoverageVector> Functions;
|
||||||
|
// Functions that have DFT entry.
|
||||||
|
std::unordered_set<size_t> FunctionsWithDFT;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DataFlowTrace {
|
||||||
|
public:
|
||||||
|
void ReadCoverage(const std::string &DirPath);
|
||||||
|
bool Init(const std::string &DirPath, std::string *FocusFunction,
|
||||||
|
Vector<SizedFile> &CorporaFiles, Random &Rand);
|
||||||
|
void Clear() { Traces.clear(); }
|
||||||
|
const Vector<uint8_t> *Get(const std::string &InputSha1) const {
|
||||||
|
auto It = Traces.find(InputSha1);
|
||||||
|
if (It != Traces.end())
|
||||||
|
return &It->second;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Input's sha1 => DFT for the FocusFunction.
|
||||||
|
std::unordered_map<std::string, Vector<uint8_t> > Traces;
|
||||||
|
BlockCoverage Coverage;
|
||||||
|
std::unordered_set<std::string> CorporaHashes;
|
||||||
|
};
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_DATA_FLOW_TRACE
|
75
custom_mutators/libfuzzer/FuzzerDefs.h
Normal file
75
custom_mutators/libfuzzer/FuzzerDefs.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Basic definitions.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_DEFS_H
|
||||||
|
#define LLVM_FUZZER_DEFS_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
template <class T> T Min(T a, T b) { return a < b ? a : b; }
|
||||||
|
template <class T> T Max(T a, T b) { return a > b ? a : b; }
|
||||||
|
|
||||||
|
class Random;
|
||||||
|
class Dictionary;
|
||||||
|
class DictionaryEntry;
|
||||||
|
class MutationDispatcher;
|
||||||
|
struct FuzzingOptions;
|
||||||
|
class InputCorpus;
|
||||||
|
struct InputInfo;
|
||||||
|
struct ExternalFunctions;
|
||||||
|
|
||||||
|
// Global interface to functions that may or may not be available.
|
||||||
|
extern ExternalFunctions *EF;
|
||||||
|
|
||||||
|
// We are using a custom allocator to give a different symbol name to STL
|
||||||
|
// containers in order to avoid ODR violations.
|
||||||
|
template<typename T>
|
||||||
|
class fuzzer_allocator: public std::allocator<T> {
|
||||||
|
public:
|
||||||
|
fuzzer_allocator() = default;
|
||||||
|
|
||||||
|
template<class U>
|
||||||
|
fuzzer_allocator(const fuzzer_allocator<U>&) {}
|
||||||
|
|
||||||
|
template<class Other>
|
||||||
|
struct rebind { typedef fuzzer_allocator<Other> other; };
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using Vector = std::vector<T, fuzzer_allocator<T>>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using Set = std::set<T, std::less<T>, fuzzer_allocator<T>>;
|
||||||
|
|
||||||
|
typedef Vector<uint8_t> Unit;
|
||||||
|
typedef Vector<Unit> UnitVector;
|
||||||
|
typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
|
||||||
|
|
||||||
|
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
|
||||||
|
|
||||||
|
uint8_t *ExtraCountersBegin();
|
||||||
|
uint8_t *ExtraCountersEnd();
|
||||||
|
void ClearExtraCounters();
|
||||||
|
|
||||||
|
extern bool RunningUserCallback;
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_DEFS_H
|
118
custom_mutators/libfuzzer/FuzzerDictionary.h
Normal file
118
custom_mutators/libfuzzer/FuzzerDictionary.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// fuzzer::Dictionary
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_DICTIONARY_H
|
||||||
|
#define LLVM_FUZZER_DICTIONARY_H
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerUtil.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
// A simple POD sized array of bytes.
|
||||||
|
template <size_t kMaxSizeT> class FixedWord {
|
||||||
|
public:
|
||||||
|
static const size_t kMaxSize = kMaxSizeT;
|
||||||
|
FixedWord() {}
|
||||||
|
FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); }
|
||||||
|
|
||||||
|
void Set(const uint8_t *B, uint8_t S) {
|
||||||
|
assert(S <= kMaxSize);
|
||||||
|
memcpy(Data, B, S);
|
||||||
|
Size = S;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const FixedWord<kMaxSize> &w) const {
|
||||||
|
return Size == w.Size && 0 == memcmp(Data, w.Data, Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t GetMaxSize() { return kMaxSize; }
|
||||||
|
const uint8_t *data() const { return Data; }
|
||||||
|
uint8_t size() const { return Size; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t Size = 0;
|
||||||
|
uint8_t Data[kMaxSize];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef FixedWord<64> Word;
|
||||||
|
|
||||||
|
class DictionaryEntry {
|
||||||
|
public:
|
||||||
|
DictionaryEntry() {}
|
||||||
|
DictionaryEntry(Word W) : W(W) {}
|
||||||
|
DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {}
|
||||||
|
const Word &GetW() const { return W; }
|
||||||
|
|
||||||
|
bool HasPositionHint() const { return PositionHint != std::numeric_limits<size_t>::max(); }
|
||||||
|
size_t GetPositionHint() const {
|
||||||
|
assert(HasPositionHint());
|
||||||
|
return PositionHint;
|
||||||
|
}
|
||||||
|
void IncUseCount() { UseCount++; }
|
||||||
|
void IncSuccessCount() { SuccessCount++; }
|
||||||
|
size_t GetUseCount() const { return UseCount; }
|
||||||
|
size_t GetSuccessCount() const {return SuccessCount; }
|
||||||
|
|
||||||
|
void Print(const char *PrintAfter = "\n") {
|
||||||
|
PrintASCII(W.data(), W.size());
|
||||||
|
if (HasPositionHint())
|
||||||
|
Printf("@%zd", GetPositionHint());
|
||||||
|
Printf("%s", PrintAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Word W;
|
||||||
|
size_t PositionHint = std::numeric_limits<size_t>::max();
|
||||||
|
size_t UseCount = 0;
|
||||||
|
size_t SuccessCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Dictionary {
|
||||||
|
public:
|
||||||
|
static const size_t kMaxDictSize = 1 << 14;
|
||||||
|
|
||||||
|
bool ContainsWord(const Word &W) const {
|
||||||
|
return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) {
|
||||||
|
return DE.GetW() == W;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const DictionaryEntry *begin() const { return &DE[0]; }
|
||||||
|
const DictionaryEntry *end() const { return begin() + Size; }
|
||||||
|
DictionaryEntry & operator[] (size_t Idx) {
|
||||||
|
assert(Idx < Size);
|
||||||
|
return DE[Idx];
|
||||||
|
}
|
||||||
|
void push_back(DictionaryEntry DE) {
|
||||||
|
if (Size < kMaxDictSize)
|
||||||
|
this->DE[Size++] = DE;
|
||||||
|
}
|
||||||
|
void clear() { Size = 0; }
|
||||||
|
bool empty() const { return Size == 0; }
|
||||||
|
size_t size() const { return Size; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
DictionaryEntry DE[kMaxDictSize];
|
||||||
|
size_t Size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parses one dictionary entry.
|
||||||
|
// If successful, write the enty to Unit and returns true,
|
||||||
|
// otherwise returns false.
|
||||||
|
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U);
|
||||||
|
// Parses the dictionary file, fills Units, returns true iff all lines
|
||||||
|
// were parsed successfully.
|
||||||
|
bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units);
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_DICTIONARY_H
|
1122
custom_mutators/libfuzzer/FuzzerDriver.cpp
Normal file
1122
custom_mutators/libfuzzer/FuzzerDriver.cpp
Normal file
File diff suppressed because it is too large
Load Diff
50
custom_mutators/libfuzzer/FuzzerExtFunctions.def
Normal file
50
custom_mutators/libfuzzer/FuzzerExtFunctions.def
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// This defines the external function pointers that
|
||||||
|
// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The
|
||||||
|
// EXT_FUNC macro must be defined at the point of inclusion. The signature of
|
||||||
|
// the macro is:
|
||||||
|
//
|
||||||
|
// EXT_FUNC(<name>, <return_type>, <function_signature>, <warn_if_missing>)
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// Optional user functions
|
||||||
|
EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false);
|
||||||
|
EXT_FUNC(LLVMFuzzerCustomMutator, size_t,
|
||||||
|
(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed),
|
||||||
|
false);
|
||||||
|
EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
|
||||||
|
(const uint8_t *Data1, size_t Size1,
|
||||||
|
const uint8_t *Data2, size_t Size2,
|
||||||
|
uint8_t *Out, size_t MaxOutSize, unsigned int Seed),
|
||||||
|
false);
|
||||||
|
|
||||||
|
// Sanitizer functions
|
||||||
|
EXT_FUNC(__lsan_enable, void, (), false);
|
||||||
|
EXT_FUNC(__lsan_disable, void, (), false);
|
||||||
|
EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false);
|
||||||
|
EXT_FUNC(__sanitizer_acquire_crash_state, int, (), true);
|
||||||
|
EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int,
|
||||||
|
(void (*malloc_hook)(const volatile void *, size_t),
|
||||||
|
void (*free_hook)(const volatile void *)),
|
||||||
|
false);
|
||||||
|
EXT_FUNC(__sanitizer_log_write, void, (const char *buf, size_t len), false);
|
||||||
|
EXT_FUNC(__sanitizer_purge_allocator, void, (), false);
|
||||||
|
EXT_FUNC(__sanitizer_print_memory_profile, void, (size_t, size_t), false);
|
||||||
|
EXT_FUNC(__sanitizer_print_stack_trace, void, (), true);
|
||||||
|
EXT_FUNC(__sanitizer_symbolize_pc, void,
|
||||||
|
(void *, const char *fmt, char *out_buf, size_t out_buf_size), false);
|
||||||
|
EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int,
|
||||||
|
(void *pc, char *module_path,
|
||||||
|
size_t module_path_len,void **pc_offset), false);
|
||||||
|
EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true);
|
||||||
|
EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false);
|
||||||
|
EXT_FUNC(__msan_scoped_disable_interceptor_checks, void, (), false);
|
||||||
|
EXT_FUNC(__msan_scoped_enable_interceptor_checks, void, (), false);
|
||||||
|
EXT_FUNC(__msan_unpoison, void, (const volatile void *, size_t size), false);
|
||||||
|
EXT_FUNC(__msan_unpoison_param, void, (size_t n), false);
|
34
custom_mutators/libfuzzer/FuzzerExtFunctions.h
Normal file
34
custom_mutators/libfuzzer/FuzzerExtFunctions.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Defines an interface to (possibly optional) functions.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H
|
||||||
|
#define LLVM_FUZZER_EXT_FUNCTIONS_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
struct ExternalFunctions {
|
||||||
|
// Initialize function pointers. Functions that are not available will be set
|
||||||
|
// to nullptr. Do not call this constructor before ``main()`` has been
|
||||||
|
// entered.
|
||||||
|
ExternalFunctions();
|
||||||
|
|
||||||
|
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||||
|
RETURN_TYPE(*NAME) FUNC_SIG = nullptr
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.def"
|
||||||
|
|
||||||
|
#undef EXT_FUNC
|
||||||
|
};
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif
|
60
custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp
Normal file
60
custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Implementation for operating systems that support dlsym(). We only use it on
|
||||||
|
// Apple platforms for now. We don't use this approach on Linux because it
|
||||||
|
// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker.
|
||||||
|
// That is a complication we don't wish to expose to clients right now.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#if LIBFUZZER_APPLE
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
using namespace fuzzer;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T GetFnPtr(const char *FnName, bool WarnIfMissing) {
|
||||||
|
|
||||||
|
dlerror(); // Clear any previous errors.
|
||||||
|
void *Fn = dlsym(RTLD_DEFAULT, FnName);
|
||||||
|
if (Fn == nullptr) {
|
||||||
|
|
||||||
|
if (WarnIfMissing) {
|
||||||
|
|
||||||
|
const char *ErrorMsg = dlerror();
|
||||||
|
Printf("WARNING: Failed to find function \"%s\".", FnName);
|
||||||
|
if (ErrorMsg) Printf(" Reason %s.", ErrorMsg);
|
||||||
|
Printf("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<T>(Fn);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
ExternalFunctions::ExternalFunctions() {
|
||||||
|
\
|
||||||
|
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) this->NAME =
|
||||||
|
GetFnPtr < decltype(ExternalFunctions::NAME)>(#NAME, WARN)
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.def"
|
||||||
|
|
||||||
|
#undef EXT_FUNC
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LIBFUZZER_APPLE
|
||||||
|
|
62
custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp
Normal file
62
custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Implementation for Linux. This relies on the linker's support for weak
|
||||||
|
// symbols. We don't use this approach on Apple platforms because it requires
|
||||||
|
// clients of LibFuzzer to pass ``-U _<symbol_name>`` to the linker to allow
|
||||||
|
// weak symbols to be undefined. That is a complication we don't want to expose
|
||||||
|
// to clients right now.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA || \
|
||||||
|
LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
// Declare these symbols as weak to allow them to be optionally defined.
|
||||||
|
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||||
|
__attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.def"
|
||||||
|
|
||||||
|
#undef EXT_FUNC
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace fuzzer;
|
||||||
|
|
||||||
|
static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) {
|
||||||
|
|
||||||
|
if (FnPtr == nullptr && WarnIfMissing) {
|
||||||
|
|
||||||
|
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
ExternalFunctions::ExternalFunctions() {
|
||||||
|
\
|
||||||
|
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) this->NAME = ::NAME;
|
||||||
|
CheckFnPtr(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(::NAME)),
|
||||||
|
#NAME, WARN);
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.def"
|
||||||
|
|
||||||
|
#undef EXT_FUNC
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
96
custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp
Normal file
96
custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
//=== FuzzerExtWindows.cpp - Interface to external functions --------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Implementation of FuzzerExtFunctions for Windows. Uses alternatename when
|
||||||
|
// compiled with MSVC. Uses weak aliases when compiled with clang. Unfortunately
|
||||||
|
// the method each compiler supports is not supported by the other.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#if LIBFUZZER_WINDOWS
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
|
||||||
|
using namespace fuzzer;
|
||||||
|
|
||||||
|
// Intermediate macro to ensure the parameter is expanded before stringified.
|
||||||
|
#define STRINGIFY_(A) #A
|
||||||
|
#define STRINGIFY(A) STRINGIFY_(A)
|
||||||
|
|
||||||
|
#if LIBFUZZER_MSVC
|
||||||
|
// Copied from compiler-rt/lib/sanitizer_common/sanitizer_win_defs.h
|
||||||
|
#if defined(_M_IX86) || defined(__i386__)
|
||||||
|
#define WIN_SYM_PREFIX "_"
|
||||||
|
#else
|
||||||
|
#define WIN_SYM_PREFIX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Declare external functions as having alternativenames, so that we can
|
||||||
|
// determine if they are not defined.
|
||||||
|
#define EXTERNAL_FUNC(Name, Default) \
|
||||||
|
__pragma( \
|
||||||
|
comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \
|
||||||
|
Name) "=" WIN_SYM_PREFIX STRINGIFY(Default)))
|
||||||
|
#else
|
||||||
|
// Declare external functions as weak to allow them to default to a
|
||||||
|
// specified function if not defined explicitly. We must use weak symbols
|
||||||
|
// because clang's support for alternatename is not 100%, see
|
||||||
|
// https://bugs.llvm.org/show_bug.cgi?id=40218 for more details.
|
||||||
|
#define EXTERNAL_FUNC(Name, Default) \
|
||||||
|
__attribute__((weak, alias(STRINGIFY(Default))))
|
||||||
|
#endif // LIBFUZZER_MSVC
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN)
|
||||||
|
RETURN_TYPE NAME##Def FUNC_SIG {
|
||||||
|
|
||||||
|
Printf("ERROR: Function \"%s\" not defined.\n", #NAME);
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.def"
|
||||||
|
|
||||||
|
#undef EXT_FUNC
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
|
||||||
|
|
||||||
|
if (Fun == FunDef) {
|
||||||
|
|
||||||
|
if (WarnIfMissing)
|
||||||
|
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Fun;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
ExternalFunctions::ExternalFunctions() {
|
||||||
|
\
|
||||||
|
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) this->NAME =
|
||||||
|
GetFnPtr < decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.def"
|
||||||
|
|
||||||
|
#undef EXT_FUNC
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LIBFUZZER_WINDOWS
|
||||||
|
|
71
custom_mutators/libfuzzer/FuzzerExtraCounters.cpp
Normal file
71
custom_mutators/libfuzzer/FuzzerExtraCounters.cpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Extra coverage counters defined by user code.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \
|
||||||
|
LIBFUZZER_OPENBSD || LIBFUZZER_FUCHSIA || LIBFUZZER_EMSCRIPTEN
|
||||||
|
__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters;
|
||||||
|
__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters;
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
uint8_t *ExtraCountersBegin() {
|
||||||
|
|
||||||
|
return &__start___libfuzzer_extra_counters;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *ExtraCountersEnd() {
|
||||||
|
|
||||||
|
return &__stop___libfuzzer_extra_counters;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
void ClearExtraCounters() { // hand-written memset, don't asan-ify.
|
||||||
|
uintptr_t *Beg = reinterpret_cast<uintptr_t *>(ExtraCountersBegin());
|
||||||
|
uintptr_t *End = reinterpret_cast<uintptr_t *>(ExtraCountersEnd());
|
||||||
|
for (; Beg < End; Beg++) {
|
||||||
|
|
||||||
|
*Beg = 0;
|
||||||
|
__asm__ __volatile__("" : : : "memory");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#else
|
||||||
|
// TODO: implement for other platforms.
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
uint8_t *ExtraCountersBegin() {
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *ExtraCountersEnd() {
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearExtraCounters() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
197
custom_mutators/libfuzzer/FuzzerFlags.def
Normal file
197
custom_mutators/libfuzzer/FuzzerFlags.def
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the
|
||||||
|
// point of inclusion. We are not using any flag parsing library for better
|
||||||
|
// portability and independence.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.")
|
||||||
|
FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.")
|
||||||
|
FUZZER_FLAG_INT(runs, -1,
|
||||||
|
"Number of individual test runs (-1 for infinite runs).")
|
||||||
|
FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. "
|
||||||
|
"If 0, libFuzzer tries to guess a good value based on the corpus "
|
||||||
|
"and reports it. ")
|
||||||
|
FUZZER_FLAG_INT(len_control, 100, "Try generating small inputs first, "
|
||||||
|
"then try larger inputs over time. Specifies the rate at which the length "
|
||||||
|
"limit is increased (smaller == faster). If 0, immediately try inputs with "
|
||||||
|
"size up to max_len. Default value is 0, if LLVMFuzzerCustomMutator is used.")
|
||||||
|
FUZZER_FLAG_STRING(seed_inputs, "A comma-separated list of input files "
|
||||||
|
"to use as an additional seed corpus. Alternatively, an \"@\" followed by "
|
||||||
|
"the name of a file containing the comma-separated list.")
|
||||||
|
FUZZER_FLAG_INT(keep_seed, 0, "If 1, keep seed inputs in the corpus even if "
|
||||||
|
"they do not produce new coverage. When used with |reduce_inputs==1|, the "
|
||||||
|
"seed inputs will never be reduced. This option can be useful when seeds are"
|
||||||
|
"not properly formed for the fuzz target but still have useful snippets.")
|
||||||
|
FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.")
|
||||||
|
FUZZER_FLAG_INT(cross_over_uniform_dist, 0, "Experimental. If 1, use a "
|
||||||
|
"uniform probability distribution when choosing inputs to cross over with. "
|
||||||
|
"Some of the inputs in the corpus may never get chosen for mutation "
|
||||||
|
"depending on the input mutation scheduling policy. With this flag, all "
|
||||||
|
"inputs, regardless of the input mutation scheduling policy, can be chosen "
|
||||||
|
"as an input to cross over with. This can be particularly useful with "
|
||||||
|
"|keep_seed==1|; all the initial seed inputs, even though they do not "
|
||||||
|
"increase coverage because they are not properly formed, will still be "
|
||||||
|
"chosen as an input to cross over with.")
|
||||||
|
|
||||||
|
FUZZER_FLAG_INT(mutate_depth, 5,
|
||||||
|
"Apply this number of consecutive mutations to each input.")
|
||||||
|
FUZZER_FLAG_INT(reduce_depth, 0, "Experimental/internal. "
|
||||||
|
"Reduce depth if mutations lose unique features")
|
||||||
|
FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup")
|
||||||
|
FUZZER_FLAG_INT(prefer_small, 1,
|
||||||
|
"If 1, always prefer smaller inputs during the corpus shuffle.")
|
||||||
|
FUZZER_FLAG_INT(
|
||||||
|
timeout, 1200,
|
||||||
|
"Timeout in seconds (if positive). "
|
||||||
|
"If one unit runs more than this number of seconds the process will abort.")
|
||||||
|
FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug "
|
||||||
|
"this exit code will be used.")
|
||||||
|
FUZZER_FLAG_INT(timeout_exitcode, 70, "When libFuzzer reports a timeout "
|
||||||
|
"this exit code will be used.")
|
||||||
|
FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total "
|
||||||
|
"time in seconds to run the fuzzer.")
|
||||||
|
FUZZER_FLAG_INT(help, 0, "Print help.")
|
||||||
|
FUZZER_FLAG_INT(fork, 0, "Experimental mode where fuzzing happens "
|
||||||
|
"in a subprocess")
|
||||||
|
FUZZER_FLAG_INT(ignore_timeouts, 1, "Ignore timeouts in fork mode")
|
||||||
|
FUZZER_FLAG_INT(ignore_ooms, 1, "Ignore OOMs in fork mode")
|
||||||
|
FUZZER_FLAG_INT(ignore_crashes, 0, "Ignore crashes in fork mode")
|
||||||
|
FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be "
|
||||||
|
"merged into the 1-st corpus. Only interesting units will be taken. "
|
||||||
|
"This flag can be used to minimize a corpus.")
|
||||||
|
FUZZER_FLAG_STRING(stop_file, "Stop fuzzing ASAP if this file exists")
|
||||||
|
FUZZER_FLAG_STRING(merge_inner, "internal flag")
|
||||||
|
FUZZER_FLAG_STRING(merge_control_file,
|
||||||
|
"Specify a control file used for the merge process. "
|
||||||
|
"If a merge process gets killed it tries to leave this file "
|
||||||
|
"in a state suitable for resuming the merge. "
|
||||||
|
"By default a temporary file will be used."
|
||||||
|
"The same file can be used for multistep merge process.")
|
||||||
|
FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided"
|
||||||
|
" crash input. Use with -runs=N or -max_total_time=N to limit "
|
||||||
|
"the number attempts."
|
||||||
|
" Use with -exact_artifact_path to specify the output."
|
||||||
|
" Combine with ASAN_OPTIONS=dedup_token_length=3 (or similar) to ensure that"
|
||||||
|
" the minimized input triggers the same crash."
|
||||||
|
)
|
||||||
|
FUZZER_FLAG_INT(cleanse_crash, 0, "If 1, tries to cleanse the provided"
|
||||||
|
" crash input to make it contain fewer original bytes."
|
||||||
|
" Use with -exact_artifact_path to specify the output."
|
||||||
|
)
|
||||||
|
FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag")
|
||||||
|
FUZZER_FLAG_STRING(features_dir, "internal flag. Used to dump feature sets on disk."
|
||||||
|
"Every time a new input is added to the corpus, a corresponding file in the features_dir"
|
||||||
|
" is created containing the unique features of that input."
|
||||||
|
" Features are stored in binary format.")
|
||||||
|
FUZZER_FLAG_STRING(mutation_graph_file, "Saves a graph (in DOT format) to"
|
||||||
|
" mutation_graph_file. The graph contains a vertex for each input that has"
|
||||||
|
" unique coverage; directed edges are provided between parents and children"
|
||||||
|
" where the child has unique coverage, and are recorded with the type of"
|
||||||
|
" mutation that caused the child.")
|
||||||
|
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
|
||||||
|
FUZZER_FLAG_INT(use_memmem, 1,
|
||||||
|
"Use hints from intercepting memmem, strstr, etc")
|
||||||
|
FUZZER_FLAG_INT(use_value_profile, 0,
|
||||||
|
"Experimental. Use value profile to guide fuzzing.")
|
||||||
|
FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations")
|
||||||
|
FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus inputs.")
|
||||||
|
FUZZER_FLAG_INT(reduce_inputs, 1,
|
||||||
|
"Try to reduce the size of inputs while preserving their full feature sets")
|
||||||
|
FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn"
|
||||||
|
" this number of jobs in separate worker processes"
|
||||||
|
" with stdout/stderr redirected to fuzz-JOB.log.")
|
||||||
|
FUZZER_FLAG_UNSIGNED(workers, 0,
|
||||||
|
"Number of simultaneous worker processes to run the jobs."
|
||||||
|
" If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.")
|
||||||
|
FUZZER_FLAG_INT(reload, 1,
|
||||||
|
"Reload the main corpus every <N> seconds to get new units"
|
||||||
|
" discovered by other processes. If 0, disabled")
|
||||||
|
FUZZER_FLAG_INT(report_slow_units, 10,
|
||||||
|
"Report slowest units if they run for more than this number of seconds.")
|
||||||
|
FUZZER_FLAG_INT(only_ascii, 0,
|
||||||
|
"If 1, generate only ASCII (isprint+isspace) inputs.")
|
||||||
|
FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.")
|
||||||
|
FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, "
|
||||||
|
"timeout, or slow inputs) as "
|
||||||
|
"$(artifact_prefix)file")
|
||||||
|
FUZZER_FLAG_STRING(exact_artifact_path,
|
||||||
|
"Write the single artifact on failure (crash, timeout) "
|
||||||
|
"as $(exact_artifact_path). This overrides -artifact_prefix "
|
||||||
|
"and will not use checksum in the file name. Do not "
|
||||||
|
"use the same path for several parallel processes.")
|
||||||
|
FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.")
|
||||||
|
FUZZER_FLAG_INT(print_funcs, 2, "If >=1, print out at most this number of "
|
||||||
|
"newly covered functions.")
|
||||||
|
FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.")
|
||||||
|
FUZZER_FLAG_INT(print_corpus_stats, 0,
|
||||||
|
"If 1, print statistics on corpus elements at exit.")
|
||||||
|
FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text"
|
||||||
|
" at exit.")
|
||||||
|
FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated.")
|
||||||
|
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
|
||||||
|
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.")
|
||||||
|
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
|
||||||
|
FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.")
|
||||||
|
FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.")
|
||||||
|
FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.")
|
||||||
|
FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.")
|
||||||
|
FUZZER_FLAG_INT(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.")
|
||||||
|
FUZZER_FLAG_INT(handle_usr1, 1, "If 1, try to intercept SIGUSR1.")
|
||||||
|
FUZZER_FLAG_INT(handle_usr2, 1, "If 1, try to intercept SIGUSR2.")
|
||||||
|
FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; "
|
||||||
|
"if 2, close stderr; if 3, close both. "
|
||||||
|
"Be careful, this will also close e.g. stderr of asan.")
|
||||||
|
FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled "
|
||||||
|
"try to detect memory leaks during fuzzing (i.e. not only at shut down).")
|
||||||
|
FUZZER_FLAG_INT(purge_allocator_interval, 1, "Purge allocator caches and "
|
||||||
|
"quarantines every <N> seconds. When rss_limit_mb is specified (>0), "
|
||||||
|
"purging starts when RSS exceeds 50% of rss_limit_mb. Pass "
|
||||||
|
"purge_allocator_interval=-1 to disable this functionality.")
|
||||||
|
FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. "
|
||||||
|
"If >= 2 will also print stack traces.")
|
||||||
|
FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon"
|
||||||
|
"reaching this limit of RSS memory usage.")
|
||||||
|
FUZZER_FLAG_INT(malloc_limit_mb, 0, "If non-zero, the fuzzer will exit "
|
||||||
|
"if the target tries to allocate this number of Mb with one malloc call. "
|
||||||
|
"If zero (default) same limit as rss_limit_mb is applied.")
|
||||||
|
FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates"
|
||||||
|
" from the given source location. Example: -exit_on_src_pos=foo.cc:123. "
|
||||||
|
"Used primarily for testing libFuzzer itself.")
|
||||||
|
FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum"
|
||||||
|
" was added to the corpus. "
|
||||||
|
"Used primarily for testing libFuzzer itself.")
|
||||||
|
FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed "
|
||||||
|
"after this one. Useful for fuzzers that need to do their own "
|
||||||
|
"argument parsing.")
|
||||||
|
FUZZER_FLAG_STRING(focus_function, "Experimental. "
|
||||||
|
"Fuzzing will focus on inputs that trigger calls to this function. "
|
||||||
|
"If -focus_function=auto and -data_flow_trace is used, libFuzzer "
|
||||||
|
"will choose the focus functions automatically.")
|
||||||
|
FUZZER_FLAG_INT(entropic, 0, "Experimental. Enables entropic power schedule.")
|
||||||
|
FUZZER_FLAG_INT(entropic_feature_frequency_threshold, 0xFF, "Experimental. If "
|
||||||
|
"entropic is enabled, all features which are observed less often than "
|
||||||
|
"the specified value are considered as rare.")
|
||||||
|
FUZZER_FLAG_INT(entropic_number_of_rarest_features, 100, "Experimental. If "
|
||||||
|
"entropic is enabled, we keep track of the frequencies only for the "
|
||||||
|
"Top-X least abundant features (union features that are considered as "
|
||||||
|
"rare).")
|
||||||
|
FUZZER_FLAG_INT(entropic_scale_per_exec_time, 0, "Experimental. If 1, "
|
||||||
|
"the Entropic power schedule gets scaled based on the input execution "
|
||||||
|
"time. Inputs with lower execution time get scheduled more (up to 30x). "
|
||||||
|
"Note that, if 1, fuzzer stops from being deterministic even if a "
|
||||||
|
"non-zero random seed is given.")
|
||||||
|
|
||||||
|
FUZZER_FLAG_INT(analyze_dict, 0, "Experimental")
|
||||||
|
FUZZER_DEPRECATED_FLAG(use_clang_coverage)
|
||||||
|
FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace")
|
||||||
|
FUZZER_FLAG_STRING(collect_data_flow,
|
||||||
|
"Experimental: collect the data flow trace")
|
||||||
|
|
||||||
|
FUZZER_FLAG_INT(create_missing_dirs, 0, "Automatically attempt to create "
|
||||||
|
"directories for arguments that would normally expect them to already "
|
||||||
|
"exist (i.e. artifact_prefix, exact_artifact_path, features_dir, corpus)")
|
501
custom_mutators/libfuzzer/FuzzerFork.cpp
Normal file
501
custom_mutators/libfuzzer/FuzzerFork.cpp
Normal file
@ -0,0 +1,501 @@
|
|||||||
|
//===- FuzzerFork.cpp - run fuzzing in separate subprocesses --------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Spawn and orchestrate separate fuzzing processes.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "FuzzerCommand.h"
|
||||||
|
#include "FuzzerFork.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerInternal.h"
|
||||||
|
#include "FuzzerMerge.h"
|
||||||
|
#include "FuzzerSHA1.h"
|
||||||
|
#include "FuzzerTracePC.h"
|
||||||
|
#include "FuzzerUtil.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
struct Stats {
|
||||||
|
|
||||||
|
size_t number_of_executed_units = 0;
|
||||||
|
size_t peak_rss_mb = 0;
|
||||||
|
size_t average_exec_per_sec = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static Stats ParseFinalStatsFromLog(const std::string &LogPath) {
|
||||||
|
|
||||||
|
std::ifstream In(LogPath);
|
||||||
|
std::string Line;
|
||||||
|
Stats Res;
|
||||||
|
struct {
|
||||||
|
|
||||||
|
const char *Name;
|
||||||
|
size_t * Var;
|
||||||
|
|
||||||
|
} NameVarPairs[] = {
|
||||||
|
|
||||||
|
{"stat::number_of_executed_units:", &Res.number_of_executed_units},
|
||||||
|
{"stat::peak_rss_mb:", &Res.peak_rss_mb},
|
||||||
|
{"stat::average_exec_per_sec:", &Res.average_exec_per_sec},
|
||||||
|
{nullptr, nullptr},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
while (std::getline(In, Line, '\n')) {
|
||||||
|
|
||||||
|
if (Line.find("stat::") != 0) continue;
|
||||||
|
std::istringstream ISS(Line);
|
||||||
|
std::string Name;
|
||||||
|
size_t Val;
|
||||||
|
ISS >> Name >> Val;
|
||||||
|
for (size_t i = 0; NameVarPairs[i].Name; i++)
|
||||||
|
if (Name == NameVarPairs[i].Name) *NameVarPairs[i].Var = Val;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FuzzJob {
|
||||||
|
|
||||||
|
// Inputs.
|
||||||
|
Command Cmd;
|
||||||
|
std::string CorpusDir;
|
||||||
|
std::string FeaturesDir;
|
||||||
|
std::string LogPath;
|
||||||
|
std::string SeedListPath;
|
||||||
|
std::string CFPath;
|
||||||
|
size_t JobId;
|
||||||
|
|
||||||
|
int DftTimeInSeconds = 0;
|
||||||
|
|
||||||
|
// Fuzzing Outputs.
|
||||||
|
int ExitCode;
|
||||||
|
|
||||||
|
~FuzzJob() {
|
||||||
|
|
||||||
|
RemoveFile(CFPath);
|
||||||
|
RemoveFile(LogPath);
|
||||||
|
RemoveFile(SeedListPath);
|
||||||
|
RmDirRecursive(CorpusDir);
|
||||||
|
RmDirRecursive(FeaturesDir);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GlobalEnv {
|
||||||
|
|
||||||
|
Vector<std::string> Args;
|
||||||
|
Vector<std::string> CorpusDirs;
|
||||||
|
std::string MainCorpusDir;
|
||||||
|
std::string TempDir;
|
||||||
|
std::string DFTDir;
|
||||||
|
std::string DataFlowBinary;
|
||||||
|
Set<uint32_t> Features, Cov;
|
||||||
|
Set<std::string> FilesWithDFT;
|
||||||
|
Vector<std::string> Files;
|
||||||
|
Random * Rand;
|
||||||
|
std::chrono::system_clock::time_point ProcessStartTime;
|
||||||
|
int Verbosity = 0;
|
||||||
|
|
||||||
|
size_t NumTimeouts = 0;
|
||||||
|
size_t NumOOMs = 0;
|
||||||
|
size_t NumCrashes = 0;
|
||||||
|
|
||||||
|
size_t NumRuns = 0;
|
||||||
|
|
||||||
|
std::string StopFile() {
|
||||||
|
|
||||||
|
return DirPlusFile(TempDir, "STOP");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t secondsSinceProcessStartUp() const {
|
||||||
|
|
||||||
|
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
|
std::chrono::system_clock::now() - ProcessStartTime)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FuzzJob *CreateNewJob(size_t JobId) {
|
||||||
|
|
||||||
|
Command Cmd(Args);
|
||||||
|
Cmd.removeFlag("fork");
|
||||||
|
Cmd.removeFlag("runs");
|
||||||
|
Cmd.removeFlag("collect_data_flow");
|
||||||
|
for (auto &C : CorpusDirs) // Remove all corpora from the args.
|
||||||
|
Cmd.removeArgument(C);
|
||||||
|
Cmd.addFlag("reload", "0"); // working in an isolated dir, no reload.
|
||||||
|
Cmd.addFlag("print_final_stats", "1");
|
||||||
|
Cmd.addFlag("print_funcs", "0"); // no need to spend time symbolizing.
|
||||||
|
Cmd.addFlag("max_total_time", std::to_string(std::min((size_t)300, JobId)));
|
||||||
|
Cmd.addFlag("stop_file", StopFile());
|
||||||
|
if (!DataFlowBinary.empty()) {
|
||||||
|
|
||||||
|
Cmd.addFlag("data_flow_trace", DFTDir);
|
||||||
|
if (!Cmd.hasFlag("focus_function")) Cmd.addFlag("focus_function", "auto");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Job = new FuzzJob;
|
||||||
|
std::string Seeds;
|
||||||
|
if (size_t CorpusSubsetSize =
|
||||||
|
std::min(Files.size(), (size_t)sqrt(Files.size() + 2))) {
|
||||||
|
|
||||||
|
auto Time1 = std::chrono::system_clock::now();
|
||||||
|
for (size_t i = 0; i < CorpusSubsetSize; i++) {
|
||||||
|
|
||||||
|
auto &SF = Files[Rand->SkewTowardsLast(Files.size())];
|
||||||
|
Seeds += (Seeds.empty() ? "" : ",") + SF;
|
||||||
|
CollectDFT(SF);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Time2 = std::chrono::system_clock::now();
|
||||||
|
Job->DftTimeInSeconds = duration_cast<seconds>(Time2 - Time1).count();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Seeds.empty()) {
|
||||||
|
|
||||||
|
Job->SeedListPath =
|
||||||
|
DirPlusFile(TempDir, std::to_string(JobId) + ".seeds");
|
||||||
|
WriteToFile(Seeds, Job->SeedListPath);
|
||||||
|
Cmd.addFlag("seed_inputs", "@" + Job->SeedListPath);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Job->LogPath = DirPlusFile(TempDir, std::to_string(JobId) + ".log");
|
||||||
|
Job->CorpusDir = DirPlusFile(TempDir, "C" + std::to_string(JobId));
|
||||||
|
Job->FeaturesDir = DirPlusFile(TempDir, "F" + std::to_string(JobId));
|
||||||
|
Job->CFPath = DirPlusFile(TempDir, std::to_string(JobId) + ".merge");
|
||||||
|
Job->JobId = JobId;
|
||||||
|
|
||||||
|
Cmd.addArgument(Job->CorpusDir);
|
||||||
|
Cmd.addFlag("features_dir", Job->FeaturesDir);
|
||||||
|
|
||||||
|
for (auto &D : {Job->CorpusDir, Job->FeaturesDir}) {
|
||||||
|
|
||||||
|
RmDirRecursive(D);
|
||||||
|
MkDir(D);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Cmd.setOutputFile(Job->LogPath);
|
||||||
|
Cmd.combineOutAndErr();
|
||||||
|
|
||||||
|
Job->Cmd = Cmd;
|
||||||
|
|
||||||
|
if (Verbosity >= 2)
|
||||||
|
Printf("Job %zd/%p Created: %s\n", JobId, Job,
|
||||||
|
Job->Cmd.toString().c_str());
|
||||||
|
// Start from very short runs and gradually increase them.
|
||||||
|
return Job;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunOneMergeJob(FuzzJob *Job) {
|
||||||
|
|
||||||
|
auto Stats = ParseFinalStatsFromLog(Job->LogPath);
|
||||||
|
NumRuns += Stats.number_of_executed_units;
|
||||||
|
|
||||||
|
Vector<SizedFile> TempFiles, MergeCandidates;
|
||||||
|
// Read all newly created inputs and their feature sets.
|
||||||
|
// Choose only those inputs that have new features.
|
||||||
|
GetSizedFilesFromDir(Job->CorpusDir, &TempFiles);
|
||||||
|
std::sort(TempFiles.begin(), TempFiles.end());
|
||||||
|
for (auto &F : TempFiles) {
|
||||||
|
|
||||||
|
auto FeatureFile = F.File;
|
||||||
|
FeatureFile.replace(0, Job->CorpusDir.size(), Job->FeaturesDir);
|
||||||
|
auto FeatureBytes = FileToVector(FeatureFile, 0, false);
|
||||||
|
assert((FeatureBytes.size() % sizeof(uint32_t)) == 0);
|
||||||
|
Vector<uint32_t> NewFeatures(FeatureBytes.size() / sizeof(uint32_t));
|
||||||
|
memcpy(NewFeatures.data(), FeatureBytes.data(), FeatureBytes.size());
|
||||||
|
for (auto Ft : NewFeatures) {
|
||||||
|
|
||||||
|
if (!Features.count(Ft)) {
|
||||||
|
|
||||||
|
MergeCandidates.push_back(F);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!FilesToAdd.empty() || Job->ExitCode != 0)
|
||||||
|
Printf(
|
||||||
|
"#%zd: cov: %zd ft: %zd corp: %zd exec/s %zd "
|
||||||
|
"oom/timeout/crash: %zd/%zd/%zd time: %zds job: %zd dft_time: %d\n",
|
||||||
|
NumRuns, Cov.size(), Features.size(), Files.size(),
|
||||||
|
Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes,
|
||||||
|
secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds);
|
||||||
|
|
||||||
|
if (MergeCandidates.empty()) return;
|
||||||
|
|
||||||
|
Vector<std::string> FilesToAdd;
|
||||||
|
Set<uint32_t> NewFeatures, NewCov;
|
||||||
|
CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features,
|
||||||
|
&NewFeatures, Cov, &NewCov, Job->CFPath, false);
|
||||||
|
for (auto &Path : FilesToAdd) {
|
||||||
|
|
||||||
|
auto U = FileToVector(Path);
|
||||||
|
auto NewPath = DirPlusFile(MainCorpusDir, Hash(U));
|
||||||
|
WriteToFile(U, NewPath);
|
||||||
|
Files.push_back(NewPath);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Features.insert(NewFeatures.begin(), NewFeatures.end());
|
||||||
|
Cov.insert(NewCov.begin(), NewCov.end());
|
||||||
|
for (auto Idx : NewCov)
|
||||||
|
if (auto *TE = TPC.PCTableEntryByIdx(Idx))
|
||||||
|
if (TPC.PcIsFuncEntry(TE))
|
||||||
|
PrintPC(" NEW_FUNC: %p %F %L\n", "",
|
||||||
|
TPC.GetNextInstructionPc(TE->PC));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollectDFT(const std::string &InputPath) {
|
||||||
|
|
||||||
|
if (DataFlowBinary.empty()) return;
|
||||||
|
if (!FilesWithDFT.insert(InputPath).second) return;
|
||||||
|
Command Cmd(Args);
|
||||||
|
Cmd.removeFlag("fork");
|
||||||
|
Cmd.removeFlag("runs");
|
||||||
|
Cmd.addFlag("data_flow_trace", DFTDir);
|
||||||
|
Cmd.addArgument(InputPath);
|
||||||
|
for (auto &C : CorpusDirs) // Remove all corpora from the args.
|
||||||
|
Cmd.removeArgument(C);
|
||||||
|
Cmd.setOutputFile(DirPlusFile(TempDir, "dft.log"));
|
||||||
|
Cmd.combineOutAndErr();
|
||||||
|
// Printf("CollectDFT: %s\n", Cmd.toString().c_str());
|
||||||
|
ExecuteCommand(Cmd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JobQueue {
|
||||||
|
|
||||||
|
std::queue<FuzzJob *> Qu;
|
||||||
|
std::mutex Mu;
|
||||||
|
std::condition_variable Cv;
|
||||||
|
|
||||||
|
void Push(FuzzJob *Job) {
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> Lock(Mu);
|
||||||
|
Qu.push(Job);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Cv.notify_one();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FuzzJob *Pop() {
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> Lk(Mu);
|
||||||
|
// std::lock_guard<std::mutex> Lock(Mu);
|
||||||
|
Cv.wait(Lk, [&] { return !Qu.empty(); });
|
||||||
|
assert(!Qu.empty());
|
||||||
|
auto Job = Qu.front();
|
||||||
|
Qu.pop();
|
||||||
|
return Job;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) {
|
||||||
|
|
||||||
|
while (auto Job = FuzzQ->Pop()) {
|
||||||
|
|
||||||
|
// Printf("WorkerThread: job %p\n", Job);
|
||||||
|
Job->ExitCode = ExecuteCommand(Job->Cmd);
|
||||||
|
MergeQ->Push(Job);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is just a skeleton of an experimental -fork=1 feature.
|
||||||
|
void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
|
||||||
|
const Vector<std::string> &Args,
|
||||||
|
const Vector<std::string> &CorpusDirs, int NumJobs) {
|
||||||
|
|
||||||
|
Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs);
|
||||||
|
|
||||||
|
GlobalEnv Env;
|
||||||
|
Env.Args = Args;
|
||||||
|
Env.CorpusDirs = CorpusDirs;
|
||||||
|
Env.Rand = &Rand;
|
||||||
|
Env.Verbosity = Options.Verbosity;
|
||||||
|
Env.ProcessStartTime = std::chrono::system_clock::now();
|
||||||
|
Env.DataFlowBinary = Options.CollectDataFlow;
|
||||||
|
|
||||||
|
Vector<SizedFile> SeedFiles;
|
||||||
|
for (auto &Dir : CorpusDirs)
|
||||||
|
GetSizedFilesFromDir(Dir, &SeedFiles);
|
||||||
|
std::sort(SeedFiles.begin(), SeedFiles.end());
|
||||||
|
Env.TempDir = TempPath("FuzzWithFork", ".dir");
|
||||||
|
Env.DFTDir = DirPlusFile(Env.TempDir, "DFT");
|
||||||
|
RmDirRecursive(Env.TempDir); // in case there is a leftover from old runs.
|
||||||
|
MkDir(Env.TempDir);
|
||||||
|
MkDir(Env.DFTDir);
|
||||||
|
|
||||||
|
if (CorpusDirs.empty())
|
||||||
|
MkDir(Env.MainCorpusDir = DirPlusFile(Env.TempDir, "C"));
|
||||||
|
else
|
||||||
|
Env.MainCorpusDir = CorpusDirs[0];
|
||||||
|
|
||||||
|
if (Options.KeepSeed) {
|
||||||
|
|
||||||
|
for (auto &File : SeedFiles)
|
||||||
|
Env.Files.push_back(File.File);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
auto CFPath = DirPlusFile(Env.TempDir, "merge.txt");
|
||||||
|
CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features,
|
||||||
|
{}, &Env.Cov, CFPath, false);
|
||||||
|
RemoveFile(CFPath);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs,
|
||||||
|
Env.Files.size(), Env.TempDir.c_str());
|
||||||
|
|
||||||
|
int ExitCode = 0;
|
||||||
|
|
||||||
|
JobQueue FuzzQ, MergeQ;
|
||||||
|
|
||||||
|
auto StopJobs = [&]() {
|
||||||
|
|
||||||
|
for (int i = 0; i < NumJobs; i++)
|
||||||
|
FuzzQ.Push(nullptr);
|
||||||
|
MergeQ.Push(nullptr);
|
||||||
|
WriteToFile(Unit({1}), Env.StopFile());
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t JobId = 1;
|
||||||
|
Vector<std::thread> Threads;
|
||||||
|
for (int t = 0; t < NumJobs; t++) {
|
||||||
|
|
||||||
|
Threads.push_back(std::thread(WorkerThread, &FuzzQ, &MergeQ));
|
||||||
|
FuzzQ.Push(Env.CreateNewJob(JobId++));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
std::unique_ptr<FuzzJob> Job(MergeQ.Pop());
|
||||||
|
if (!Job) break;
|
||||||
|
ExitCode = Job->ExitCode;
|
||||||
|
if (ExitCode == Options.InterruptExitCode) {
|
||||||
|
|
||||||
|
Printf("==%lu== libFuzzer: a child was interrupted; exiting\n", GetPid());
|
||||||
|
StopJobs();
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Fuzzer::MaybeExitGracefully();
|
||||||
|
|
||||||
|
Env.RunOneMergeJob(Job.get());
|
||||||
|
|
||||||
|
// Continue if our crash is one of the ignorred ones.
|
||||||
|
if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode)
|
||||||
|
Env.NumTimeouts++;
|
||||||
|
else if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode)
|
||||||
|
Env.NumOOMs++;
|
||||||
|
else if (ExitCode != 0) {
|
||||||
|
|
||||||
|
Env.NumCrashes++;
|
||||||
|
if (Options.IgnoreCrashes) {
|
||||||
|
|
||||||
|
std::ifstream In(Job->LogPath);
|
||||||
|
std::string Line;
|
||||||
|
while (std::getline(In, Line, '\n'))
|
||||||
|
if (Line.find("ERROR:") != Line.npos ||
|
||||||
|
Line.find("runtime error:") != Line.npos)
|
||||||
|
Printf("%s\n", Line.c_str());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// And exit if we don't ignore this crash.
|
||||||
|
Printf("INFO: log from the inner process:\n%s",
|
||||||
|
FileToString(Job->LogPath).c_str());
|
||||||
|
StopJobs();
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop if we are over the time budget.
|
||||||
|
// This is not precise, since other threads are still running
|
||||||
|
// and we will wait while joining them.
|
||||||
|
// We also don't stop instantly: other jobs need to finish.
|
||||||
|
if (Options.MaxTotalTimeSec > 0 &&
|
||||||
|
Env.secondsSinceProcessStartUp() >= (size_t)Options.MaxTotalTimeSec) {
|
||||||
|
|
||||||
|
Printf("INFO: fuzzed for %zd seconds, wrapping up soon\n",
|
||||||
|
Env.secondsSinceProcessStartUp());
|
||||||
|
StopJobs();
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Env.NumRuns >= Options.MaxNumberOfRuns) {
|
||||||
|
|
||||||
|
Printf("INFO: fuzzed for %zd iterations, wrapping up soon\n",
|
||||||
|
Env.NumRuns);
|
||||||
|
StopJobs();
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FuzzQ.Push(Env.CreateNewJob(JobId++));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &T : Threads)
|
||||||
|
T.join();
|
||||||
|
|
||||||
|
// The workers have terminated. Don't try to remove the directory before they
|
||||||
|
// terminate to avoid a race condition preventing cleanup on Windows.
|
||||||
|
RmDirRecursive(Env.TempDir);
|
||||||
|
|
||||||
|
// Use the exit code from the last child process.
|
||||||
|
Printf("INFO: exiting: %d time: %zds\n", ExitCode,
|
||||||
|
Env.secondsSinceProcessStartUp());
|
||||||
|
exit(ExitCode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
24
custom_mutators/libfuzzer/FuzzerFork.h
Normal file
24
custom_mutators/libfuzzer/FuzzerFork.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//===- FuzzerFork.h - run fuzzing in sub-processes --------------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_FORK_H
|
||||||
|
#define LLVM_FUZZER_FORK_H
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerOptions.h"
|
||||||
|
#include "FuzzerRandom.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
|
||||||
|
const Vector<std::string> &Args,
|
||||||
|
const Vector<std::string> &CorpusDirs, int NumJobs);
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_FORK_H
|
248
custom_mutators/libfuzzer/FuzzerIO.cpp
Normal file
248
custom_mutators/libfuzzer/FuzzerIO.cpp
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
//===- FuzzerIO.cpp - IO utils. -------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// IO functions.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerExtFunctions.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerUtil.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
static FILE *OutputFile = stderr;
|
||||||
|
|
||||||
|
long GetEpoch(const std::string &Path) {
|
||||||
|
|
||||||
|
struct stat St;
|
||||||
|
if (stat(Path.c_str(), &St)) return 0; // Can't stat, be conservative.
|
||||||
|
return St.st_mtime;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
|
||||||
|
|
||||||
|
std::ifstream T(Path, std::ios::binary);
|
||||||
|
if (ExitOnError && !T) {
|
||||||
|
|
||||||
|
Printf("No such directory: %s; exiting\n", Path.c_str());
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
T.seekg(0, T.end);
|
||||||
|
auto EndPos = T.tellg();
|
||||||
|
if (EndPos < 0) return {};
|
||||||
|
size_t FileLen = EndPos;
|
||||||
|
if (MaxSize) FileLen = std::min(FileLen, MaxSize);
|
||||||
|
|
||||||
|
T.seekg(0, T.beg);
|
||||||
|
Unit Res(FileLen);
|
||||||
|
T.read(reinterpret_cast<char *>(Res.data()), FileLen);
|
||||||
|
return Res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FileToString(const std::string &Path) {
|
||||||
|
|
||||||
|
std::ifstream T(Path, std::ios::binary);
|
||||||
|
return std::string((std::istreambuf_iterator<char>(T)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyFileToErr(const std::string &Path) {
|
||||||
|
|
||||||
|
Printf("%s", FileToString(Path).c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteToFile(const Unit &U, const std::string &Path) {
|
||||||
|
|
||||||
|
WriteToFile(U.data(), U.size(), Path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteToFile(const std::string &Data, const std::string &Path) {
|
||||||
|
|
||||||
|
WriteToFile(reinterpret_cast<const uint8_t *>(Data.c_str()), Data.size(),
|
||||||
|
Path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) {
|
||||||
|
|
||||||
|
// Use raw C interface because this function may be called from a sig handler.
|
||||||
|
FILE *Out = fopen(Path.c_str(), "wb");
|
||||||
|
if (!Out) return;
|
||||||
|
fwrite(Data, sizeof(Data[0]), Size, Out);
|
||||||
|
fclose(Out);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendToFile(const std::string &Data, const std::string &Path) {
|
||||||
|
|
||||||
|
AppendToFile(reinterpret_cast<const uint8_t *>(Data.data()), Data.size(),
|
||||||
|
Path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path) {
|
||||||
|
|
||||||
|
FILE *Out = fopen(Path.c_str(), "a");
|
||||||
|
if (!Out) return;
|
||||||
|
fwrite(Data, sizeof(Data[0]), Size, Out);
|
||||||
|
fclose(Out);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, long *Epoch,
|
||||||
|
size_t MaxSize, bool ExitOnError) {
|
||||||
|
|
||||||
|
long E = Epoch ? *Epoch : 0;
|
||||||
|
Vector<std::string> Files;
|
||||||
|
ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/ true);
|
||||||
|
size_t NumLoaded = 0;
|
||||||
|
for (size_t i = 0; i < Files.size(); i++) {
|
||||||
|
|
||||||
|
auto &X = Files[i];
|
||||||
|
if (Epoch && GetEpoch(X) < E) continue;
|
||||||
|
NumLoaded++;
|
||||||
|
if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024)
|
||||||
|
Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path);
|
||||||
|
auto S = FileToVector(X, MaxSize, ExitOnError);
|
||||||
|
if (!S.empty()) V->push_back(S);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) {
|
||||||
|
|
||||||
|
Vector<std::string> Files;
|
||||||
|
ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/ true);
|
||||||
|
for (auto &File : Files)
|
||||||
|
if (size_t Size = FileSize(File)) V->push_back({File, Size});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DirPlusFile(const std::string &DirPath,
|
||||||
|
const std::string &FileName) {
|
||||||
|
|
||||||
|
return DirPath + GetSeparator() + FileName;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DupAndCloseStderr() {
|
||||||
|
|
||||||
|
int OutputFd = DuplicateFile(2);
|
||||||
|
if (OutputFd >= 0) {
|
||||||
|
|
||||||
|
FILE *NewOutputFile = OpenFile(OutputFd, "w");
|
||||||
|
if (NewOutputFile) {
|
||||||
|
|
||||||
|
OutputFile = NewOutputFile;
|
||||||
|
if (EF->__sanitizer_set_report_fd)
|
||||||
|
EF->__sanitizer_set_report_fd(
|
||||||
|
reinterpret_cast<void *>(GetHandleFromFd(OutputFd)));
|
||||||
|
DiscardOutput(2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseStdout() {
|
||||||
|
|
||||||
|
DiscardOutput(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Printf(const char *Fmt, ...) {
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, Fmt);
|
||||||
|
vfprintf(OutputFile, Fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fflush(OutputFile);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPrintf(bool Verbose, const char *Fmt, ...) {
|
||||||
|
|
||||||
|
if (!Verbose) return;
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, Fmt);
|
||||||
|
vfprintf(OutputFile, Fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fflush(OutputFile);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool MkDirRecursiveInner(const std::string &Leaf) {
|
||||||
|
|
||||||
|
// Prevent chance of potential infinite recursion
|
||||||
|
if (Leaf == ".") return true;
|
||||||
|
|
||||||
|
const std::string &Dir = DirName(Leaf);
|
||||||
|
|
||||||
|
if (IsDirectory(Dir)) {
|
||||||
|
|
||||||
|
MkDir(Leaf);
|
||||||
|
return IsDirectory(Leaf);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = MkDirRecursiveInner(Dir);
|
||||||
|
if (!ret) {
|
||||||
|
|
||||||
|
// Give up early if a previous MkDir failed
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MkDir(Leaf);
|
||||||
|
return IsDirectory(Leaf);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MkDirRecursive(const std::string &Dir) {
|
||||||
|
|
||||||
|
if (Dir.empty()) return false;
|
||||||
|
|
||||||
|
if (IsDirectory(Dir)) return true;
|
||||||
|
|
||||||
|
return MkDirRecursiveInner(Dir);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RmDirRecursive(const std::string &Dir) {
|
||||||
|
|
||||||
|
IterateDirRecursive(
|
||||||
|
Dir, [](const std::string &Path) {},
|
||||||
|
[](const std::string &Path) { RmDir(Path); },
|
||||||
|
[](const std::string &Path) { RemoveFile(Path); });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TempPath(const char *Prefix, const char *Extension) {
|
||||||
|
|
||||||
|
return DirPlusFile(TmpDir(), std::string("libFuzzerTemp.") + Prefix +
|
||||||
|
std::to_string(GetPid()) + Extension);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
112
custom_mutators/libfuzzer/FuzzerIO.h
Normal file
112
custom_mutators/libfuzzer/FuzzerIO.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// IO interface.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_IO_H
|
||||||
|
#define LLVM_FUZZER_IO_H
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
long GetEpoch(const std::string &Path);
|
||||||
|
|
||||||
|
Unit FileToVector(const std::string &Path, size_t MaxSize = 0,
|
||||||
|
bool ExitOnError = true);
|
||||||
|
|
||||||
|
std::string FileToString(const std::string &Path);
|
||||||
|
|
||||||
|
void CopyFileToErr(const std::string &Path);
|
||||||
|
|
||||||
|
void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path);
|
||||||
|
// Write Data.c_str() to the file without terminating null character.
|
||||||
|
void WriteToFile(const std::string &Data, const std::string &Path);
|
||||||
|
void WriteToFile(const Unit &U, const std::string &Path);
|
||||||
|
|
||||||
|
void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path);
|
||||||
|
void AppendToFile(const std::string &Data, const std::string &Path);
|
||||||
|
|
||||||
|
void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V,
|
||||||
|
long *Epoch, size_t MaxSize, bool ExitOnError);
|
||||||
|
|
||||||
|
// Returns "Dir/FileName" or equivalent for the current OS.
|
||||||
|
std::string DirPlusFile(const std::string &DirPath,
|
||||||
|
const std::string &FileName);
|
||||||
|
|
||||||
|
// Returns the name of the dir, similar to the 'dirname' utility.
|
||||||
|
std::string DirName(const std::string &FileName);
|
||||||
|
|
||||||
|
// Returns path to a TmpDir.
|
||||||
|
std::string TmpDir();
|
||||||
|
|
||||||
|
std::string TempPath(const char *Prefix, const char *Extension);
|
||||||
|
|
||||||
|
bool IsInterestingCoverageFile(const std::string &FileName);
|
||||||
|
|
||||||
|
void DupAndCloseStderr();
|
||||||
|
|
||||||
|
void CloseStdout();
|
||||||
|
|
||||||
|
void Printf(const char *Fmt, ...);
|
||||||
|
void VPrintf(bool Verbose, const char *Fmt, ...);
|
||||||
|
|
||||||
|
// Print using raw syscalls, useful when printing at early init stages.
|
||||||
|
void RawPrint(const char *Str);
|
||||||
|
|
||||||
|
// Platform specific functions:
|
||||||
|
bool IsFile(const std::string &Path);
|
||||||
|
bool IsDirectory(const std::string &Path);
|
||||||
|
size_t FileSize(const std::string &Path);
|
||||||
|
|
||||||
|
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||||
|
Vector<std::string> *V, bool TopDir);
|
||||||
|
|
||||||
|
bool MkDirRecursive(const std::string &Dir);
|
||||||
|
void RmDirRecursive(const std::string &Dir);
|
||||||
|
|
||||||
|
// Iterate files and dirs inside Dir, recursively.
|
||||||
|
// Call DirPreCallback/DirPostCallback on dirs before/after
|
||||||
|
// calling FileCallback on files.
|
||||||
|
void IterateDirRecursive(const std::string &Dir,
|
||||||
|
void (*DirPreCallback)(const std::string &Dir),
|
||||||
|
void (*DirPostCallback)(const std::string &Dir),
|
||||||
|
void (*FileCallback)(const std::string &Dir));
|
||||||
|
|
||||||
|
struct SizedFile {
|
||||||
|
std::string File;
|
||||||
|
size_t Size;
|
||||||
|
bool operator<(const SizedFile &B) const { return Size < B.Size; }
|
||||||
|
};
|
||||||
|
|
||||||
|
void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
|
||||||
|
|
||||||
|
char GetSeparator();
|
||||||
|
bool IsSeparator(char C);
|
||||||
|
// Similar to the basename utility: returns the file name w/o the dir prefix.
|
||||||
|
std::string Basename(const std::string &Path);
|
||||||
|
|
||||||
|
FILE* OpenFile(int Fd, const char *Mode);
|
||||||
|
|
||||||
|
int CloseFile(int Fd);
|
||||||
|
|
||||||
|
int DuplicateFile(int Fd);
|
||||||
|
|
||||||
|
void RemoveFile(const std::string &Path);
|
||||||
|
void RenameFile(const std::string &OldPath, const std::string &NewPath);
|
||||||
|
|
||||||
|
intptr_t GetHandleFromFd(int fd);
|
||||||
|
|
||||||
|
void MkDir(const std::string &Path);
|
||||||
|
void RmDir(const std::string &Path);
|
||||||
|
|
||||||
|
const std::string &getDevNull();
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_IO_H
|
223
custom_mutators/libfuzzer/FuzzerIOPosix.cpp
Normal file
223
custom_mutators/libfuzzer/FuzzerIOPosix.cpp
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// IO functions implementation using Posix API.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#if LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
bool IsFile(const std::string &Path) {
|
||||||
|
|
||||||
|
struct stat St;
|
||||||
|
if (stat(Path.c_str(), &St)) return false;
|
||||||
|
return S_ISREG(St.st_mode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDirectory(const std::string &Path) {
|
||||||
|
|
||||||
|
struct stat St;
|
||||||
|
if (stat(Path.c_str(), &St)) return false;
|
||||||
|
return S_ISDIR(St.st_mode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FileSize(const std::string &Path) {
|
||||||
|
|
||||||
|
struct stat St;
|
||||||
|
if (stat(Path.c_str(), &St)) return 0;
|
||||||
|
return St.st_size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Basename(const std::string &Path) {
|
||||||
|
|
||||||
|
size_t Pos = Path.rfind(GetSeparator());
|
||||||
|
if (Pos == std::string::npos) return Path;
|
||||||
|
assert(Pos < Path.size());
|
||||||
|
return Path.substr(Pos + 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||||
|
Vector<std::string> *V, bool TopDir) {
|
||||||
|
|
||||||
|
auto E = GetEpoch(Dir);
|
||||||
|
if (Epoch)
|
||||||
|
if (E && *Epoch >= E) return;
|
||||||
|
|
||||||
|
DIR *D = opendir(Dir.c_str());
|
||||||
|
if (!D) {
|
||||||
|
|
||||||
|
Printf("%s: %s; exiting\n", strerror(errno), Dir.c_str());
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
while (auto E = readdir(D)) {
|
||||||
|
|
||||||
|
std::string Path = DirPlusFile(Dir, E->d_name);
|
||||||
|
if (E->d_type == DT_REG || E->d_type == DT_LNK ||
|
||||||
|
(E->d_type == DT_UNKNOWN && IsFile(Path)))
|
||||||
|
V->push_back(Path);
|
||||||
|
else if ((E->d_type == DT_DIR ||
|
||||||
|
(E->d_type == DT_UNKNOWN && IsDirectory(Path))) &&
|
||||||
|
*E->d_name != '.')
|
||||||
|
ListFilesInDirRecursive(Path, Epoch, V, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(D);
|
||||||
|
if (Epoch && TopDir) *Epoch = E;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void IterateDirRecursive(const std::string &Dir,
|
||||||
|
void (*DirPreCallback)(const std::string &Dir),
|
||||||
|
void (*DirPostCallback)(const std::string &Dir),
|
||||||
|
void (*FileCallback)(const std::string &Dir)) {
|
||||||
|
|
||||||
|
DirPreCallback(Dir);
|
||||||
|
DIR *D = opendir(Dir.c_str());
|
||||||
|
if (!D) return;
|
||||||
|
while (auto E = readdir(D)) {
|
||||||
|
|
||||||
|
std::string Path = DirPlusFile(Dir, E->d_name);
|
||||||
|
if (E->d_type == DT_REG || E->d_type == DT_LNK ||
|
||||||
|
(E->d_type == DT_UNKNOWN && IsFile(Path)))
|
||||||
|
FileCallback(Path);
|
||||||
|
else if ((E->d_type == DT_DIR ||
|
||||||
|
(E->d_type == DT_UNKNOWN && IsDirectory(Path))) &&
|
||||||
|
*E->d_name != '.')
|
||||||
|
IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(D);
|
||||||
|
DirPostCallback(Dir);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
char GetSeparator() {
|
||||||
|
|
||||||
|
return '/';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSeparator(char C) {
|
||||||
|
|
||||||
|
return C == '/';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *OpenFile(int Fd, const char *Mode) {
|
||||||
|
|
||||||
|
return fdopen(Fd, Mode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int CloseFile(int fd) {
|
||||||
|
|
||||||
|
return close(fd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int DuplicateFile(int Fd) {
|
||||||
|
|
||||||
|
return dup(Fd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveFile(const std::string &Path) {
|
||||||
|
|
||||||
|
unlink(Path.c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenameFile(const std::string &OldPath, const std::string &NewPath) {
|
||||||
|
|
||||||
|
rename(OldPath.c_str(), NewPath.c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
intptr_t GetHandleFromFd(int fd) {
|
||||||
|
|
||||||
|
return static_cast<intptr_t>(fd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DirName(const std::string &FileName) {
|
||||||
|
|
||||||
|
char *Tmp = new char[FileName.size() + 1];
|
||||||
|
memcpy(Tmp, FileName.c_str(), FileName.size() + 1);
|
||||||
|
std::string Res = dirname(Tmp);
|
||||||
|
delete[] Tmp;
|
||||||
|
return Res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TmpDir() {
|
||||||
|
|
||||||
|
if (auto Env = getenv("TMPDIR")) return Env;
|
||||||
|
return "/tmp";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInterestingCoverageFile(const std::string &FileName) {
|
||||||
|
|
||||||
|
if (FileName.find("compiler-rt/lib/") != std::string::npos)
|
||||||
|
return false; // sanitizer internal.
|
||||||
|
if (FileName.find("/usr/lib/") != std::string::npos) return false;
|
||||||
|
if (FileName.find("/usr/include/") != std::string::npos) return false;
|
||||||
|
if (FileName == "<null>") return false;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawPrint(const char *Str) {
|
||||||
|
|
||||||
|
write(2, Str, strlen(Str));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MkDir(const std::string &Path) {
|
||||||
|
|
||||||
|
mkdir(Path.c_str(), 0700);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RmDir(const std::string &Path) {
|
||||||
|
|
||||||
|
rmdir(Path.c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &getDevNull() {
|
||||||
|
|
||||||
|
static const std::string devNull = "/dev/null";
|
||||||
|
return devNull;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LIBFUZZER_POSIX
|
||||||
|
|
513
custom_mutators/libfuzzer/FuzzerIOWindows.cpp
Normal file
513
custom_mutators/libfuzzer/FuzzerIOWindows.cpp
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// IO functions implementation for Windows.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#if LIBFUZZER_WINDOWS
|
||||||
|
|
||||||
|
#include "FuzzerExtFunctions.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <fstream>
|
||||||
|
#include <io.h>
|
||||||
|
#include <iterator>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
static bool IsFile(const std::string &Path, const DWORD &FileAttributes) {
|
||||||
|
|
||||||
|
if (FileAttributes & FILE_ATTRIBUTE_NORMAL) return true;
|
||||||
|
|
||||||
|
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) return false;
|
||||||
|
|
||||||
|
HANDLE FileHandle(CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL,
|
||||||
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
|
||||||
|
|
||||||
|
if (FileHandle == INVALID_HANDLE_VALUE) {
|
||||||
|
|
||||||
|
Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
|
||||||
|
GetLastError());
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD FileType = GetFileType(FileHandle);
|
||||||
|
|
||||||
|
if (FileType == FILE_TYPE_UNKNOWN) {
|
||||||
|
|
||||||
|
Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
|
||||||
|
GetLastError());
|
||||||
|
CloseHandle(FileHandle);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FileType != FILE_TYPE_DISK) {
|
||||||
|
|
||||||
|
CloseHandle(FileHandle);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(FileHandle);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFile(const std::string &Path) {
|
||||||
|
|
||||||
|
DWORD Att = GetFileAttributesA(Path.c_str());
|
||||||
|
|
||||||
|
if (Att == INVALID_FILE_ATTRIBUTES) {
|
||||||
|
|
||||||
|
Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
|
||||||
|
Path.c_str(), GetLastError());
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsFile(Path, Att);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsDir(DWORD FileAttrs) {
|
||||||
|
|
||||||
|
if (FileAttrs == INVALID_FILE_ATTRIBUTES) return false;
|
||||||
|
return FileAttrs & FILE_ATTRIBUTE_DIRECTORY;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDirectory(const std::string &Path) {
|
||||||
|
|
||||||
|
DWORD Att = GetFileAttributesA(Path.c_str());
|
||||||
|
|
||||||
|
if (Att == INVALID_FILE_ATTRIBUTES) {
|
||||||
|
|
||||||
|
Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
|
||||||
|
Path.c_str(), GetLastError());
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsDir(Att);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Basename(const std::string &Path) {
|
||||||
|
|
||||||
|
size_t Pos = Path.find_last_of("/\\");
|
||||||
|
if (Pos == std::string::npos) return Path;
|
||||||
|
assert(Pos < Path.size());
|
||||||
|
return Path.substr(Pos + 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FileSize(const std::string &Path) {
|
||||||
|
|
||||||
|
WIN32_FILE_ATTRIBUTE_DATA attr;
|
||||||
|
if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) {
|
||||||
|
|
||||||
|
DWORD LastError = GetLastError();
|
||||||
|
if (LastError != ERROR_FILE_NOT_FOUND)
|
||||||
|
Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
|
||||||
|
Path.c_str(), LastError);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ULARGE_INTEGER size;
|
||||||
|
size.HighPart = attr.nFileSizeHigh;
|
||||||
|
size.LowPart = attr.nFileSizeLow;
|
||||||
|
return size.QuadPart;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||||
|
Vector<std::string> *V, bool TopDir) {
|
||||||
|
|
||||||
|
auto E = GetEpoch(Dir);
|
||||||
|
if (Epoch)
|
||||||
|
if (E && *Epoch >= E) return;
|
||||||
|
|
||||||
|
std::string Path(Dir);
|
||||||
|
assert(!Path.empty());
|
||||||
|
if (Path.back() != '\\') Path.push_back('\\');
|
||||||
|
Path.push_back('*');
|
||||||
|
|
||||||
|
// Get the first directory entry.
|
||||||
|
WIN32_FIND_DATAA FindInfo;
|
||||||
|
HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
|
||||||
|
if (FindHandle == INVALID_HANDLE_VALUE) {
|
||||||
|
|
||||||
|
if (GetLastError() == ERROR_FILE_NOT_FOUND) return;
|
||||||
|
Printf("No such file or directory: %s; exiting\n", Dir.c_str());
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
|
||||||
|
|
||||||
|
if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
|
|
||||||
|
size_t FilenameLen = strlen(FindInfo.cFileName);
|
||||||
|
if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') ||
|
||||||
|
(FilenameLen == 2 && FindInfo.cFileName[0] == '.' &&
|
||||||
|
FindInfo.cFileName[1] == '.'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ListFilesInDirRecursive(FileName, Epoch, V, false);
|
||||||
|
|
||||||
|
} else if (IsFile(FileName, FindInfo.dwFileAttributes))
|
||||||
|
|
||||||
|
V->push_back(FileName);
|
||||||
|
|
||||||
|
} while (FindNextFileA(FindHandle, &FindInfo));
|
||||||
|
|
||||||
|
DWORD LastError = GetLastError();
|
||||||
|
if (LastError != ERROR_NO_MORE_FILES)
|
||||||
|
Printf("FindNextFileA failed (Error code: %lu).\n", LastError);
|
||||||
|
|
||||||
|
FindClose(FindHandle);
|
||||||
|
|
||||||
|
if (Epoch && TopDir) *Epoch = E;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void IterateDirRecursive(const std::string &Dir,
|
||||||
|
void (*DirPreCallback)(const std::string &Dir),
|
||||||
|
void (*DirPostCallback)(const std::string &Dir),
|
||||||
|
void (*FileCallback)(const std::string &Dir)) {
|
||||||
|
|
||||||
|
// TODO(metzman): Implement ListFilesInDirRecursive via this function.
|
||||||
|
DirPreCallback(Dir);
|
||||||
|
|
||||||
|
DWORD DirAttrs = GetFileAttributesA(Dir.c_str());
|
||||||
|
if (!IsDir(DirAttrs)) return;
|
||||||
|
|
||||||
|
std::string TargetDir(Dir);
|
||||||
|
assert(!TargetDir.empty());
|
||||||
|
if (TargetDir.back() != '\\') TargetDir.push_back('\\');
|
||||||
|
TargetDir.push_back('*');
|
||||||
|
|
||||||
|
WIN32_FIND_DATAA FindInfo;
|
||||||
|
// Find the directory's first file.
|
||||||
|
HANDLE FindHandle = FindFirstFileA(TargetDir.c_str(), &FindInfo);
|
||||||
|
if (FindHandle == INVALID_HANDLE_VALUE) {
|
||||||
|
|
||||||
|
DWORD LastError = GetLastError();
|
||||||
|
if (LastError != ERROR_FILE_NOT_FOUND) {
|
||||||
|
|
||||||
|
// If the directory isn't empty, then something abnormal is going on.
|
||||||
|
Printf("FindFirstFileA failed for %s (Error code: %lu).\n", Dir.c_str(),
|
||||||
|
LastError);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
std::string Path = DirPlusFile(Dir, FindInfo.cFileName);
|
||||||
|
DWORD PathAttrs = FindInfo.dwFileAttributes;
|
||||||
|
if (IsDir(PathAttrs)) {
|
||||||
|
|
||||||
|
// Is Path the current directory (".") or the parent ("..")?
|
||||||
|
if (strcmp(FindInfo.cFileName, ".") == 0 ||
|
||||||
|
strcmp(FindInfo.cFileName, "..") == 0)
|
||||||
|
continue;
|
||||||
|
IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback);
|
||||||
|
|
||||||
|
} else if (PathAttrs != INVALID_FILE_ATTRIBUTES) {
|
||||||
|
|
||||||
|
FileCallback(Path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (FindNextFileA(FindHandle, &FindInfo));
|
||||||
|
|
||||||
|
DWORD LastError = GetLastError();
|
||||||
|
if (LastError != ERROR_NO_MORE_FILES)
|
||||||
|
Printf("FindNextFileA failed for %s (Error code: %lu).\n", Dir.c_str(),
|
||||||
|
LastError);
|
||||||
|
|
||||||
|
FindClose(FindHandle);
|
||||||
|
DirPostCallback(Dir);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
char GetSeparator() {
|
||||||
|
|
||||||
|
return '\\';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *OpenFile(int Fd, const char *Mode) {
|
||||||
|
|
||||||
|
return _fdopen(Fd, Mode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int CloseFile(int Fd) {
|
||||||
|
|
||||||
|
return _close(Fd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int DuplicateFile(int Fd) {
|
||||||
|
|
||||||
|
return _dup(Fd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveFile(const std::string &Path) {
|
||||||
|
|
||||||
|
_unlink(Path.c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenameFile(const std::string &OldPath, const std::string &NewPath) {
|
||||||
|
|
||||||
|
rename(OldPath.c_str(), NewPath.c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
intptr_t GetHandleFromFd(int fd) {
|
||||||
|
|
||||||
|
return _get_osfhandle(fd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSeparator(char C) {
|
||||||
|
|
||||||
|
return C == '\\' || C == '/';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
|
||||||
|
// Returns number of characters considered if successful.
|
||||||
|
static size_t ParseDrive(const std::string &FileName, const size_t Offset,
|
||||||
|
bool Relative = true) {
|
||||||
|
|
||||||
|
if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':') return 0;
|
||||||
|
if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
|
||||||
|
|
||||||
|
if (!Relative) // Accept relative path?
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a file name, like: SomeFile.txt
|
||||||
|
// Returns number of characters considered if successful.
|
||||||
|
static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
|
||||||
|
|
||||||
|
size_t Pos = Offset;
|
||||||
|
const size_t End = FileName.size();
|
||||||
|
for (; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
|
||||||
|
;
|
||||||
|
return Pos - Offset;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a directory ending in separator, like: `SomeDir\`
|
||||||
|
// Returns number of characters considered if successful.
|
||||||
|
static size_t ParseDir(const std::string &FileName, const size_t Offset) {
|
||||||
|
|
||||||
|
size_t Pos = Offset;
|
||||||
|
const size_t End = FileName.size();
|
||||||
|
if (Pos >= End || IsSeparator(FileName[Pos])) return 0;
|
||||||
|
for (; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
|
||||||
|
;
|
||||||
|
if (Pos >= End) return 0;
|
||||||
|
++Pos; // Include separator.
|
||||||
|
return Pos - Offset;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a servername and share, like: `SomeServer\SomeShare\`
|
||||||
|
// Returns number of characters considered if successful.
|
||||||
|
static size_t ParseServerAndShare(const std::string &FileName,
|
||||||
|
const size_t Offset) {
|
||||||
|
|
||||||
|
size_t Pos = Offset, Res;
|
||||||
|
if (!(Res = ParseDir(FileName, Pos))) return 0;
|
||||||
|
Pos += Res;
|
||||||
|
if (!(Res = ParseDir(FileName, Pos))) return 0;
|
||||||
|
Pos += Res;
|
||||||
|
return Pos - Offset;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the given Ref string from the position Offset, to exactly match the
|
||||||
|
// given string Patt. Returns number of characters considered if successful.
|
||||||
|
static size_t ParseCustomString(const std::string &Ref, size_t Offset,
|
||||||
|
const char *Patt) {
|
||||||
|
|
||||||
|
size_t Len = strlen(Patt);
|
||||||
|
if (Offset + Len > Ref.size()) return 0;
|
||||||
|
return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a location, like:
|
||||||
|
// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C:
|
||||||
|
// Returns number of characters considered if successful.
|
||||||
|
static size_t ParseLocation(const std::string &FileName) {
|
||||||
|
|
||||||
|
size_t Pos = 0, Res;
|
||||||
|
|
||||||
|
if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) {
|
||||||
|
|
||||||
|
Pos += Res;
|
||||||
|
if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) {
|
||||||
|
|
||||||
|
Pos += Res;
|
||||||
|
if ((Res = ParseServerAndShare(FileName, Pos))) return Pos + Res;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Res = ParseDrive(FileName, Pos, false))) return Pos + Res;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
|
||||||
|
|
||||||
|
++Pos;
|
||||||
|
if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
|
||||||
|
|
||||||
|
++Pos;
|
||||||
|
if ((Res = ParseServerAndShare(FileName, Pos))) return Pos + Res;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pos;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Res = ParseDrive(FileName, Pos))) return Pos + Res;
|
||||||
|
|
||||||
|
return Pos;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DirName(const std::string &FileName) {
|
||||||
|
|
||||||
|
size_t LocationLen = ParseLocation(FileName);
|
||||||
|
size_t DirLen = 0, Res;
|
||||||
|
while ((Res = ParseDir(FileName, LocationLen + DirLen)))
|
||||||
|
DirLen += Res;
|
||||||
|
size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
|
||||||
|
|
||||||
|
if (LocationLen + DirLen + FileLen != FileName.size()) {
|
||||||
|
|
||||||
|
Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str());
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DirLen) {
|
||||||
|
|
||||||
|
--DirLen; // Remove trailing separator.
|
||||||
|
if (!FileLen) { // Path ended in separator.
|
||||||
|
assert(DirLen);
|
||||||
|
// Remove file name from Dir.
|
||||||
|
while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
|
||||||
|
--DirLen;
|
||||||
|
if (DirLen) // Remove trailing separator.
|
||||||
|
--DirLen;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LocationLen) { // Relative path.
|
||||||
|
if (!DirLen) return ".";
|
||||||
|
return std::string(".\\").append(FileName, 0, DirLen);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileName.substr(0, LocationLen + DirLen);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TmpDir() {
|
||||||
|
|
||||||
|
std::string Tmp;
|
||||||
|
Tmp.resize(MAX_PATH + 1);
|
||||||
|
DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]);
|
||||||
|
if (Size == 0) {
|
||||||
|
|
||||||
|
Printf("Couldn't get Tmp path.\n");
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Tmp.resize(Size);
|
||||||
|
return Tmp;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInterestingCoverageFile(const std::string &FileName) {
|
||||||
|
|
||||||
|
if (FileName.find("Program Files") != std::string::npos) return false;
|
||||||
|
if (FileName.find("compiler-rt\\lib\\") != std::string::npos)
|
||||||
|
return false; // sanitizer internal.
|
||||||
|
if (FileName == "<null>") return false;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RawPrint(const char *Str) {
|
||||||
|
|
||||||
|
_write(2, Str, strlen(Str));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MkDir(const std::string &Path) {
|
||||||
|
|
||||||
|
if (CreateDirectoryA(Path.c_str(), nullptr)) return;
|
||||||
|
Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(),
|
||||||
|
GetLastError());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RmDir(const std::string &Path) {
|
||||||
|
|
||||||
|
if (RemoveDirectoryA(Path.c_str())) return;
|
||||||
|
Printf("RemoveDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(),
|
||||||
|
GetLastError());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &getDevNull() {
|
||||||
|
|
||||||
|
static const std::string devNull = "NUL";
|
||||||
|
return devNull;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LIBFUZZER_WINDOWS
|
||||||
|
|
79
custom_mutators/libfuzzer/FuzzerInterface.h
Normal file
79
custom_mutators/libfuzzer/FuzzerInterface.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Define the interface between libFuzzer and the library being tested.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// NOTE: the libFuzzer interface is thin and in the majority of cases
|
||||||
|
// you should not include this file into your target. In 95% of cases
|
||||||
|
// all you need is to define the following function in your file:
|
||||||
|
// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||||
|
|
||||||
|
// WARNING: keep the interface in C.
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_INTERFACE_H
|
||||||
|
#define LLVM_FUZZER_INTERFACE_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
// Define FUZZER_INTERFACE_VISIBILITY to set default visibility in a way that
|
||||||
|
// doesn't break MSVC.
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define FUZZER_INTERFACE_VISIBILITY __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define FUZZER_INTERFACE_VISIBILITY __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Mandatory user-provided target function.
|
||||||
|
// Executes the code under test with [Data, Data+Size) as the input.
|
||||||
|
// libFuzzer will invoke this function *many* times with different inputs.
|
||||||
|
// Must return 0.
|
||||||
|
FUZZER_INTERFACE_VISIBILITY int
|
||||||
|
LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
|
||||||
|
|
||||||
|
// Optional user-provided initialization function.
|
||||||
|
// If provided, this function will be called by libFuzzer once at startup.
|
||||||
|
// It may read and modify argc/argv.
|
||||||
|
// Must return 0.
|
||||||
|
FUZZER_INTERFACE_VISIBILITY int LLVMFuzzerInitialize(int *argc, char ***argv);
|
||||||
|
|
||||||
|
// Optional user-provided custom mutator.
|
||||||
|
// Mutates raw data in [Data, Data+Size) inplace.
|
||||||
|
// Returns the new size, which is not greater than MaxSize.
|
||||||
|
// Given the same Seed produces the same mutation.
|
||||||
|
FUZZER_INTERFACE_VISIBILITY size_t
|
||||||
|
LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||||
|
unsigned int Seed);
|
||||||
|
|
||||||
|
// Optional user-provided custom cross-over function.
|
||||||
|
// Combines pieces of Data1 & Data2 together into Out.
|
||||||
|
// Returns the new size, which is not greater than MaxOutSize.
|
||||||
|
// Should produce the same mutation given the same Seed.
|
||||||
|
FUZZER_INTERFACE_VISIBILITY size_t
|
||||||
|
LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
|
||||||
|
const uint8_t *Data2, size_t Size2, uint8_t *Out,
|
||||||
|
size_t MaxOutSize, unsigned int Seed);
|
||||||
|
|
||||||
|
// Experimental, may go away in future.
|
||||||
|
// libFuzzer-provided function to be used inside LLVMFuzzerCustomMutator.
|
||||||
|
// Mutates raw data in [Data, Data+Size) inplace.
|
||||||
|
// Returns the new size, which is not greater than MaxSize.
|
||||||
|
FUZZER_INTERFACE_VISIBILITY size_t
|
||||||
|
LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
|
||||||
|
#undef FUZZER_INTERFACE_VISIBILITY
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_INTERFACE_H
|
173
custom_mutators/libfuzzer/FuzzerInternal.h
Normal file
173
custom_mutators/libfuzzer/FuzzerInternal.h
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Define the main class fuzzer::Fuzzer and most functions.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_INTERNAL_H
|
||||||
|
#define LLVM_FUZZER_INTERNAL_H
|
||||||
|
|
||||||
|
#include "FuzzerDataFlowTrace.h"
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerExtFunctions.h"
|
||||||
|
#include "FuzzerInterface.h"
|
||||||
|
#include "FuzzerOptions.h"
|
||||||
|
#include "FuzzerSHA1.h"
|
||||||
|
#include "FuzzerValueBitMap.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
class Fuzzer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
|
||||||
|
FuzzingOptions Options);
|
||||||
|
~Fuzzer();
|
||||||
|
void Loop(Vector<SizedFile> &CorporaFiles);
|
||||||
|
void ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles);
|
||||||
|
void MinimizeCrashLoop(const Unit &U);
|
||||||
|
void RereadOutputCorpus(size_t MaxSize);
|
||||||
|
|
||||||
|
size_t secondsSinceProcessStartUp() {
|
||||||
|
return duration_cast<seconds>(system_clock::now() - ProcessStartTime)
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TimedOut() {
|
||||||
|
return Options.MaxTotalTimeSec > 0 &&
|
||||||
|
secondsSinceProcessStartUp() >
|
||||||
|
static_cast<size_t>(Options.MaxTotalTimeSec);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t execPerSec() {
|
||||||
|
size_t Seconds = secondsSinceProcessStartUp();
|
||||||
|
return Seconds ? TotalNumberOfRuns / Seconds : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; }
|
||||||
|
|
||||||
|
static void StaticAlarmCallback();
|
||||||
|
static void StaticCrashSignalCallback();
|
||||||
|
static void StaticExitCallback();
|
||||||
|
static void StaticInterruptCallback();
|
||||||
|
static void StaticFileSizeExceedCallback();
|
||||||
|
static void StaticGracefulExitCallback();
|
||||||
|
|
||||||
|
void ExecuteCallback(const uint8_t *Data, size_t Size);
|
||||||
|
bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
|
||||||
|
InputInfo *II = nullptr, bool ForceAddToCorpus = false,
|
||||||
|
bool *FoundUniqFeatures = nullptr);
|
||||||
|
|
||||||
|
// Merge Corpora[1:] into Corpora[0].
|
||||||
|
void Merge(const Vector<std::string> &Corpora);
|
||||||
|
void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
|
||||||
|
MutationDispatcher &GetMD() { return MD; }
|
||||||
|
void PrintFinalStats();
|
||||||
|
void SetMaxInputLen(size_t MaxInputLen);
|
||||||
|
void SetMaxMutationLen(size_t MaxMutationLen);
|
||||||
|
void RssLimitCallback();
|
||||||
|
|
||||||
|
bool InFuzzingThread() const { return IsMyThread; }
|
||||||
|
size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const;
|
||||||
|
void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
|
||||||
|
bool DuringInitialCorpusExecution);
|
||||||
|
|
||||||
|
void HandleMalloc(size_t Size);
|
||||||
|
static void MaybeExitGracefully();
|
||||||
|
std::string WriteToOutputCorpus(const Unit &U);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AlarmCallback();
|
||||||
|
void CrashCallback();
|
||||||
|
void ExitCallback();
|
||||||
|
void CrashOnOverwrittenData();
|
||||||
|
void InterruptCallback();
|
||||||
|
void MutateAndTestOne();
|
||||||
|
void PurgeAllocator();
|
||||||
|
void ReportNewCoverage(InputInfo *II, const Unit &U);
|
||||||
|
void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size);
|
||||||
|
void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix);
|
||||||
|
void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0,
|
||||||
|
size_t Features = 0);
|
||||||
|
void PrintStatusForNewUnit(const Unit &U, const char *Text);
|
||||||
|
void CheckExitOnSrcPosOrItem();
|
||||||
|
|
||||||
|
static void StaticDeathCallback();
|
||||||
|
void DumpCurrentUnit(const char *Prefix);
|
||||||
|
void DeathCallback();
|
||||||
|
|
||||||
|
void AllocateCurrentUnitData();
|
||||||
|
uint8_t *CurrentUnitData = nullptr;
|
||||||
|
std::atomic<size_t> CurrentUnitSize;
|
||||||
|
uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit.
|
||||||
|
|
||||||
|
bool GracefulExitRequested = false;
|
||||||
|
|
||||||
|
size_t TotalNumberOfRuns = 0;
|
||||||
|
size_t NumberOfNewUnitsAdded = 0;
|
||||||
|
|
||||||
|
size_t LastCorpusUpdateRun = 0;
|
||||||
|
|
||||||
|
bool HasMoreMallocsThanFrees = false;
|
||||||
|
size_t NumberOfLeakDetectionAttempts = 0;
|
||||||
|
|
||||||
|
system_clock::time_point LastAllocatorPurgeAttemptTime = system_clock::now();
|
||||||
|
|
||||||
|
UserCallback CB;
|
||||||
|
InputCorpus &Corpus;
|
||||||
|
MutationDispatcher &MD;
|
||||||
|
FuzzingOptions Options;
|
||||||
|
DataFlowTrace DFT;
|
||||||
|
|
||||||
|
system_clock::time_point ProcessStartTime = system_clock::now();
|
||||||
|
system_clock::time_point UnitStartTime, UnitStopTime;
|
||||||
|
long TimeOfLongestUnitInSeconds = 0;
|
||||||
|
long EpochOfLastReadOfOutputCorpus = 0;
|
||||||
|
|
||||||
|
size_t MaxInputLen = 0;
|
||||||
|
size_t MaxMutationLen = 0;
|
||||||
|
size_t TmpMaxMutationLen = 0;
|
||||||
|
|
||||||
|
Vector<uint32_t> UniqFeatureSetTmp;
|
||||||
|
|
||||||
|
// Need to know our own thread.
|
||||||
|
static thread_local bool IsMyThread;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScopedEnableMsanInterceptorChecks {
|
||||||
|
ScopedEnableMsanInterceptorChecks() {
|
||||||
|
if (EF->__msan_scoped_enable_interceptor_checks)
|
||||||
|
EF->__msan_scoped_enable_interceptor_checks();
|
||||||
|
}
|
||||||
|
~ScopedEnableMsanInterceptorChecks() {
|
||||||
|
if (EF->__msan_scoped_disable_interceptor_checks)
|
||||||
|
EF->__msan_scoped_disable_interceptor_checks();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScopedDisableMsanInterceptorChecks {
|
||||||
|
ScopedDisableMsanInterceptorChecks() {
|
||||||
|
if (EF->__msan_scoped_disable_interceptor_checks)
|
||||||
|
EF->__msan_scoped_disable_interceptor_checks();
|
||||||
|
}
|
||||||
|
~ScopedDisableMsanInterceptorChecks() {
|
||||||
|
if (EF->__msan_scoped_enable_interceptor_checks)
|
||||||
|
EF->__msan_scoped_enable_interceptor_checks();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_INTERNAL_H
|
1087
custom_mutators/libfuzzer/FuzzerLoop.cpp
Normal file
1087
custom_mutators/libfuzzer/FuzzerLoop.cpp
Normal file
File diff suppressed because it is too large
Load Diff
485
custom_mutators/libfuzzer/FuzzerMerge.cpp
Normal file
485
custom_mutators/libfuzzer/FuzzerMerge.cpp
Normal file
@ -0,0 +1,485 @@
|
|||||||
|
//===- FuzzerMerge.cpp - merging corpora ----------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Merging corpora.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "FuzzerCommand.h"
|
||||||
|
#include "FuzzerMerge.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerInternal.h"
|
||||||
|
#include "FuzzerTracePC.h"
|
||||||
|
#include "FuzzerUtil.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
bool Merger::Parse(const std::string &Str, bool ParseCoverage) {
|
||||||
|
|
||||||
|
std::istringstream SS(Str);
|
||||||
|
return Parse(SS, ParseCoverage);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) {
|
||||||
|
|
||||||
|
if (!Parse(IS, ParseCoverage)) {
|
||||||
|
|
||||||
|
Printf("MERGE: failed to parse the control file (unexpected error)\n");
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// The control file example:
|
||||||
|
//
|
||||||
|
// 3 # The number of inputs
|
||||||
|
// 1 # The number of inputs in the first corpus, <= the previous number
|
||||||
|
// file0
|
||||||
|
// file1
|
||||||
|
// file2 # One file name per line.
|
||||||
|
// STARTED 0 123 # FileID, file size
|
||||||
|
// FT 0 1 4 6 8 # FileID COV1 COV2 ...
|
||||||
|
// COV 0 7 8 9 # FileID COV1 COV1
|
||||||
|
// STARTED 1 456 # If FT is missing, the input crashed while processing.
|
||||||
|
// STARTED 2 567
|
||||||
|
// FT 2 8 9
|
||||||
|
// COV 2 11 12
|
||||||
|
bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
|
||||||
|
|
||||||
|
LastFailure.clear();
|
||||||
|
std::string Line;
|
||||||
|
|
||||||
|
// Parse NumFiles.
|
||||||
|
if (!std::getline(IS, Line, '\n')) return false;
|
||||||
|
std::istringstream L1(Line);
|
||||||
|
size_t NumFiles = 0;
|
||||||
|
L1 >> NumFiles;
|
||||||
|
if (NumFiles == 0 || NumFiles > 10000000) return false;
|
||||||
|
|
||||||
|
// Parse NumFilesInFirstCorpus.
|
||||||
|
if (!std::getline(IS, Line, '\n')) return false;
|
||||||
|
std::istringstream L2(Line);
|
||||||
|
NumFilesInFirstCorpus = NumFiles + 1;
|
||||||
|
L2 >> NumFilesInFirstCorpus;
|
||||||
|
if (NumFilesInFirstCorpus > NumFiles) return false;
|
||||||
|
|
||||||
|
// Parse file names.
|
||||||
|
Files.resize(NumFiles);
|
||||||
|
for (size_t i = 0; i < NumFiles; i++)
|
||||||
|
if (!std::getline(IS, Files[i].Name, '\n')) return false;
|
||||||
|
|
||||||
|
// Parse STARTED, FT, and COV lines.
|
||||||
|
size_t ExpectedStartMarker = 0;
|
||||||
|
const size_t kInvalidStartMarker = -1;
|
||||||
|
size_t LastSeenStartMarker = kInvalidStartMarker;
|
||||||
|
Vector<uint32_t> TmpFeatures;
|
||||||
|
Set<uint32_t> PCs;
|
||||||
|
while (std::getline(IS, Line, '\n')) {
|
||||||
|
|
||||||
|
std::istringstream ISS1(Line);
|
||||||
|
std::string Marker;
|
||||||
|
size_t N;
|
||||||
|
ISS1 >> Marker;
|
||||||
|
ISS1 >> N;
|
||||||
|
if (Marker == "STARTED") {
|
||||||
|
|
||||||
|
// STARTED FILE_ID FILE_SIZE
|
||||||
|
if (ExpectedStartMarker != N) return false;
|
||||||
|
ISS1 >> Files[ExpectedStartMarker].Size;
|
||||||
|
LastSeenStartMarker = ExpectedStartMarker;
|
||||||
|
assert(ExpectedStartMarker < Files.size());
|
||||||
|
ExpectedStartMarker++;
|
||||||
|
|
||||||
|
} else if (Marker == "FT") {
|
||||||
|
|
||||||
|
// FT FILE_ID COV1 COV2 COV3 ...
|
||||||
|
size_t CurrentFileIdx = N;
|
||||||
|
if (CurrentFileIdx != LastSeenStartMarker) return false;
|
||||||
|
LastSeenStartMarker = kInvalidStartMarker;
|
||||||
|
if (ParseCoverage) {
|
||||||
|
|
||||||
|
TmpFeatures.clear(); // use a vector from outer scope to avoid resizes.
|
||||||
|
while (ISS1 >> N)
|
||||||
|
TmpFeatures.push_back(N);
|
||||||
|
std::sort(TmpFeatures.begin(), TmpFeatures.end());
|
||||||
|
Files[CurrentFileIdx].Features = TmpFeatures;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (Marker == "COV") {
|
||||||
|
|
||||||
|
size_t CurrentFileIdx = N;
|
||||||
|
if (ParseCoverage)
|
||||||
|
while (ISS1 >> N)
|
||||||
|
if (PCs.insert(N).second) Files[CurrentFileIdx].Cov.push_back(N);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LastSeenStartMarker != kInvalidStartMarker)
|
||||||
|
LastFailure = Files[LastSeenStartMarker].Name;
|
||||||
|
|
||||||
|
FirstNotProcessedFile = ExpectedStartMarker;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Merger::ApproximateMemoryConsumption() const {
|
||||||
|
|
||||||
|
size_t Res = 0;
|
||||||
|
for (const auto &F : Files)
|
||||||
|
Res += sizeof(F) + F.Features.size() * sizeof(F.Features[0]);
|
||||||
|
return Res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decides which files need to be merged (add those to NewFiles).
|
||||||
|
// Returns the number of new features added.
|
||||||
|
size_t Merger::Merge(const Set<uint32_t> &InitialFeatures,
|
||||||
|
Set<uint32_t> * NewFeatures,
|
||||||
|
const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov,
|
||||||
|
Vector<std::string> *NewFiles) {
|
||||||
|
|
||||||
|
NewFiles->clear();
|
||||||
|
assert(NumFilesInFirstCorpus <= Files.size());
|
||||||
|
Set<uint32_t> AllFeatures = InitialFeatures;
|
||||||
|
|
||||||
|
// What features are in the initial corpus?
|
||||||
|
for (size_t i = 0; i < NumFilesInFirstCorpus; i++) {
|
||||||
|
|
||||||
|
auto &Cur = Files[i].Features;
|
||||||
|
AllFeatures.insert(Cur.begin(), Cur.end());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all features that we already know from all other inputs.
|
||||||
|
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
|
||||||
|
|
||||||
|
auto & Cur = Files[i].Features;
|
||||||
|
Vector<uint32_t> Tmp;
|
||||||
|
std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(),
|
||||||
|
AllFeatures.end(), std::inserter(Tmp, Tmp.begin()));
|
||||||
|
Cur.swap(Tmp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort. Give preference to
|
||||||
|
// * smaller files
|
||||||
|
// * files with more features.
|
||||||
|
std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(),
|
||||||
|
[&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool {
|
||||||
|
|
||||||
|
if (a.Size != b.Size) return a.Size < b.Size;
|
||||||
|
return a.Features.size() > b.Features.size();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// One greedy pass: add the file's features to AllFeatures.
|
||||||
|
// If new features were added, add this file to NewFiles.
|
||||||
|
for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
|
||||||
|
|
||||||
|
auto &Cur = Files[i].Features;
|
||||||
|
// Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(),
|
||||||
|
// Files[i].Size, Cur.size());
|
||||||
|
bool FoundNewFeatures = false;
|
||||||
|
for (auto Fe : Cur) {
|
||||||
|
|
||||||
|
if (AllFeatures.insert(Fe).second) {
|
||||||
|
|
||||||
|
FoundNewFeatures = true;
|
||||||
|
NewFeatures->insert(Fe);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FoundNewFeatures) NewFiles->push_back(Files[i].Name);
|
||||||
|
for (auto Cov : Files[i].Cov)
|
||||||
|
if (InitialCov.find(Cov) == InitialCov.end()) NewCov->insert(Cov);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewFeatures->size();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<uint32_t> Merger::AllFeatures() const {
|
||||||
|
|
||||||
|
Set<uint32_t> S;
|
||||||
|
for (auto &File : Files)
|
||||||
|
S.insert(File.Features.begin(), File.Features.end());
|
||||||
|
return S;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inner process. May crash if the target crashes.
|
||||||
|
void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
|
||||||
|
|
||||||
|
Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str());
|
||||||
|
Merger M;
|
||||||
|
std::ifstream IF(CFPath);
|
||||||
|
M.ParseOrExit(IF, false);
|
||||||
|
IF.close();
|
||||||
|
if (!M.LastFailure.empty())
|
||||||
|
Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n",
|
||||||
|
M.LastFailure.c_str());
|
||||||
|
|
||||||
|
Printf(
|
||||||
|
"MERGE-INNER: %zd total files;"
|
||||||
|
" %zd processed earlier; will process %zd files now\n",
|
||||||
|
M.Files.size(), M.FirstNotProcessedFile,
|
||||||
|
M.Files.size() - M.FirstNotProcessedFile);
|
||||||
|
|
||||||
|
std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app);
|
||||||
|
Set<size_t> AllFeatures;
|
||||||
|
auto PrintStatsWrapper = [this, &AllFeatures](const char *Where) {
|
||||||
|
|
||||||
|
this->PrintStats(Where, "\n", 0, AllFeatures.size());
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Set<const TracePC::PCTableEntry *> AllPCs;
|
||||||
|
for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
|
||||||
|
|
||||||
|
Fuzzer::MaybeExitGracefully();
|
||||||
|
auto U = FileToVector(M.Files[i].Name);
|
||||||
|
if (U.size() > MaxInputLen) {
|
||||||
|
|
||||||
|
U.resize(MaxInputLen);
|
||||||
|
U.shrink_to_fit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the pre-run marker.
|
||||||
|
OF << "STARTED " << i << " " << U.size() << "\n";
|
||||||
|
OF.flush(); // Flush is important since Command::Execute may crash.
|
||||||
|
// Run.
|
||||||
|
TPC.ResetMaps();
|
||||||
|
ExecuteCallback(U.data(), U.size());
|
||||||
|
// Collect coverage. We are iterating over the files in this order:
|
||||||
|
// * First, files in the initial corpus ordered by size, smallest first.
|
||||||
|
// * Then, all other files, smallest first.
|
||||||
|
// So it makes no sense to record all features for all files, instead we
|
||||||
|
// only record features that were not seen before.
|
||||||
|
Set<size_t> UniqFeatures;
|
||||||
|
TPC.CollectFeatures([&](size_t Feature) {
|
||||||
|
|
||||||
|
if (AllFeatures.insert(Feature).second) UniqFeatures.insert(Feature);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
TPC.UpdateObservedPCs();
|
||||||
|
// Show stats.
|
||||||
|
if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)))
|
||||||
|
PrintStatsWrapper("pulse ");
|
||||||
|
if (TotalNumberOfRuns == M.NumFilesInFirstCorpus)
|
||||||
|
PrintStatsWrapper("LOADED");
|
||||||
|
// Write the post-run marker and the coverage.
|
||||||
|
OF << "FT " << i;
|
||||||
|
for (size_t F : UniqFeatures)
|
||||||
|
OF << " " << F;
|
||||||
|
OF << "\n";
|
||||||
|
OF << "COV " << i;
|
||||||
|
TPC.ForEachObservedPC([&](const TracePC::PCTableEntry *TE) {
|
||||||
|
|
||||||
|
if (AllPCs.insert(TE).second) OF << " " << TPC.PCTableEntryIdx(TE);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
OF << "\n";
|
||||||
|
OF.flush();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintStatsWrapper("DONE ");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t WriteNewControlFile(const std::string & CFPath,
|
||||||
|
const Vector<SizedFile> & OldCorpus,
|
||||||
|
const Vector<SizedFile> & NewCorpus,
|
||||||
|
const Vector<MergeFileInfo> &KnownFiles) {
|
||||||
|
|
||||||
|
std::unordered_set<std::string> FilesToSkip;
|
||||||
|
for (auto &SF : KnownFiles)
|
||||||
|
FilesToSkip.insert(SF.Name);
|
||||||
|
|
||||||
|
Vector<std::string> FilesToUse;
|
||||||
|
auto MaybeUseFile = [=, &FilesToUse](std::string Name) {
|
||||||
|
|
||||||
|
if (FilesToSkip.find(Name) == FilesToSkip.end()) FilesToUse.push_back(Name);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto &SF : OldCorpus)
|
||||||
|
MaybeUseFile(SF.File);
|
||||||
|
auto FilesToUseFromOldCorpus = FilesToUse.size();
|
||||||
|
for (auto &SF : NewCorpus)
|
||||||
|
MaybeUseFile(SF.File);
|
||||||
|
|
||||||
|
RemoveFile(CFPath);
|
||||||
|
std::ofstream ControlFile(CFPath);
|
||||||
|
ControlFile << FilesToUse.size() << "\n";
|
||||||
|
ControlFile << FilesToUseFromOldCorpus << "\n";
|
||||||
|
for (auto &FN : FilesToUse)
|
||||||
|
ControlFile << FN << "\n";
|
||||||
|
|
||||||
|
if (!ControlFile) {
|
||||||
|
|
||||||
|
Printf("MERGE-OUTER: failed to write to the control file: %s\n",
|
||||||
|
CFPath.c_str());
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return FilesToUse.size();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outer process. Does not call the target code and thus should not fail.
|
||||||
|
void CrashResistantMerge(const Vector<std::string> &Args,
|
||||||
|
const Vector<SizedFile> & OldCorpus,
|
||||||
|
const Vector<SizedFile> & NewCorpus,
|
||||||
|
Vector<std::string> * NewFiles,
|
||||||
|
const Set<uint32_t> & InitialFeatures,
|
||||||
|
Set<uint32_t> * NewFeatures,
|
||||||
|
const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov,
|
||||||
|
const std::string &CFPath, bool V /*Verbose*/) {
|
||||||
|
|
||||||
|
if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge.
|
||||||
|
size_t NumAttempts = 0;
|
||||||
|
Vector<MergeFileInfo> KnownFiles;
|
||||||
|
if (FileSize(CFPath)) {
|
||||||
|
|
||||||
|
VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n",
|
||||||
|
CFPath.c_str());
|
||||||
|
Merger M;
|
||||||
|
std::ifstream IF(CFPath);
|
||||||
|
if (M.Parse(IF, /*ParseCoverage=*/true)) {
|
||||||
|
|
||||||
|
VPrintf(V,
|
||||||
|
"MERGE-OUTER: control file ok, %zd files total,"
|
||||||
|
" first not processed file %zd\n",
|
||||||
|
M.Files.size(), M.FirstNotProcessedFile);
|
||||||
|
if (!M.LastFailure.empty())
|
||||||
|
VPrintf(V,
|
||||||
|
"MERGE-OUTER: '%s' will be skipped as unlucky "
|
||||||
|
"(merge has stumbled on it the last time)\n",
|
||||||
|
M.LastFailure.c_str());
|
||||||
|
if (M.FirstNotProcessedFile >= M.Files.size()) {
|
||||||
|
|
||||||
|
// Merge has already been completed with the given merge control file.
|
||||||
|
if (M.Files.size() == OldCorpus.size() + NewCorpus.size()) {
|
||||||
|
|
||||||
|
VPrintf(
|
||||||
|
V,
|
||||||
|
"MERGE-OUTER: nothing to do, merge has been completed before\n");
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number of input files likely changed, start merge from scratch, but
|
||||||
|
// reuse coverage information from the given merge control file.
|
||||||
|
VPrintf(
|
||||||
|
V,
|
||||||
|
"MERGE-OUTER: starting merge from scratch, but reusing coverage "
|
||||||
|
"information from the given control file\n");
|
||||||
|
KnownFiles = M.Files;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// There is a merge in progress, continue.
|
||||||
|
NumAttempts = M.Files.size() - M.FirstNotProcessedFile;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
VPrintf(V, "MERGE-OUTER: bad control file, will overwrite it\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NumAttempts) {
|
||||||
|
|
||||||
|
// The supplied control file is empty or bad, create a fresh one.
|
||||||
|
VPrintf(V,
|
||||||
|
"MERGE-OUTER: "
|
||||||
|
"%zd files, %zd in the initial corpus, %zd processed earlier\n",
|
||||||
|
OldCorpus.size() + NewCorpus.size(), OldCorpus.size(),
|
||||||
|
KnownFiles.size());
|
||||||
|
NumAttempts = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the inner process until it passes.
|
||||||
|
// Every inner process should execute at least one input.
|
||||||
|
Command BaseCmd(Args);
|
||||||
|
BaseCmd.removeFlag("merge");
|
||||||
|
BaseCmd.removeFlag("fork");
|
||||||
|
BaseCmd.removeFlag("collect_data_flow");
|
||||||
|
for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) {
|
||||||
|
|
||||||
|
Fuzzer::MaybeExitGracefully();
|
||||||
|
VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt);
|
||||||
|
Command Cmd(BaseCmd);
|
||||||
|
Cmd.addFlag("merge_control_file", CFPath);
|
||||||
|
Cmd.addFlag("merge_inner", "1");
|
||||||
|
if (!V) {
|
||||||
|
|
||||||
|
Cmd.setOutputFile(getDevNull());
|
||||||
|
Cmd.combineOutAndErr();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ExitCode = ExecuteCommand(Cmd);
|
||||||
|
if (!ExitCode) {
|
||||||
|
|
||||||
|
VPrintf(V, "MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the control file and do the merge.
|
||||||
|
Merger M;
|
||||||
|
std::ifstream IF(CFPath);
|
||||||
|
IF.seekg(0, IF.end);
|
||||||
|
VPrintf(V, "MERGE-OUTER: the control file has %zd bytes\n",
|
||||||
|
(size_t)IF.tellg());
|
||||||
|
IF.seekg(0, IF.beg);
|
||||||
|
M.ParseOrExit(IF, true);
|
||||||
|
IF.close();
|
||||||
|
VPrintf(V,
|
||||||
|
"MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n",
|
||||||
|
M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb());
|
||||||
|
|
||||||
|
M.Files.insert(M.Files.end(), KnownFiles.begin(), KnownFiles.end());
|
||||||
|
M.Merge(InitialFeatures, NewFeatures, InitialCov, NewCov, NewFiles);
|
||||||
|
VPrintf(V,
|
||||||
|
"MERGE-OUTER: %zd new files with %zd new features added; "
|
||||||
|
"%zd new coverage edges\n",
|
||||||
|
NewFiles->size(), NewFeatures->size(), NewCov->size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
87
custom_mutators/libfuzzer/FuzzerMerge.h
Normal file
87
custom_mutators/libfuzzer/FuzzerMerge.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Merging Corpora.
|
||||||
|
//
|
||||||
|
// The task:
|
||||||
|
// Take the existing corpus (possibly empty) and merge new inputs into
|
||||||
|
// it so that only inputs with new coverage ('features') are added.
|
||||||
|
// The process should tolerate the crashes, OOMs, leaks, etc.
|
||||||
|
//
|
||||||
|
// Algorithm:
|
||||||
|
// The outer process collects the set of files and writes their names
|
||||||
|
// into a temporary "control" file, then repeatedly launches the inner
|
||||||
|
// process until all inputs are processed.
|
||||||
|
// The outer process does not actually execute the target code.
|
||||||
|
//
|
||||||
|
// The inner process reads the control file and sees a) list of all the inputs
|
||||||
|
// and b) the last processed input. Then it starts processing the inputs one
|
||||||
|
// by one. Before processing every input it writes one line to control file:
|
||||||
|
// STARTED INPUT_ID INPUT_SIZE
|
||||||
|
// After processing an input it writes the following lines:
|
||||||
|
// FT INPUT_ID Feature1 Feature2 Feature3 ...
|
||||||
|
// COV INPUT_ID Coverage1 Coverage2 Coverage3 ...
|
||||||
|
// If a crash happens while processing an input the last line in the control
|
||||||
|
// file will be "STARTED INPUT_ID" and so the next process will know
|
||||||
|
// where to resume.
|
||||||
|
//
|
||||||
|
// Once all inputs are processed by the inner process(es) the outer process
|
||||||
|
// reads the control files and does the merge based entirely on the contents
|
||||||
|
// of control file.
|
||||||
|
// It uses a single pass greedy algorithm choosing first the smallest inputs
|
||||||
|
// within the same size the inputs that have more new features.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_MERGE_H
|
||||||
|
#define LLVM_FUZZER_MERGE_H
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
|
||||||
|
#include <istream>
|
||||||
|
#include <ostream>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
struct MergeFileInfo {
|
||||||
|
std::string Name;
|
||||||
|
size_t Size = 0;
|
||||||
|
Vector<uint32_t> Features, Cov;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Merger {
|
||||||
|
Vector<MergeFileInfo> Files;
|
||||||
|
size_t NumFilesInFirstCorpus = 0;
|
||||||
|
size_t FirstNotProcessedFile = 0;
|
||||||
|
std::string LastFailure;
|
||||||
|
|
||||||
|
bool Parse(std::istream &IS, bool ParseCoverage);
|
||||||
|
bool Parse(const std::string &Str, bool ParseCoverage);
|
||||||
|
void ParseOrExit(std::istream &IS, bool ParseCoverage);
|
||||||
|
size_t Merge(const Set<uint32_t> &InitialFeatures, Set<uint32_t> *NewFeatures,
|
||||||
|
const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov,
|
||||||
|
Vector<std::string> *NewFiles);
|
||||||
|
size_t ApproximateMemoryConsumption() const;
|
||||||
|
Set<uint32_t> AllFeatures() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
void CrashResistantMerge(const Vector<std::string> &Args,
|
||||||
|
const Vector<SizedFile> &OldCorpus,
|
||||||
|
const Vector<SizedFile> &NewCorpus,
|
||||||
|
Vector<std::string> *NewFiles,
|
||||||
|
const Set<uint32_t> &InitialFeatures,
|
||||||
|
Set<uint32_t> *NewFeatures,
|
||||||
|
const Set<uint32_t> &InitialCov,
|
||||||
|
Set<uint32_t> *NewCov,
|
||||||
|
const std::string &CFPath,
|
||||||
|
bool Verbose);
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_MERGE_H
|
720
custom_mutators/libfuzzer/FuzzerMutate.cpp
Normal file
720
custom_mutators/libfuzzer/FuzzerMutate.cpp
Normal file
@ -0,0 +1,720 @@
|
|||||||
|
//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Mutate a test input.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerExtFunctions.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerMutate.h"
|
||||||
|
#include "FuzzerOptions.h"
|
||||||
|
#include "FuzzerTracePC.h"
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
const size_t Dictionary::kMaxDictSize;
|
||||||
|
|
||||||
|
static void PrintASCII(const Word &W, const char *PrintAfter) {
|
||||||
|
|
||||||
|
PrintASCII(W.data(), W.size(), PrintAfter);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MutationDispatcher::MutationDispatcher(Random & Rand,
|
||||||
|
const FuzzingOptions &Options)
|
||||||
|
: Rand(Rand), Options(Options) {
|
||||||
|
|
||||||
|
DefaultMutators.insert(
|
||||||
|
DefaultMutators.begin(),
|
||||||
|
{
|
||||||
|
|
||||||
|
{&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
|
||||||
|
{&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
|
||||||
|
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
|
||||||
|
"InsertRepeatedBytes"},
|
||||||
|
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
|
||||||
|
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
|
||||||
|
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
|
||||||
|
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
|
||||||
|
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
|
||||||
|
{&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
|
||||||
|
{&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
|
||||||
|
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
|
||||||
|
"ManualDict"},
|
||||||
|
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
|
||||||
|
"PersAutoDict"},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Options.UseCmp)
|
||||||
|
DefaultMutators.push_back(
|
||||||
|
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
|
||||||
|
|
||||||
|
if (EF->LLVMFuzzerCustomMutator)
|
||||||
|
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
|
||||||
|
else
|
||||||
|
Mutators = DefaultMutators;
|
||||||
|
|
||||||
|
if (EF->LLVMFuzzerCustomCrossOver)
|
||||||
|
Mutators.push_back(
|
||||||
|
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static char RandCh(Random &Rand) {
|
||||||
|
|
||||||
|
if (Rand.RandBool()) return Rand(256);
|
||||||
|
const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
|
||||||
|
return Special[Rand(sizeof(Special) - 1)];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size == 0) return 0;
|
||||||
|
if (!CrossOverWith) return 0;
|
||||||
|
const Unit &Other = *CrossOverWith;
|
||||||
|
if (Other.empty()) return 0;
|
||||||
|
CustomCrossOverInPlaceHere.resize(MaxSize);
|
||||||
|
auto & U = CustomCrossOverInPlaceHere;
|
||||||
|
size_t NewSize = EF->LLVMFuzzerCustomCrossOver(
|
||||||
|
Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand());
|
||||||
|
if (!NewSize) return 0;
|
||||||
|
assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit");
|
||||||
|
memcpy(Data, U.data(), NewSize);
|
||||||
|
return NewSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size > MaxSize || Size == 0) return 0;
|
||||||
|
size_t ShuffleAmount =
|
||||||
|
Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size.
|
||||||
|
size_t ShuffleStart = Rand(Size - ShuffleAmount);
|
||||||
|
assert(ShuffleStart + ShuffleAmount <= Size);
|
||||||
|
std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand);
|
||||||
|
return Size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size <= 1) return 0;
|
||||||
|
size_t N = Rand(Size / 2) + 1;
|
||||||
|
assert(N < Size);
|
||||||
|
size_t Idx = Rand(Size - N + 1);
|
||||||
|
// Erase Data[Idx:Idx+N].
|
||||||
|
memmove(Data + Idx, Data + Idx + N, Size - Idx - N);
|
||||||
|
// Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx);
|
||||||
|
return Size - N;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size >= MaxSize) return 0;
|
||||||
|
size_t Idx = Rand(Size + 1);
|
||||||
|
// Insert new value at Data[Idx].
|
||||||
|
memmove(Data + Idx + 1, Data + Idx, Size - Idx);
|
||||||
|
Data[Idx] = RandCh(Rand);
|
||||||
|
return Size + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data,
|
||||||
|
size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
const size_t kMinBytesToInsert = 3;
|
||||||
|
if (Size + kMinBytesToInsert >= MaxSize) return 0;
|
||||||
|
size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128);
|
||||||
|
size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert;
|
||||||
|
assert(Size + N <= MaxSize && N);
|
||||||
|
size_t Idx = Rand(Size + 1);
|
||||||
|
// Insert new values at Data[Idx].
|
||||||
|
memmove(Data + Idx + N, Data + Idx, Size - Idx);
|
||||||
|
// Give preference to 0x00 and 0xff.
|
||||||
|
uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255);
|
||||||
|
for (size_t i = 0; i < N; i++)
|
||||||
|
Data[Idx + i] = Byte;
|
||||||
|
return Size + N;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size > MaxSize) return 0;
|
||||||
|
size_t Idx = Rand(Size);
|
||||||
|
Data[Idx] = RandCh(Rand);
|
||||||
|
return Size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size > MaxSize) return 0;
|
||||||
|
size_t Idx = Rand(Size);
|
||||||
|
Data[Idx] ^= 1 << Rand(8);
|
||||||
|
return Size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data,
|
||||||
|
size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize,
|
||||||
|
DictionaryEntry &DE) {
|
||||||
|
|
||||||
|
const Word &W = DE.GetW();
|
||||||
|
bool UsePositionHint = DE.HasPositionHint() &&
|
||||||
|
DE.GetPositionHint() + W.size() < Size &&
|
||||||
|
Rand.RandBool();
|
||||||
|
if (Rand.RandBool()) { // Insert W.
|
||||||
|
if (Size + W.size() > MaxSize) return 0;
|
||||||
|
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1);
|
||||||
|
memmove(Data + Idx + W.size(), Data + Idx, Size - Idx);
|
||||||
|
memcpy(Data + Idx, W.data(), W.size());
|
||||||
|
Size += W.size();
|
||||||
|
|
||||||
|
} else { // Overwrite some bytes with W.
|
||||||
|
|
||||||
|
if (W.size() > Size) return 0;
|
||||||
|
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size());
|
||||||
|
memcpy(Data + Idx, W.data(), W.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Somewhere in the past we have observed a comparison instructions
|
||||||
|
// with arguments Arg1 Arg2. This function tries to guess a dictionary
|
||||||
|
// entry that will satisfy that comparison.
|
||||||
|
// It first tries to find one of the arguments (possibly swapped) in the
|
||||||
|
// input and if it succeeds it creates a DE with a position hint.
|
||||||
|
// Otherwise it creates a DE with one of the arguments w/o a position hint.
|
||||||
|
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||||
|
const void *Arg1, const void *Arg2, const void *Arg1Mutation,
|
||||||
|
const void *Arg2Mutation, size_t ArgSize, const uint8_t *Data,
|
||||||
|
size_t Size) {
|
||||||
|
|
||||||
|
bool HandleFirst = Rand.RandBool();
|
||||||
|
const void * ExistingBytes, *DesiredBytes;
|
||||||
|
Word W;
|
||||||
|
const uint8_t *End = Data + Size;
|
||||||
|
for (int Arg = 0; Arg < 2; Arg++) {
|
||||||
|
|
||||||
|
ExistingBytes = HandleFirst ? Arg1 : Arg2;
|
||||||
|
DesiredBytes = HandleFirst ? Arg2Mutation : Arg1Mutation;
|
||||||
|
HandleFirst = !HandleFirst;
|
||||||
|
W.Set(reinterpret_cast<const uint8_t *>(DesiredBytes), ArgSize);
|
||||||
|
const size_t kMaxNumPositions = 8;
|
||||||
|
size_t Positions[kMaxNumPositions];
|
||||||
|
size_t NumPositions = 0;
|
||||||
|
for (const uint8_t *Cur = Data;
|
||||||
|
Cur < End && NumPositions < kMaxNumPositions; Cur++) {
|
||||||
|
|
||||||
|
Cur =
|
||||||
|
(const uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize);
|
||||||
|
if (!Cur) break;
|
||||||
|
Positions[NumPositions++] = Cur - Data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NumPositions) continue;
|
||||||
|
return DictionaryEntry(W, Positions[Rand(NumPositions)]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DictionaryEntry DE(W);
|
||||||
|
return DE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||||
|
T Arg1, T Arg2, const uint8_t *Data, size_t Size) {
|
||||||
|
|
||||||
|
if (Rand.RandBool()) Arg1 = Bswap(Arg1);
|
||||||
|
if (Rand.RandBool()) Arg2 = Bswap(Arg2);
|
||||||
|
T Arg1Mutation = Arg1 + Rand(-1, 1);
|
||||||
|
T Arg2Mutation = Arg2 + Rand(-1, 1);
|
||||||
|
return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation,
|
||||||
|
sizeof(Arg1), Data, Size);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||||
|
const Word &Arg1, const Word &Arg2, const uint8_t *Data, size_t Size) {
|
||||||
|
|
||||||
|
return MakeDictionaryEntryFromCMP(Arg1.data(), Arg2.data(), Arg1.data(),
|
||||||
|
Arg2.data(), Arg1.size(), Data, Size);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_AddWordFromTORC(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
Word W;
|
||||||
|
DictionaryEntry DE;
|
||||||
|
switch (Rand(4)) {
|
||||||
|
|
||||||
|
case 0: {
|
||||||
|
|
||||||
|
auto X = TPC.TORC8.Get(Rand.Rand());
|
||||||
|
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 1: {
|
||||||
|
|
||||||
|
auto X = TPC.TORC4.Get(Rand.Rand());
|
||||||
|
if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool())
|
||||||
|
DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data,
|
||||||
|
Size);
|
||||||
|
else
|
||||||
|
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 2: {
|
||||||
|
|
||||||
|
auto X = TPC.TORCW.Get(Rand.Rand());
|
||||||
|
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
if (Options.UseMemmem) {
|
||||||
|
|
||||||
|
auto X = TPC.MMT.Get(Rand.Rand());
|
||||||
|
DE = DictionaryEntry(X);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DE.GetW().size()) return 0;
|
||||||
|
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||||
|
if (!Size) return 0;
|
||||||
|
DictionaryEntry &DERef =
|
||||||
|
CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ %
|
||||||
|
kCmpDictionaryEntriesDequeSize];
|
||||||
|
DERef = DE;
|
||||||
|
CurrentDictionaryEntrySequence.push_back(&DERef);
|
||||||
|
return Size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary(
|
||||||
|
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||||
|
|
||||||
|
return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data,
|
||||||
|
size_t Size, size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size > MaxSize) return 0;
|
||||||
|
if (D.empty()) return 0;
|
||||||
|
DictionaryEntry &DE = D[Rand(D.size())];
|
||||||
|
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||||
|
if (!Size) return 0;
|
||||||
|
DE.IncUseCount();
|
||||||
|
CurrentDictionaryEntrySequence.push_back(&DE);
|
||||||
|
return Size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrites part of To[0,ToSize) with a part of From[0,FromSize).
|
||||||
|
// Returns ToSize.
|
||||||
|
size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize,
|
||||||
|
uint8_t *To, size_t ToSize) {
|
||||||
|
|
||||||
|
// Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize).
|
||||||
|
size_t ToBeg = Rand(ToSize);
|
||||||
|
size_t CopySize = Rand(ToSize - ToBeg) + 1;
|
||||||
|
assert(ToBeg + CopySize <= ToSize);
|
||||||
|
CopySize = std::min(CopySize, FromSize);
|
||||||
|
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||||
|
assert(FromBeg + CopySize <= FromSize);
|
||||||
|
memmove(To + ToBeg, From + FromBeg, CopySize);
|
||||||
|
return ToSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inserts part of From[0,ToSize) into To.
|
||||||
|
// Returns new size of To on success or 0 on failure.
|
||||||
|
size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize,
|
||||||
|
uint8_t *To, size_t ToSize,
|
||||||
|
size_t MaxToSize) {
|
||||||
|
|
||||||
|
if (ToSize >= MaxToSize) return 0;
|
||||||
|
size_t AvailableSpace = MaxToSize - ToSize;
|
||||||
|
size_t MaxCopySize = std::min(AvailableSpace, FromSize);
|
||||||
|
size_t CopySize = Rand(MaxCopySize) + 1;
|
||||||
|
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||||
|
assert(FromBeg + CopySize <= FromSize);
|
||||||
|
size_t ToInsertPos = Rand(ToSize + 1);
|
||||||
|
assert(ToInsertPos + CopySize <= MaxToSize);
|
||||||
|
size_t TailSize = ToSize - ToInsertPos;
|
||||||
|
if (To == From) {
|
||||||
|
|
||||||
|
MutateInPlaceHere.resize(MaxToSize);
|
||||||
|
memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize);
|
||||||
|
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||||
|
memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||||
|
memmove(To + ToInsertPos, From + FromBeg, CopySize);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToSize + CopySize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size > MaxSize || Size == 0) return 0;
|
||||||
|
// If Size == MaxSize, `InsertPartOf(...)` will
|
||||||
|
// fail so there's no point using it in this case.
|
||||||
|
if (Size == MaxSize || Rand.RandBool())
|
||||||
|
return CopyPartOf(Data, Size, Data, Size);
|
||||||
|
else
|
||||||
|
return InsertPartOf(Data, Size, Data, Size, MaxSize);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size > MaxSize) return 0;
|
||||||
|
size_t B = Rand(Size);
|
||||||
|
while (B < Size && !isdigit(Data[B]))
|
||||||
|
B++;
|
||||||
|
if (B == Size) return 0;
|
||||||
|
size_t E = B;
|
||||||
|
while (E < Size && isdigit(Data[E]))
|
||||||
|
E++;
|
||||||
|
assert(B < E);
|
||||||
|
// now we have digits in [B, E).
|
||||||
|
// strtol and friends don't accept non-zero-teminated data, parse it manually.
|
||||||
|
uint64_t Val = Data[B] - '0';
|
||||||
|
for (size_t i = B + 1; i < E; i++)
|
||||||
|
Val = Val * 10 + Data[i] - '0';
|
||||||
|
|
||||||
|
// Mutate the integer value.
|
||||||
|
switch (Rand(5)) {
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
Val++;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
Val--;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Val /= 2;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
Val *= 2;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
Val = Rand(Val * Val);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just replace the bytes with the new ones, don't bother moving bytes.
|
||||||
|
for (size_t i = B; i < E; i++) {
|
||||||
|
|
||||||
|
size_t Idx = E + B - i - 1;
|
||||||
|
assert(Idx >= B && Idx < E);
|
||||||
|
Data[Idx] = (Val % 10) + '0';
|
||||||
|
Val /= 10;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) {
|
||||||
|
|
||||||
|
if (Size < sizeof(T)) return 0;
|
||||||
|
size_t Off = Rand(Size - sizeof(T) + 1);
|
||||||
|
assert(Off + sizeof(T) <= Size);
|
||||||
|
T Val;
|
||||||
|
if (Off < 64 && !Rand(4)) {
|
||||||
|
|
||||||
|
Val = Size;
|
||||||
|
if (Rand.RandBool()) Val = Bswap(Val);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
memcpy(&Val, Data + Off, sizeof(Val));
|
||||||
|
T Add = Rand(21);
|
||||||
|
Add -= 10;
|
||||||
|
if (Rand.RandBool())
|
||||||
|
Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes.
|
||||||
|
else
|
||||||
|
Val = Val + Add; // Add assuming current endiannes.
|
||||||
|
if (Add == 0 || Rand.RandBool()) // Maybe negate.
|
||||||
|
Val = -Val;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(Data + Off, &Val, sizeof(Val));
|
||||||
|
return Size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data,
|
||||||
|
size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size > MaxSize) return 0;
|
||||||
|
switch (Rand(4)) {
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return ChangeBinaryInteger<uint64_t>(Data, Size, Rand);
|
||||||
|
case 2:
|
||||||
|
return ChangeBinaryInteger<uint32_t>(Data, Size, Rand);
|
||||||
|
case 1:
|
||||||
|
return ChangeBinaryInteger<uint16_t>(Data, Size, Rand);
|
||||||
|
case 0:
|
||||||
|
return ChangeBinaryInteger<uint8_t>(Data, Size, Rand);
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
if (Size > MaxSize) return 0;
|
||||||
|
if (Size == 0) return 0;
|
||||||
|
if (!CrossOverWith) return 0;
|
||||||
|
const Unit &O = *CrossOverWith;
|
||||||
|
if (O.empty()) return 0;
|
||||||
|
size_t NewSize = 0;
|
||||||
|
switch (Rand(3)) {
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
MutateInPlaceHere.resize(MaxSize);
|
||||||
|
NewSize = CrossOver(Data, Size, O.data(), O.size(),
|
||||||
|
MutateInPlaceHere.data(), MaxSize);
|
||||||
|
memcpy(Data, MutateInPlaceHere.data(), NewSize);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
NewSize = InsertPartOf(O.data(), O.size(), Data, Size, MaxSize);
|
||||||
|
if (!NewSize) NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(NewSize > 0 && "CrossOver returned empty unit");
|
||||||
|
assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
|
||||||
|
return NewSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MutationDispatcher::StartMutationSequence() {
|
||||||
|
|
||||||
|
CurrentMutatorSequence.clear();
|
||||||
|
CurrentDictionaryEntrySequence.clear();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy successful dictionary entries to PersistentAutoDictionary.
|
||||||
|
void MutationDispatcher::RecordSuccessfulMutationSequence() {
|
||||||
|
|
||||||
|
for (auto DE : CurrentDictionaryEntrySequence) {
|
||||||
|
|
||||||
|
// PersistentAutoDictionary.AddWithSuccessCountOne(DE);
|
||||||
|
DE->IncSuccessCount();
|
||||||
|
assert(DE->GetW().size());
|
||||||
|
// Linear search is fine here as this happens seldom.
|
||||||
|
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
|
||||||
|
PersistentAutoDictionary.push_back({DE->GetW(), 1});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MutationDispatcher::PrintRecommendedDictionary() {
|
||||||
|
|
||||||
|
Vector<DictionaryEntry> V;
|
||||||
|
for (auto &DE : PersistentAutoDictionary)
|
||||||
|
if (!ManualDictionary.ContainsWord(DE.GetW())) V.push_back(DE);
|
||||||
|
if (V.empty()) return;
|
||||||
|
Printf("###### Recommended dictionary. ######\n");
|
||||||
|
for (auto &DE : V) {
|
||||||
|
|
||||||
|
assert(DE.GetW().size());
|
||||||
|
Printf("\"");
|
||||||
|
PrintASCII(DE.GetW(), "\"");
|
||||||
|
Printf(" # Uses: %zd\n", DE.GetUseCount());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Printf("###### End of recommended dictionary. ######\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MutationDispatcher::PrintMutationSequence() {
|
||||||
|
|
||||||
|
Printf("MS: %zd ", CurrentMutatorSequence.size());
|
||||||
|
for (auto M : CurrentMutatorSequence)
|
||||||
|
Printf("%s-", M.Name);
|
||||||
|
if (!CurrentDictionaryEntrySequence.empty()) {
|
||||||
|
|
||||||
|
Printf(" DE: ");
|
||||||
|
for (auto DE : CurrentDictionaryEntrySequence) {
|
||||||
|
|
||||||
|
Printf("\"");
|
||||||
|
PrintASCII(DE->GetW(), "\"-");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MutationDispatcher::MutationSequence() {
|
||||||
|
|
||||||
|
std::string MS;
|
||||||
|
for (auto M : CurrentMutatorSequence) {
|
||||||
|
|
||||||
|
MS += M.Name;
|
||||||
|
MS += "-";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return MS;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||||
|
|
||||||
|
return MutateImpl(Data, Size, MaxSize, Mutators);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize) {
|
||||||
|
|
||||||
|
return MutateImpl(Data, Size, MaxSize, DefaultMutators);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutates Data in place, returns new size.
|
||||||
|
size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize,
|
||||||
|
Vector<Mutator> &Mutators) {
|
||||||
|
|
||||||
|
assert(MaxSize > 0);
|
||||||
|
// Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
|
||||||
|
// in which case they will return 0.
|
||||||
|
// Try several times before returning un-mutated data.
|
||||||
|
for (int Iter = 0; Iter < 100; Iter++) {
|
||||||
|
|
||||||
|
auto M = Mutators[Rand(Mutators.size())];
|
||||||
|
size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
|
||||||
|
if (NewSize && NewSize <= MaxSize) {
|
||||||
|
|
||||||
|
if (Options.OnlyASCII) ToASCII(Data, NewSize);
|
||||||
|
CurrentMutatorSequence.push_back(M);
|
||||||
|
return NewSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
*Data = ' ';
|
||||||
|
return 1; // Fallback, should not happen frequently.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask represents the set of Data bytes that are worth mutating.
|
||||||
|
size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize,
|
||||||
|
const Vector<uint8_t> &Mask) {
|
||||||
|
|
||||||
|
size_t MaskedSize = std::min(Size, Mask.size());
|
||||||
|
// * Copy the worthy bytes into a temporary array T
|
||||||
|
// * Mutate T
|
||||||
|
// * Copy T back.
|
||||||
|
// This is totally unoptimized.
|
||||||
|
auto &T = MutateWithMaskTemp;
|
||||||
|
if (T.size() < Size) T.resize(Size);
|
||||||
|
size_t OneBits = 0;
|
||||||
|
for (size_t I = 0; I < MaskedSize; I++)
|
||||||
|
if (Mask[I]) T[OneBits++] = Data[I];
|
||||||
|
|
||||||
|
if (!OneBits) return 0;
|
||||||
|
assert(!T.empty());
|
||||||
|
size_t NewSize = Mutate(T.data(), OneBits, OneBits);
|
||||||
|
assert(NewSize <= OneBits);
|
||||||
|
(void)NewSize;
|
||||||
|
// Even if NewSize < OneBits we still use all OneBits bytes.
|
||||||
|
for (size_t I = 0, J = 0; I < MaskedSize; I++)
|
||||||
|
if (Mask[I]) Data[I] = T[J++];
|
||||||
|
return Size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
|
||||||
|
|
||||||
|
ManualDictionary.push_back({W, std::numeric_limits<size_t>::max()});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
158
custom_mutators/libfuzzer/FuzzerMutate.h
Normal file
158
custom_mutators/libfuzzer/FuzzerMutate.h
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// fuzzer::MutationDispatcher
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_MUTATE_H
|
||||||
|
#define LLVM_FUZZER_MUTATE_H
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerDictionary.h"
|
||||||
|
#include "FuzzerOptions.h"
|
||||||
|
#include "FuzzerRandom.h"
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
class MutationDispatcher {
|
||||||
|
public:
|
||||||
|
MutationDispatcher(Random &Rand, const FuzzingOptions &Options);
|
||||||
|
~MutationDispatcher() {}
|
||||||
|
/// Indicate that we are about to start a new sequence of mutations.
|
||||||
|
void StartMutationSequence();
|
||||||
|
/// Print the current sequence of mutations.
|
||||||
|
void PrintMutationSequence();
|
||||||
|
/// Return the current sequence of mutations.
|
||||||
|
std::string MutationSequence();
|
||||||
|
/// Indicate that the current sequence of mutations was successful.
|
||||||
|
void RecordSuccessfulMutationSequence();
|
||||||
|
/// Mutates data by invoking user-provided mutator.
|
||||||
|
size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
/// Mutates data by invoking user-provided crossover.
|
||||||
|
size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
/// Mutates data by shuffling bytes.
|
||||||
|
size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
/// Mutates data by erasing bytes.
|
||||||
|
size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
/// Mutates data by inserting a byte.
|
||||||
|
size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
/// Mutates data by inserting several repeated bytes.
|
||||||
|
size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
/// Mutates data by chanding one byte.
|
||||||
|
size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
/// Mutates data by chanding one bit.
|
||||||
|
size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
/// Mutates data by copying/inserting a part of data into a different place.
|
||||||
|
size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
|
||||||
|
/// Mutates data by adding a word from the manual dictionary.
|
||||||
|
size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize);
|
||||||
|
|
||||||
|
/// Mutates data by adding a word from the TORC.
|
||||||
|
size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
|
||||||
|
/// Mutates data by adding a word from the persistent automatic dictionary.
|
||||||
|
size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize);
|
||||||
|
|
||||||
|
/// Tries to find an ASCII integer in Data, changes it to another ASCII int.
|
||||||
|
size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
/// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways.
|
||||||
|
size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
|
||||||
|
/// CrossOver Data with CrossOverWith.
|
||||||
|
size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
|
||||||
|
/// Applies one of the configured mutations.
|
||||||
|
/// Returns the new size of data which could be up to MaxSize.
|
||||||
|
size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
|
||||||
|
/// Applies one of the configured mutations to the bytes of Data
|
||||||
|
/// that have '1' in Mask.
|
||||||
|
/// Mask.size() should be >= Size.
|
||||||
|
size_t MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||||
|
const Vector<uint8_t> &Mask);
|
||||||
|
|
||||||
|
/// Applies one of the default mutations. Provided as a service
|
||||||
|
/// to mutation authors.
|
||||||
|
size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
|
||||||
|
/// Creates a cross-over of two pieces of Data, returns its size.
|
||||||
|
size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
|
||||||
|
size_t Size2, uint8_t *Out, size_t MaxOutSize);
|
||||||
|
|
||||||
|
void AddWordToManualDictionary(const Word &W);
|
||||||
|
|
||||||
|
void PrintRecommendedDictionary();
|
||||||
|
|
||||||
|
void SetCrossOverWith(const Unit *U) { CrossOverWith = U; }
|
||||||
|
|
||||||
|
Random &GetRand() { return Rand; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Mutator {
|
||||||
|
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
|
||||||
|
const char *Name;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
|
||||||
|
size_t MaxSize);
|
||||||
|
size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||||
|
Vector<Mutator> &Mutators);
|
||||||
|
|
||||||
|
size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||||
|
size_t ToSize, size_t MaxToSize);
|
||||||
|
size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||||
|
size_t ToSize);
|
||||||
|
size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||||
|
DictionaryEntry &DE);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2,
|
||||||
|
const uint8_t *Data, size_t Size);
|
||||||
|
DictionaryEntry MakeDictionaryEntryFromCMP(const Word &Arg1, const Word &Arg2,
|
||||||
|
const uint8_t *Data, size_t Size);
|
||||||
|
DictionaryEntry MakeDictionaryEntryFromCMP(const void *Arg1, const void *Arg2,
|
||||||
|
const void *Arg1Mutation,
|
||||||
|
const void *Arg2Mutation,
|
||||||
|
size_t ArgSize,
|
||||||
|
const uint8_t *Data, size_t Size);
|
||||||
|
|
||||||
|
Random &Rand;
|
||||||
|
const FuzzingOptions Options;
|
||||||
|
|
||||||
|
// Dictionary provided by the user via -dict=DICT_FILE.
|
||||||
|
Dictionary ManualDictionary;
|
||||||
|
// Temporary dictionary modified by the fuzzer itself,
|
||||||
|
// recreated periodically.
|
||||||
|
Dictionary TempAutoDictionary;
|
||||||
|
// Persistent dictionary modified by the fuzzer, consists of
|
||||||
|
// entries that led to successful discoveries in the past mutations.
|
||||||
|
Dictionary PersistentAutoDictionary;
|
||||||
|
|
||||||
|
Vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
|
||||||
|
|
||||||
|
static const size_t kCmpDictionaryEntriesDequeSize = 16;
|
||||||
|
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
|
||||||
|
size_t CmpDictionaryEntriesDequeIdx = 0;
|
||||||
|
|
||||||
|
const Unit *CrossOverWith = nullptr;
|
||||||
|
Vector<uint8_t> MutateInPlaceHere;
|
||||||
|
Vector<uint8_t> MutateWithMaskTemp;
|
||||||
|
// CustomCrossOver needs its own buffer as a custom implementation may call
|
||||||
|
// LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere.
|
||||||
|
Vector<uint8_t> CustomCrossOverInPlaceHere;
|
||||||
|
|
||||||
|
Vector<Mutator> Mutators;
|
||||||
|
Vector<Mutator> DefaultMutators;
|
||||||
|
Vector<Mutator> CurrentMutatorSequence;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_MUTATE_H
|
90
custom_mutators/libfuzzer/FuzzerOptions.h
Normal file
90
custom_mutators/libfuzzer/FuzzerOptions.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// fuzzer::FuzzingOptions
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_OPTIONS_H
|
||||||
|
#define LLVM_FUZZER_OPTIONS_H
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
struct FuzzingOptions {
|
||||||
|
int Verbosity = 1;
|
||||||
|
size_t MaxLen = 0;
|
||||||
|
size_t LenControl = 1000;
|
||||||
|
bool KeepSeed = false;
|
||||||
|
int UnitTimeoutSec = 300;
|
||||||
|
int TimeoutExitCode = 70;
|
||||||
|
int OOMExitCode = 71;
|
||||||
|
int InterruptExitCode = 72;
|
||||||
|
int ErrorExitCode = 77;
|
||||||
|
bool IgnoreTimeouts = true;
|
||||||
|
bool IgnoreOOMs = true;
|
||||||
|
bool IgnoreCrashes = false;
|
||||||
|
int MaxTotalTimeSec = 0;
|
||||||
|
int RssLimitMb = 0;
|
||||||
|
int MallocLimitMb = 0;
|
||||||
|
bool DoCrossOver = true;
|
||||||
|
bool CrossOverUniformDist = false;
|
||||||
|
int MutateDepth = 5;
|
||||||
|
bool ReduceDepth = false;
|
||||||
|
bool UseCounters = false;
|
||||||
|
bool UseMemmem = true;
|
||||||
|
bool UseCmp = false;
|
||||||
|
int UseValueProfile = false;
|
||||||
|
bool Shrink = false;
|
||||||
|
bool ReduceInputs = false;
|
||||||
|
int ReloadIntervalSec = 1;
|
||||||
|
bool ShuffleAtStartUp = true;
|
||||||
|
bool PreferSmall = true;
|
||||||
|
size_t MaxNumberOfRuns = -1L;
|
||||||
|
int ReportSlowUnits = 10;
|
||||||
|
bool OnlyASCII = false;
|
||||||
|
bool Entropic = false;
|
||||||
|
size_t EntropicFeatureFrequencyThreshold = 0xFF;
|
||||||
|
size_t EntropicNumberOfRarestFeatures = 100;
|
||||||
|
bool EntropicScalePerExecTime = false;
|
||||||
|
std::string OutputCorpus;
|
||||||
|
std::string ArtifactPrefix = "./";
|
||||||
|
std::string ExactArtifactPath;
|
||||||
|
std::string ExitOnSrcPos;
|
||||||
|
std::string ExitOnItem;
|
||||||
|
std::string FocusFunction;
|
||||||
|
std::string DataFlowTrace;
|
||||||
|
std::string CollectDataFlow;
|
||||||
|
std::string FeaturesDir;
|
||||||
|
std::string MutationGraphFile;
|
||||||
|
std::string StopFile;
|
||||||
|
bool SaveArtifacts = true;
|
||||||
|
bool PrintNEW = true; // Print a status line when new units are found;
|
||||||
|
bool PrintNewCovPcs = false;
|
||||||
|
int PrintNewCovFuncs = 0;
|
||||||
|
bool PrintFinalStats = false;
|
||||||
|
bool PrintCorpusStats = false;
|
||||||
|
bool PrintCoverage = false;
|
||||||
|
bool DumpCoverage = false;
|
||||||
|
bool DetectLeaks = true;
|
||||||
|
int PurgeAllocatorIntervalSec = 1;
|
||||||
|
int TraceMalloc = 0;
|
||||||
|
bool HandleAbrt = false;
|
||||||
|
bool HandleAlrm = false;
|
||||||
|
bool HandleBus = false;
|
||||||
|
bool HandleFpe = false;
|
||||||
|
bool HandleIll = false;
|
||||||
|
bool HandleInt = false;
|
||||||
|
bool HandleSegv = false;
|
||||||
|
bool HandleTerm = false;
|
||||||
|
bool HandleXfsz = false;
|
||||||
|
bool HandleUsr1 = false;
|
||||||
|
bool HandleUsr2 = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_OPTIONS_H
|
163
custom_mutators/libfuzzer/FuzzerPlatform.h
Normal file
163
custom_mutators/libfuzzer/FuzzerPlatform.h
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
//===-- FuzzerPlatform.h --------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Common platform macros.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_PLATFORM_H
|
||||||
|
#define LLVM_FUZZER_PLATFORM_H
|
||||||
|
|
||||||
|
// Platform detection.
|
||||||
|
#ifdef __linux__
|
||||||
|
#define LIBFUZZER_APPLE 0
|
||||||
|
#define LIBFUZZER_FUCHSIA 0
|
||||||
|
#define LIBFUZZER_LINUX 1
|
||||||
|
#define LIBFUZZER_NETBSD 0
|
||||||
|
#define LIBFUZZER_FREEBSD 0
|
||||||
|
#define LIBFUZZER_OPENBSD 0
|
||||||
|
#define LIBFUZZER_WINDOWS 0
|
||||||
|
#define LIBFUZZER_EMSCRIPTEN 0
|
||||||
|
#elif __APPLE__
|
||||||
|
#define LIBFUZZER_APPLE 1
|
||||||
|
#define LIBFUZZER_FUCHSIA 0
|
||||||
|
#define LIBFUZZER_LINUX 0
|
||||||
|
#define LIBFUZZER_NETBSD 0
|
||||||
|
#define LIBFUZZER_FREEBSD 0
|
||||||
|
#define LIBFUZZER_OPENBSD 0
|
||||||
|
#define LIBFUZZER_WINDOWS 0
|
||||||
|
#define LIBFUZZER_EMSCRIPTEN 0
|
||||||
|
#elif __NetBSD__
|
||||||
|
#define LIBFUZZER_APPLE 0
|
||||||
|
#define LIBFUZZER_FUCHSIA 0
|
||||||
|
#define LIBFUZZER_LINUX 0
|
||||||
|
#define LIBFUZZER_NETBSD 1
|
||||||
|
#define LIBFUZZER_FREEBSD 0
|
||||||
|
#define LIBFUZZER_OPENBSD 0
|
||||||
|
#define LIBFUZZER_WINDOWS 0
|
||||||
|
#define LIBFUZZER_EMSCRIPTEN 0
|
||||||
|
#elif __FreeBSD__
|
||||||
|
#define LIBFUZZER_APPLE 0
|
||||||
|
#define LIBFUZZER_FUCHSIA 0
|
||||||
|
#define LIBFUZZER_LINUX 0
|
||||||
|
#define LIBFUZZER_NETBSD 0
|
||||||
|
#define LIBFUZZER_FREEBSD 1
|
||||||
|
#define LIBFUZZER_OPENBSD 0
|
||||||
|
#define LIBFUZZER_WINDOWS 0
|
||||||
|
#define LIBFUZZER_EMSCRIPTEN 0
|
||||||
|
#elif __OpenBSD__
|
||||||
|
#define LIBFUZZER_APPLE 0
|
||||||
|
#define LIBFUZZER_FUCHSIA 0
|
||||||
|
#define LIBFUZZER_LINUX 0
|
||||||
|
#define LIBFUZZER_NETBSD 0
|
||||||
|
#define LIBFUZZER_FREEBSD 0
|
||||||
|
#define LIBFUZZER_OPENBSD 1
|
||||||
|
#define LIBFUZZER_WINDOWS 0
|
||||||
|
#define LIBFUZZER_EMSCRIPTEN 0
|
||||||
|
#elif _WIN32
|
||||||
|
#define LIBFUZZER_APPLE 0
|
||||||
|
#define LIBFUZZER_FUCHSIA 0
|
||||||
|
#define LIBFUZZER_LINUX 0
|
||||||
|
#define LIBFUZZER_NETBSD 0
|
||||||
|
#define LIBFUZZER_FREEBSD 0
|
||||||
|
#define LIBFUZZER_OPENBSD 0
|
||||||
|
#define LIBFUZZER_WINDOWS 1
|
||||||
|
#define LIBFUZZER_EMSCRIPTEN 0
|
||||||
|
#elif __Fuchsia__
|
||||||
|
#define LIBFUZZER_APPLE 0
|
||||||
|
#define LIBFUZZER_FUCHSIA 1
|
||||||
|
#define LIBFUZZER_LINUX 0
|
||||||
|
#define LIBFUZZER_NETBSD 0
|
||||||
|
#define LIBFUZZER_FREEBSD 0
|
||||||
|
#define LIBFUZZER_OPENBSD 0
|
||||||
|
#define LIBFUZZER_WINDOWS 0
|
||||||
|
#define LIBFUZZER_EMSCRIPTEN 0
|
||||||
|
#elif __EMSCRIPTEN__
|
||||||
|
#define LIBFUZZER_APPLE 0
|
||||||
|
#define LIBFUZZER_FUCHSIA 0
|
||||||
|
#define LIBFUZZER_LINUX 0
|
||||||
|
#define LIBFUZZER_NETBSD 0
|
||||||
|
#define LIBFUZZER_FREEBSD 0
|
||||||
|
#define LIBFUZZER_OPENBSD 0
|
||||||
|
#define LIBFUZZER_WINDOWS 0
|
||||||
|
#define LIBFUZZER_EMSCRIPTEN 1
|
||||||
|
#else
|
||||||
|
#error "Support for your platform has not been implemented"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(__clang__)
|
||||||
|
// MSVC compiler is being used.
|
||||||
|
#define LIBFUZZER_MSVC 1
|
||||||
|
#else
|
||||||
|
#define LIBFUZZER_MSVC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __has_attribute
|
||||||
|
#define __has_attribute(x) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LIBFUZZER_POSIX \
|
||||||
|
(LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD || \
|
||||||
|
LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN)
|
||||||
|
|
||||||
|
#ifdef __x86_64
|
||||||
|
#if __has_attribute(target)
|
||||||
|
#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt")))
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_TARGET_POPCNT
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_TARGET_POPCNT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __clang__ // avoid gcc warning.
|
||||||
|
#if __has_attribute(no_sanitize)
|
||||||
|
#define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||||
|
#endif
|
||||||
|
#define ALWAYS_INLINE __attribute__((always_inline))
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||||
|
#define ALWAYS_INLINE
|
||||||
|
#endif // __clang__
|
||||||
|
|
||||||
|
#if LIBFUZZER_WINDOWS
|
||||||
|
#define ATTRIBUTE_NO_SANITIZE_ADDRESS
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LIBFUZZER_WINDOWS
|
||||||
|
#define ATTRIBUTE_ALIGNED(X) __declspec(align(X))
|
||||||
|
#define ATTRIBUTE_INTERFACE __declspec(dllexport)
|
||||||
|
// This is used for __sancov_lowest_stack which is needed for
|
||||||
|
// -fsanitize-coverage=stack-depth. That feature is not yet available on
|
||||||
|
// Windows, so make the symbol static to avoid linking errors.
|
||||||
|
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC static
|
||||||
|
#define ATTRIBUTE_NOINLINE __declspec(noinline)
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_ALIGNED(X) __attribute__((aligned(X)))
|
||||||
|
#define ATTRIBUTE_INTERFACE __attribute__((visibility("default")))
|
||||||
|
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
|
||||||
|
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local
|
||||||
|
|
||||||
|
#define ATTRIBUTE_NOINLINE __attribute__((noinline))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__has_feature)
|
||||||
|
#if __has_feature(address_sanitizer)
|
||||||
|
#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS
|
||||||
|
#elif __has_feature(memory_sanitizer)
|
||||||
|
#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_PLATFORM_H
|
38
custom_mutators/libfuzzer/FuzzerRandom.h
Normal file
38
custom_mutators/libfuzzer/FuzzerRandom.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// fuzzer::Random
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_RANDOM_H
|
||||||
|
#define LLVM_FUZZER_RANDOM_H
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
class Random : public std::minstd_rand {
|
||||||
|
public:
|
||||||
|
Random(unsigned int seed) : std::minstd_rand(seed) {}
|
||||||
|
result_type operator()() { return this->std::minstd_rand::operator()(); }
|
||||||
|
size_t Rand() { return this->operator()(); }
|
||||||
|
size_t RandBool() { return Rand() % 2; }
|
||||||
|
size_t SkewTowardsLast(size_t n) {
|
||||||
|
size_t T = this->operator()(n * n);
|
||||||
|
size_t Res = sqrt(T);
|
||||||
|
return Res;
|
||||||
|
}
|
||||||
|
size_t operator()(size_t n) { return n ? Rand() % n : 0; }
|
||||||
|
intptr_t operator()(intptr_t From, intptr_t To) {
|
||||||
|
assert(From < To);
|
||||||
|
intptr_t RangeSize = To - From + 1;
|
||||||
|
return operator()(RangeSize) + From;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_RANDOM_H
|
269
custom_mutators/libfuzzer/FuzzerSHA1.cpp
Normal file
269
custom_mutators/libfuzzer/FuzzerSHA1.cpp
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// This code is taken from public domain
|
||||||
|
// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c)
|
||||||
|
// and modified by adding anonymous namespace, adding an interface
|
||||||
|
// function fuzzer::ComputeSHA1() and removing unnecessary code.
|
||||||
|
//
|
||||||
|
// lib/Fuzzer can not use SHA1 implementation from openssl because
|
||||||
|
// openssl may not be available and because we may be fuzzing openssl itself.
|
||||||
|
// For the same reason we do not want to depend on SHA1 from LLVM tree.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "FuzzerSHA1.h"
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
|
||||||
|
/* This code is public-domain - it is based on libcrypt
|
||||||
|
* placed in the public domain by Wei Dai and other contributors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace { // Added for LibFuzzer
|
||||||
|
|
||||||
|
#ifdef __BIG_ENDIAN__
|
||||||
|
#define SHA_BIG_ENDIAN
|
||||||
|
// Windows is always little endian and MSVC doesn't have <endian.h>
|
||||||
|
#elif defined __LITTLE_ENDIAN__ || LIBFUZZER_WINDOWS
|
||||||
|
/* override */
|
||||||
|
#elif defined __BYTE_ORDER
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
#define SHA_BIG_ENDIAN
|
||||||
|
#endif
|
||||||
|
#else // ! defined __LITTLE_ENDIAN__
|
||||||
|
#include <endian.h> // machine/endian.h
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
#define SHA_BIG_ENDIAN
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header */
|
||||||
|
|
||||||
|
#define HASH_LENGTH 20
|
||||||
|
#define BLOCK_LENGTH 64
|
||||||
|
|
||||||
|
typedef struct sha1nfo {
|
||||||
|
|
||||||
|
uint32_t buffer[BLOCK_LENGTH / 4];
|
||||||
|
uint32_t state[HASH_LENGTH / 4];
|
||||||
|
uint32_t byteCount;
|
||||||
|
uint8_t bufferOffset;
|
||||||
|
uint8_t keyBuffer[BLOCK_LENGTH];
|
||||||
|
uint8_t innerHash[HASH_LENGTH];
|
||||||
|
|
||||||
|
} sha1nfo;
|
||||||
|
|
||||||
|
/* public API - prototypes - TODO: doxygen*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
void sha1_init(sha1nfo *s);
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
void sha1_writebyte(sha1nfo *s, uint8_t data);
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
void sha1_write(sha1nfo *s, const char *data, size_t len);
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
uint8_t *sha1_result(sha1nfo *s);
|
||||||
|
|
||||||
|
/* code */
|
||||||
|
#define SHA1_K0 0x5a827999
|
||||||
|
#define SHA1_K20 0x6ed9eba1
|
||||||
|
#define SHA1_K40 0x8f1bbcdc
|
||||||
|
#define SHA1_K60 0xca62c1d6
|
||||||
|
|
||||||
|
void sha1_init(sha1nfo *s) {
|
||||||
|
|
||||||
|
s->state[0] = 0x67452301;
|
||||||
|
s->state[1] = 0xefcdab89;
|
||||||
|
s->state[2] = 0x98badcfe;
|
||||||
|
s->state[3] = 0x10325476;
|
||||||
|
s->state[4] = 0xc3d2e1f0;
|
||||||
|
s->byteCount = 0;
|
||||||
|
s->bufferOffset = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t sha1_rol32(uint32_t number, uint8_t bits) {
|
||||||
|
|
||||||
|
return ((number << bits) | (number >> (32 - bits)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha1_hashBlock(sha1nfo *s) {
|
||||||
|
|
||||||
|
uint8_t i;
|
||||||
|
uint32_t a, b, c, d, e, t;
|
||||||
|
|
||||||
|
a = s->state[0];
|
||||||
|
b = s->state[1];
|
||||||
|
c = s->state[2];
|
||||||
|
d = s->state[3];
|
||||||
|
e = s->state[4];
|
||||||
|
for (i = 0; i < 80; i++) {
|
||||||
|
|
||||||
|
if (i >= 16) {
|
||||||
|
|
||||||
|
t = s->buffer[(i + 13) & 15] ^ s->buffer[(i + 8) & 15] ^
|
||||||
|
s->buffer[(i + 2) & 15] ^ s->buffer[i & 15];
|
||||||
|
s->buffer[i & 15] = sha1_rol32(t, 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < 20) {
|
||||||
|
|
||||||
|
t = (d ^ (b & (c ^ d))) + SHA1_K0;
|
||||||
|
|
||||||
|
} else if (i < 40) {
|
||||||
|
|
||||||
|
t = (b ^ c ^ d) + SHA1_K20;
|
||||||
|
|
||||||
|
} else if (i < 60) {
|
||||||
|
|
||||||
|
t = ((b & c) | (d & (b | c))) + SHA1_K40;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
t = (b ^ c ^ d) + SHA1_K60;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
t += sha1_rol32(a, 5) + e + s->buffer[i & 15];
|
||||||
|
e = d;
|
||||||
|
d = c;
|
||||||
|
c = sha1_rol32(b, 30);
|
||||||
|
b = a;
|
||||||
|
a = t;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
s->state[0] += a;
|
||||||
|
s->state[1] += b;
|
||||||
|
s->state[2] += c;
|
||||||
|
s->state[3] += d;
|
||||||
|
s->state[4] += e;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha1_addUncounted(sha1nfo *s, uint8_t data) {
|
||||||
|
|
||||||
|
uint8_t *const b = (uint8_t *)s->buffer;
|
||||||
|
#ifdef SHA_BIG_ENDIAN
|
||||||
|
b[s->bufferOffset] = data;
|
||||||
|
#else
|
||||||
|
b[s->bufferOffset ^ 3] = data;
|
||||||
|
#endif
|
||||||
|
s->bufferOffset++;
|
||||||
|
if (s->bufferOffset == BLOCK_LENGTH) {
|
||||||
|
|
||||||
|
sha1_hashBlock(s);
|
||||||
|
s->bufferOffset = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha1_writebyte(sha1nfo *s, uint8_t data) {
|
||||||
|
|
||||||
|
++s->byteCount;
|
||||||
|
sha1_addUncounted(s, data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha1_write(sha1nfo *s, const char *data, size_t len) {
|
||||||
|
|
||||||
|
for (; len--;)
|
||||||
|
sha1_writebyte(s, (uint8_t)*data++);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha1_pad(sha1nfo *s) {
|
||||||
|
|
||||||
|
// Implement SHA-1 padding (fips180-2 §5.1.1)
|
||||||
|
|
||||||
|
// Pad with 0x80 followed by 0x00 until the end of the block
|
||||||
|
sha1_addUncounted(s, 0x80);
|
||||||
|
while (s->bufferOffset != 56)
|
||||||
|
sha1_addUncounted(s, 0x00);
|
||||||
|
|
||||||
|
// Append length in the last 8 bytes
|
||||||
|
sha1_addUncounted(s, 0); // We're only using 32 bit lengths
|
||||||
|
sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths
|
||||||
|
sha1_addUncounted(s, 0); // So zero pad the top bits
|
||||||
|
sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8
|
||||||
|
sha1_addUncounted(
|
||||||
|
s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as
|
||||||
|
sha1_addUncounted(s, s->byteCount >> 13); // byte.
|
||||||
|
sha1_addUncounted(s, s->byteCount >> 5);
|
||||||
|
sha1_addUncounted(s, s->byteCount << 3);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *sha1_result(sha1nfo *s) {
|
||||||
|
|
||||||
|
// Pad to complete the last block
|
||||||
|
sha1_pad(s);
|
||||||
|
|
||||||
|
#ifndef SHA_BIG_ENDIAN
|
||||||
|
// Swap byte order back
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
|
||||||
|
s->state[i] = (((s->state[i]) << 24) & 0xff000000) |
|
||||||
|
(((s->state[i]) << 8) & 0x00ff0000) |
|
||||||
|
(((s->state[i]) >> 8) & 0x0000ff00) |
|
||||||
|
(((s->state[i]) >> 24) & 0x000000ff);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Return pointer to hash (20 characters)
|
||||||
|
return (uint8_t *)s->state;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
// The rest is added for LibFuzzer
|
||||||
|
void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) {
|
||||||
|
|
||||||
|
sha1nfo s;
|
||||||
|
sha1_init(&s);
|
||||||
|
sha1_write(&s, (const char *)Data, Len);
|
||||||
|
memcpy(Out, sha1_result(&s), HASH_LENGTH);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) {
|
||||||
|
|
||||||
|
std::stringstream SS;
|
||||||
|
for (int i = 0; i < kSHA1NumBytes; i++)
|
||||||
|
SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i];
|
||||||
|
return SS.str();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Hash(const Unit &U) {
|
||||||
|
|
||||||
|
uint8_t Hash[kSHA1NumBytes];
|
||||||
|
ComputeSHA1(U.data(), U.size(), Hash);
|
||||||
|
return Sha1ToString(Hash);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
32
custom_mutators/libfuzzer/FuzzerSHA1.h
Normal file
32
custom_mutators/libfuzzer/FuzzerSHA1.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// SHA1 utils.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_SHA1_H
|
||||||
|
#define LLVM_FUZZER_SHA1_H
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include <cstddef>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
// Private copy of SHA1 implementation.
|
||||||
|
static const int kSHA1NumBytes = 20;
|
||||||
|
|
||||||
|
// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'.
|
||||||
|
void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out);
|
||||||
|
|
||||||
|
std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]);
|
||||||
|
|
||||||
|
std::string Hash(const Unit &U);
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_SHA1_H
|
819
custom_mutators/libfuzzer/FuzzerTracePC.cpp
Normal file
819
custom_mutators/libfuzzer/FuzzerTracePC.cpp
Normal file
@ -0,0 +1,819 @@
|
|||||||
|
//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Trace PCs.
|
||||||
|
// This module implements __sanitizer_cov_trace_pc_guard[_init],
|
||||||
|
// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "FuzzerTracePC.h"
|
||||||
|
#include "FuzzerBuiltins.h"
|
||||||
|
#include "FuzzerBuiltinsMsvc.h"
|
||||||
|
#include "FuzzerCorpus.h"
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerDictionary.h"
|
||||||
|
#include "FuzzerExtFunctions.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#include "FuzzerUtil.h"
|
||||||
|
#include "FuzzerValueBitMap.h"
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
// Used by -fsanitize-coverage=stack-depth to track stack depth
|
||||||
|
ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack;
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
TracePC TPC;
|
||||||
|
|
||||||
|
size_t TracePC::GetTotalPCCoverage() {
|
||||||
|
|
||||||
|
return ObservedPCs.size();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) {
|
||||||
|
|
||||||
|
if (Start == Stop) return;
|
||||||
|
if (NumModules && Modules[NumModules - 1].Start() == Start) return;
|
||||||
|
assert(NumModules < sizeof(Modules) / sizeof(Modules[0]));
|
||||||
|
auto & M = Modules[NumModules++];
|
||||||
|
uint8_t *AlignedStart = RoundUpByPage(Start);
|
||||||
|
uint8_t *AlignedStop = RoundDownByPage(Stop);
|
||||||
|
size_t NumFullPages = AlignedStop > AlignedStart
|
||||||
|
? (AlignedStop - AlignedStart) / PageSize()
|
||||||
|
: 0;
|
||||||
|
bool NeedFirst = Start < AlignedStart || !NumFullPages;
|
||||||
|
bool NeedLast = Stop > AlignedStop && AlignedStop >= AlignedStart;
|
||||||
|
M.NumRegions = NumFullPages + NeedFirst + NeedLast;
|
||||||
|
;
|
||||||
|
assert(M.NumRegions > 0);
|
||||||
|
M.Regions = new Module::Region[M.NumRegions];
|
||||||
|
assert(M.Regions);
|
||||||
|
size_t R = 0;
|
||||||
|
if (NeedFirst)
|
||||||
|
M.Regions[R++] = {Start, std::min(Stop, AlignedStart), true, false};
|
||||||
|
for (uint8_t *P = AlignedStart; P < AlignedStop; P += PageSize())
|
||||||
|
M.Regions[R++] = {P, P + PageSize(), true, true};
|
||||||
|
if (NeedLast) M.Regions[R++] = {AlignedStop, Stop, true, false};
|
||||||
|
assert(R == M.NumRegions);
|
||||||
|
assert(M.Size() == (size_t)(Stop - Start));
|
||||||
|
assert(M.Stop() == Stop);
|
||||||
|
assert(M.Start() == Start);
|
||||||
|
NumInline8bitCounters += M.Size();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) {
|
||||||
|
|
||||||
|
const PCTableEntry *B = reinterpret_cast<const PCTableEntry *>(Start);
|
||||||
|
const PCTableEntry *E = reinterpret_cast<const PCTableEntry *>(Stop);
|
||||||
|
if (NumPCTables && ModulePCTable[NumPCTables - 1].Start == B) return;
|
||||||
|
assert(NumPCTables < sizeof(ModulePCTable) / sizeof(ModulePCTable[0]));
|
||||||
|
ModulePCTable[NumPCTables++] = {B, E};
|
||||||
|
NumPCsInPCTables += E - B;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TracePC::PrintModuleInfo() {
|
||||||
|
|
||||||
|
if (NumModules) {
|
||||||
|
|
||||||
|
Printf("INFO: Loaded %zd modules (%zd inline 8-bit counters): ",
|
||||||
|
NumModules, NumInline8bitCounters);
|
||||||
|
for (size_t i = 0; i < NumModules; i++)
|
||||||
|
Printf("%zd [%p, %p), ", Modules[i].Size(), Modules[i].Start(),
|
||||||
|
Modules[i].Stop());
|
||||||
|
Printf("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NumPCTables) {
|
||||||
|
|
||||||
|
Printf("INFO: Loaded %zd PC tables (%zd PCs): ", NumPCTables,
|
||||||
|
NumPCsInPCTables);
|
||||||
|
for (size_t i = 0; i < NumPCTables; i++) {
|
||||||
|
|
||||||
|
Printf("%zd [%p,%p), ", ModulePCTable[i].Stop - ModulePCTable[i].Start,
|
||||||
|
ModulePCTable[i].Start, ModulePCTable[i].Stop);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Printf("\n");
|
||||||
|
|
||||||
|
if (NumInline8bitCounters && NumInline8bitCounters != NumPCsInPCTables) {
|
||||||
|
|
||||||
|
Printf(
|
||||||
|
"ERROR: The size of coverage PC tables does not match the\n"
|
||||||
|
"number of instrumented PCs. This might be a compiler bug,\n"
|
||||||
|
"please contact the libFuzzer developers.\n"
|
||||||
|
"Also check https://bugs.llvm.org/show_bug.cgi?id=34636\n"
|
||||||
|
"for possible workarounds (tl;dr: don't use the old GNU ld)\n");
|
||||||
|
_Exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin())
|
||||||
|
Printf("INFO: %zd Extra Counters\n", NumExtraCounters);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
|
||||||
|
|
||||||
|
const uintptr_t kBits = 12;
|
||||||
|
const uintptr_t kMask = (1 << kBits) - 1;
|
||||||
|
uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits);
|
||||||
|
ValueProfileMap.AddValueModPrime(Idx);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \return the address of the previous instruction.
|
||||||
|
/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.h`
|
||||||
|
inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
|
||||||
|
|
||||||
|
#if defined(__arm__)
|
||||||
|
// T32 (Thumb) branch instructions might be 16 or 32 bit long,
|
||||||
|
// so we return (pc-2) in that case in order to be safe.
|
||||||
|
// For A32 mode we return (pc-4) because all instructions are 32 bit long.
|
||||||
|
return (PC - 3) & (~1);
|
||||||
|
#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__)
|
||||||
|
// PCs are always 4 byte aligned.
|
||||||
|
return PC - 4;
|
||||||
|
#elif defined(__sparc__) || defined(__mips__)
|
||||||
|
return PC - 8;
|
||||||
|
#else
|
||||||
|
return PC - 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \return the address of the next instruction.
|
||||||
|
/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cpp`
|
||||||
|
ALWAYS_INLINE uintptr_t TracePC::GetNextInstructionPc(uintptr_t PC) {
|
||||||
|
|
||||||
|
#if defined(__mips__)
|
||||||
|
return PC + 8;
|
||||||
|
#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \
|
||||||
|
defined(__aarch64__)
|
||||||
|
return PC + 4;
|
||||||
|
#else
|
||||||
|
return PC + 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TracePC::UpdateObservedPCs() {
|
||||||
|
|
||||||
|
Vector<uintptr_t> CoveredFuncs;
|
||||||
|
auto ObservePC = [&](const PCTableEntry *TE) {
|
||||||
|
|
||||||
|
if (ObservedPCs.insert(TE).second && DoPrintNewPCs) {
|
||||||
|
|
||||||
|
PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p",
|
||||||
|
GetNextInstructionPc(TE->PC));
|
||||||
|
Printf("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
auto Observe = [&](const PCTableEntry *TE) {
|
||||||
|
|
||||||
|
if (PcIsFuncEntry(TE))
|
||||||
|
if (++ObservedFuncs[TE->PC] == 1 && NumPrintNewFuncs)
|
||||||
|
CoveredFuncs.push_back(TE->PC);
|
||||||
|
ObservePC(TE);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
if (NumPCsInPCTables) {
|
||||||
|
|
||||||
|
if (NumInline8bitCounters == NumPCsInPCTables) {
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NumModules; i++) {
|
||||||
|
|
||||||
|
auto &M = Modules[i];
|
||||||
|
assert(M.Size() ==
|
||||||
|
(size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
|
||||||
|
for (size_t r = 0; r < M.NumRegions; r++) {
|
||||||
|
|
||||||
|
auto &R = M.Regions[r];
|
||||||
|
if (!R.Enabled) continue;
|
||||||
|
for (uint8_t *P = R.Start; P < R.Stop; P++)
|
||||||
|
if (*P) Observe(&ModulePCTable[i].Start[M.Idx(P)]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N;
|
||||||
|
i++) {
|
||||||
|
|
||||||
|
Printf("\tNEW_FUNC[%zd/%zd]: ", i + 1, CoveredFuncs.size());
|
||||||
|
PrintPC("%p %F %L", "%p", GetNextInstructionPc(CoveredFuncs[i]));
|
||||||
|
Printf("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t TracePC::PCTableEntryIdx(const PCTableEntry *TE) {
|
||||||
|
|
||||||
|
size_t TotalTEs = 0;
|
||||||
|
for (size_t i = 0; i < NumPCTables; i++) {
|
||||||
|
|
||||||
|
auto &M = ModulePCTable[i];
|
||||||
|
if (TE >= M.Start && TE < M.Stop) return TotalTEs + TE - M.Start;
|
||||||
|
TotalTEs += M.Stop - M.Start;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(0);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const TracePC::PCTableEntry *TracePC::PCTableEntryByIdx(uintptr_t Idx) {
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NumPCTables; i++) {
|
||||||
|
|
||||||
|
auto & M = ModulePCTable[i];
|
||||||
|
size_t Size = M.Stop - M.Start;
|
||||||
|
if (Idx < Size) return &M.Start[Idx];
|
||||||
|
Idx -= Size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetModuleName(uintptr_t PC) {
|
||||||
|
|
||||||
|
char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
|
||||||
|
void *OffsetRaw = nullptr;
|
||||||
|
if (!EF->__sanitizer_get_module_and_offset_for_pc(
|
||||||
|
reinterpret_cast<void *>(PC), ModulePathRaw, sizeof(ModulePathRaw),
|
||||||
|
&OffsetRaw))
|
||||||
|
return "";
|
||||||
|
return ModulePathRaw;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class CallBack>
|
||||||
|
void TracePC::IterateCoveredFunctions(CallBack CB) {
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NumPCTables; i++) {
|
||||||
|
|
||||||
|
auto &M = ModulePCTable[i];
|
||||||
|
assert(M.Start < M.Stop);
|
||||||
|
auto ModuleName = GetModuleName(M.Start->PC);
|
||||||
|
for (auto NextFE = M.Start; NextFE < M.Stop;) {
|
||||||
|
|
||||||
|
auto FE = NextFE;
|
||||||
|
assert(PcIsFuncEntry(FE) && "Not a function entry point");
|
||||||
|
do {
|
||||||
|
|
||||||
|
NextFE++;
|
||||||
|
|
||||||
|
} while (NextFE < M.Stop && !(PcIsFuncEntry(NextFE)));
|
||||||
|
|
||||||
|
CB(FE, NextFE, ObservedFuncs[FE->PC]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TracePC::SetFocusFunction(const std::string &FuncName) {
|
||||||
|
|
||||||
|
// This function should be called once.
|
||||||
|
assert(!FocusFunctionCounterPtr);
|
||||||
|
// "auto" is not a valid function name. If this function is called with "auto"
|
||||||
|
// that means the auto focus functionality failed.
|
||||||
|
if (FuncName.empty() || FuncName == "auto") return;
|
||||||
|
for (size_t M = 0; M < NumModules; M++) {
|
||||||
|
|
||||||
|
auto & PCTE = ModulePCTable[M];
|
||||||
|
size_t N = PCTE.Stop - PCTE.Start;
|
||||||
|
for (size_t I = 0; I < N; I++) {
|
||||||
|
|
||||||
|
if (!(PcIsFuncEntry(&PCTE.Start[I]))) continue; // not a function entry.
|
||||||
|
auto Name = DescribePC("%F", GetNextInstructionPc(PCTE.Start[I].PC));
|
||||||
|
if (Name[0] == 'i' && Name[1] == 'n' && Name[2] == ' ')
|
||||||
|
Name = Name.substr(3, std::string::npos);
|
||||||
|
if (FuncName != Name) continue;
|
||||||
|
Printf("INFO: Focus function is set to '%s'\n", Name.c_str());
|
||||||
|
FocusFunctionCounterPtr = Modules[M].Start() + I;
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Printf(
|
||||||
|
"ERROR: Failed to set focus function. Make sure the function name is "
|
||||||
|
"valid (%s) and symbolization is enabled.\n",
|
||||||
|
FuncName.c_str());
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TracePC::ObservedFocusFunction() {
|
||||||
|
|
||||||
|
return FocusFunctionCounterPtr && *FocusFunctionCounterPtr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TracePC::PrintCoverage() {
|
||||||
|
|
||||||
|
if (!EF->__sanitizer_symbolize_pc ||
|
||||||
|
!EF->__sanitizer_get_module_and_offset_for_pc) {
|
||||||
|
|
||||||
|
Printf(
|
||||||
|
"INFO: __sanitizer_symbolize_pc or "
|
||||||
|
"__sanitizer_get_module_and_offset_for_pc is not available,"
|
||||||
|
" not printing coverage\n");
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Printf("COVERAGE:\n");
|
||||||
|
auto CoveredFunctionCallback = [&](const PCTableEntry *First,
|
||||||
|
const PCTableEntry *Last,
|
||||||
|
uintptr_t Counter) {
|
||||||
|
|
||||||
|
assert(First < Last);
|
||||||
|
auto VisualizePC = GetNextInstructionPc(First->PC);
|
||||||
|
std::string FileStr = DescribePC("%s", VisualizePC);
|
||||||
|
if (!IsInterestingCoverageFile(FileStr)) return;
|
||||||
|
std::string FunctionStr = DescribePC("%F", VisualizePC);
|
||||||
|
if (FunctionStr.find("in ") == 0) FunctionStr = FunctionStr.substr(3);
|
||||||
|
std::string LineStr = DescribePC("%l", VisualizePC);
|
||||||
|
size_t NumEdges = Last - First;
|
||||||
|
Vector<uintptr_t> UncoveredPCs;
|
||||||
|
for (auto TE = First; TE < Last; TE++)
|
||||||
|
if (!ObservedPCs.count(TE)) UncoveredPCs.push_back(TE->PC);
|
||||||
|
Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter);
|
||||||
|
Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges);
|
||||||
|
Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(),
|
||||||
|
LineStr.c_str());
|
||||||
|
if (Counter)
|
||||||
|
for (auto PC : UncoveredPCs)
|
||||||
|
Printf(" UNCOVERED_PC: %s\n",
|
||||||
|
DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str());
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IterateCoveredFunctions(CoveredFunctionCallback);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value profile.
|
||||||
|
// We keep track of various values that affect control flow.
|
||||||
|
// These values are inserted into a bit-set-based hash map.
|
||||||
|
// Every new bit in the map is treated as a new coverage.
|
||||||
|
//
|
||||||
|
// For memcmp/strcmp/etc the interesting value is the length of the common
|
||||||
|
// prefix of the parameters.
|
||||||
|
// For cmp instructions the interesting value is a XOR of the parameters.
|
||||||
|
// The interesting value is mixed up with the PC and is then added to the map.
|
||||||
|
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
|
||||||
|
size_t n, bool StopAtZero) {
|
||||||
|
|
||||||
|
if (!n) return;
|
||||||
|
size_t Len = std::min(n, Word::GetMaxSize());
|
||||||
|
const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
|
||||||
|
const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
|
||||||
|
uint8_t B1[Word::kMaxSize];
|
||||||
|
uint8_t B2[Word::kMaxSize];
|
||||||
|
// Copy the data into locals in this non-msan-instrumented function
|
||||||
|
// to avoid msan complaining further.
|
||||||
|
size_t Hash = 0; // Compute some simple hash of both strings.
|
||||||
|
for (size_t i = 0; i < Len; i++) {
|
||||||
|
|
||||||
|
B1[i] = A1[i];
|
||||||
|
B2[i] = A2[i];
|
||||||
|
size_t T = B1[i];
|
||||||
|
Hash ^= (T << 8) | B2[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t I = 0;
|
||||||
|
uint8_t HammingDistance = 0;
|
||||||
|
for (; I < Len; I++) {
|
||||||
|
|
||||||
|
if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0)) {
|
||||||
|
|
||||||
|
HammingDistance = Popcountll(B1[I] ^ B2[I]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PC = reinterpret_cast<size_t>(caller_pc);
|
||||||
|
size_t Idx = (PC & 4095) | (I << 12);
|
||||||
|
Idx += HammingDistance;
|
||||||
|
ValueProfileMap.AddValue(Idx);
|
||||||
|
TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE ATTRIBUTE_NO_SANITIZE_ALL void
|
||||||
|
TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) {
|
||||||
|
|
||||||
|
uint64_t ArgXor = Arg1 ^ Arg2;
|
||||||
|
if (sizeof(T) == 4)
|
||||||
|
TORC4.Insert(ArgXor, Arg1, Arg2);
|
||||||
|
else if (sizeof(T) == 8)
|
||||||
|
TORC8.Insert(ArgXor, Arg1, Arg2);
|
||||||
|
uint64_t HammingDistance = Popcountll(ArgXor); // [0,64]
|
||||||
|
uint64_t AbsoluteDistance = (Arg1 == Arg2 ? 0 : Clzll(Arg1 - Arg2) + 1);
|
||||||
|
ValueProfileMap.AddValue(PC * 128 + HammingDistance);
|
||||||
|
ValueProfileMap.AddValue(PC * 128 + 64 + AbsoluteDistance);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t InternalStrnlen(const char *S, size_t MaxLen) {
|
||||||
|
|
||||||
|
size_t Len = 0;
|
||||||
|
for (; Len < MaxLen && S[Len]; Len++) {}
|
||||||
|
return Len;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds min of (strlen(S1), strlen(S2)).
|
||||||
|
// Needed bacause one of these strings may actually be non-zero terminated.
|
||||||
|
static size_t InternalStrnlen2(const char *S1, const char *S2) {
|
||||||
|
|
||||||
|
size_t Len = 0;
|
||||||
|
for (; S1[Len] && S2[Len]; Len++) {}
|
||||||
|
return Len;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TracePC::ClearInlineCounters() {
|
||||||
|
|
||||||
|
IterateCounterRegions([](const Module::Region &R) {
|
||||||
|
|
||||||
|
if (R.Enabled) memset(R.Start, 0, R.Stop - R.Start);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
void TracePC::RecordInitialStack() {
|
||||||
|
|
||||||
|
int stack;
|
||||||
|
__sancov_lowest_stack = InitialStack = reinterpret_cast<uintptr_t>(&stack);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t TracePC::GetMaxStackOffset() const {
|
||||||
|
|
||||||
|
return InitialStack - __sancov_lowest_stack; // Stack grows down
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WarnAboutDeprecatedInstrumentation(const char *flag) {
|
||||||
|
|
||||||
|
// Use RawPrint because Printf cannot be used on Windows before OutputFile is
|
||||||
|
// initialized.
|
||||||
|
RawPrint(flag);
|
||||||
|
RawPrint(
|
||||||
|
" is no longer supported by libFuzzer.\n"
|
||||||
|
"Please either migrate to a compiler that supports -fsanitize=fuzzer\n"
|
||||||
|
"or use an older version of libFuzzer\n");
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
|
||||||
|
|
||||||
|
fuzzer::WarnAboutDeprecatedInstrumentation(
|
||||||
|
"-fsanitize-coverage=trace-pc-guard");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Best-effort support for -fsanitize-coverage=trace-pc, which is available
|
||||||
|
// in both Clang and GCC.
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
void __sanitizer_cov_trace_pc() {
|
||||||
|
|
||||||
|
fuzzer::WarnAboutDeprecatedInstrumentation("-fsanitize-coverage=trace-pc");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
|
||||||
|
|
||||||
|
fuzzer::WarnAboutDeprecatedInstrumentation(
|
||||||
|
"-fsanitize-coverage=trace-pc-guard");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
void __sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop) {
|
||||||
|
|
||||||
|
fuzzer::TPC.HandleInline8bitCountersInit(Start, Stop);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
|
||||||
|
const uintptr_t *pcs_end) {
|
||||||
|
|
||||||
|
fuzzer::TPC.HandlePCsInit(pcs_beg, pcs_end);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCallerCallee(PC, Callee);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
// Now the __sanitizer_cov_trace_const_cmp[1248] callbacks just mimic
|
||||||
|
// the behaviour of __sanitizer_cov_trace_cmp[1248] ones. This, however,
|
||||||
|
// should be changed later to make full use of instrumentation.
|
||||||
|
void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
|
||||||
|
|
||||||
|
uint64_t N = Cases[0];
|
||||||
|
uint64_t ValSizeInBits = Cases[1];
|
||||||
|
uint64_t *Vals = Cases + 2;
|
||||||
|
// Skip the most common and the most boring case: all switch values are small.
|
||||||
|
// We may want to skip this at compile-time, but it will make the
|
||||||
|
// instrumentation less general.
|
||||||
|
if (Vals[N - 1] < 256) return;
|
||||||
|
// Also skip small inputs values, they won't give good signal.
|
||||||
|
if (Val < 256) return;
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
size_t i;
|
||||||
|
uint64_t Smaller = 0;
|
||||||
|
uint64_t Larger = ~(uint64_t)0;
|
||||||
|
// Find two switch values such that Smaller < Val < Larger.
|
||||||
|
// Use 0 and 0xfff..f as the defaults.
|
||||||
|
for (i = 0; i < N; i++) {
|
||||||
|
|
||||||
|
if (Val < Vals[i]) {
|
||||||
|
|
||||||
|
Larger = Vals[i];
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Val > Vals[i]) Smaller = Vals[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply HandleCmp to {Val,Smaller} and {Val, Larger},
|
||||||
|
// use i as the PC modifier for HandleCmp.
|
||||||
|
if (ValSizeInBits == 16) {
|
||||||
|
|
||||||
|
fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast<uint16_t>(Val),
|
||||||
|
(uint16_t)(Smaller));
|
||||||
|
fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast<uint16_t>(Val),
|
||||||
|
(uint16_t)(Larger));
|
||||||
|
|
||||||
|
} else if (ValSizeInBits == 32) {
|
||||||
|
|
||||||
|
fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast<uint32_t>(Val),
|
||||||
|
(uint32_t)(Smaller));
|
||||||
|
fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast<uint32_t>(Val),
|
||||||
|
(uint32_t)(Larger));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
fuzzer::TPC.HandleCmp(PC + 2 * i, Val, Smaller);
|
||||||
|
fuzzer::TPC.HandleCmp(PC + 2 * i + 1, Val, Larger);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_div4(uint32_t Val) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_div8(uint64_t Val) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
ATTRIBUTE_TARGET_POPCNT
|
||||||
|
void __sanitizer_cov_trace_gep(uintptr_t Idx) {
|
||||||
|
|
||||||
|
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||||
|
fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
|
||||||
|
__sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2,
|
||||||
|
size_t n, int result) {
|
||||||
|
|
||||||
|
if (!fuzzer::RunningUserCallback) return;
|
||||||
|
if (result == 0) return; // No reason to mutate.
|
||||||
|
if (n <= 1) return; // Not interesting.
|
||||||
|
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/ false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
|
||||||
|
__sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2,
|
||||||
|
size_t n, int result) {
|
||||||
|
|
||||||
|
if (!fuzzer::RunningUserCallback) return;
|
||||||
|
if (result == 0) return; // No reason to mutate.
|
||||||
|
size_t Len1 = fuzzer::InternalStrnlen(s1, n);
|
||||||
|
size_t Len2 = fuzzer::InternalStrnlen(s2, n);
|
||||||
|
n = std::min(n, Len1);
|
||||||
|
n = std::min(n, Len2);
|
||||||
|
if (n <= 1) return; // Not interesting.
|
||||||
|
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/ true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
|
||||||
|
__sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2,
|
||||||
|
int result) {
|
||||||
|
|
||||||
|
if (!fuzzer::RunningUserCallback) return;
|
||||||
|
if (result == 0) return; // No reason to mutate.
|
||||||
|
size_t N = fuzzer::InternalStrnlen2(s1, s2);
|
||||||
|
if (N <= 1) return; // Not interesting.
|
||||||
|
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/ true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
|
||||||
|
__sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
|
||||||
|
const char *s2, size_t n, int result) {
|
||||||
|
|
||||||
|
if (!fuzzer::RunningUserCallback) return;
|
||||||
|
return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
|
||||||
|
__sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
|
||||||
|
const char *s2, int result) {
|
||||||
|
|
||||||
|
if (!fuzzer::RunningUserCallback) return;
|
||||||
|
return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
|
||||||
|
__sanitizer_weak_hook_strstr(void *called_pc, const char *s1, const char *s2,
|
||||||
|
char *result) {
|
||||||
|
|
||||||
|
if (!fuzzer::RunningUserCallback) return;
|
||||||
|
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
|
||||||
|
__sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
|
||||||
|
const char *s2, char *result) {
|
||||||
|
|
||||||
|
if (!fuzzer::RunningUserCallback) return;
|
||||||
|
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void
|
||||||
|
__sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
|
||||||
|
const void *s2, size_t len2, void *result) {
|
||||||
|
|
||||||
|
if (!fuzzer::RunningUserCallback) return;
|
||||||
|
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), len2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
291
custom_mutators/libfuzzer/FuzzerTracePC.h
Normal file
291
custom_mutators/libfuzzer/FuzzerTracePC.h
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// fuzzer::TracePC
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_TRACE_PC
|
||||||
|
#define LLVM_FUZZER_TRACE_PC
|
||||||
|
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
#include "FuzzerDictionary.h"
|
||||||
|
#include "FuzzerValueBitMap.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
// TableOfRecentCompares (TORC) remembers the most recently performed
|
||||||
|
// comparisons of type T.
|
||||||
|
// We record the arguments of CMP instructions in this table unconditionally
|
||||||
|
// because it seems cheaper this way than to compute some expensive
|
||||||
|
// conditions inside __sanitizer_cov_trace_cmp*.
|
||||||
|
// After the unit has been executed we may decide to use the contents of
|
||||||
|
// this table to populate a Dictionary.
|
||||||
|
template<class T, size_t kSizeT>
|
||||||
|
struct TableOfRecentCompares {
|
||||||
|
static const size_t kSize = kSizeT;
|
||||||
|
struct Pair {
|
||||||
|
T A, B;
|
||||||
|
};
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
void Insert(size_t Idx, const T &Arg1, const T &Arg2) {
|
||||||
|
Idx = Idx % kSize;
|
||||||
|
Table[Idx].A = Arg1;
|
||||||
|
Table[Idx].B = Arg2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair Get(size_t I) { return Table[I % kSize]; }
|
||||||
|
|
||||||
|
Pair Table[kSize];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t kSizeT>
|
||||||
|
struct MemMemTable {
|
||||||
|
static const size_t kSize = kSizeT;
|
||||||
|
Word MemMemWords[kSize];
|
||||||
|
Word EmptyWord;
|
||||||
|
|
||||||
|
void Add(const uint8_t *Data, size_t Size) {
|
||||||
|
if (Size <= 2) return;
|
||||||
|
Size = std::min(Size, Word::GetMaxSize());
|
||||||
|
size_t Idx = SimpleFastHash(Data, Size) % kSize;
|
||||||
|
MemMemWords[Idx].Set(Data, Size);
|
||||||
|
}
|
||||||
|
const Word &Get(size_t Idx) {
|
||||||
|
for (size_t i = 0; i < kSize; i++) {
|
||||||
|
const Word &W = MemMemWords[(Idx + i) % kSize];
|
||||||
|
if (W.size()) return W;
|
||||||
|
}
|
||||||
|
EmptyWord.Set(nullptr, 0);
|
||||||
|
return EmptyWord;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TracePC {
|
||||||
|
public:
|
||||||
|
void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop);
|
||||||
|
void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop);
|
||||||
|
void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee);
|
||||||
|
template <class T> void HandleCmp(uintptr_t PC, T Arg1, T Arg2);
|
||||||
|
size_t GetTotalPCCoverage();
|
||||||
|
void SetUseCounters(bool UC) { UseCounters = UC; }
|
||||||
|
void SetUseValueProfileMask(uint32_t VPMask) { UseValueProfileMask = VPMask; }
|
||||||
|
void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; }
|
||||||
|
void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; }
|
||||||
|
void UpdateObservedPCs();
|
||||||
|
template <class Callback> void CollectFeatures(Callback CB) const;
|
||||||
|
|
||||||
|
void ResetMaps() {
|
||||||
|
ValueProfileMap.Reset();
|
||||||
|
ClearExtraCounters();
|
||||||
|
ClearInlineCounters();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearInlineCounters();
|
||||||
|
|
||||||
|
void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize);
|
||||||
|
void PrintFeatureSet();
|
||||||
|
|
||||||
|
void PrintModuleInfo();
|
||||||
|
|
||||||
|
void PrintCoverage();
|
||||||
|
|
||||||
|
template<class CallBack>
|
||||||
|
void IterateCoveredFunctions(CallBack CB);
|
||||||
|
|
||||||
|
void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
|
||||||
|
size_t n, bool StopAtZero);
|
||||||
|
|
||||||
|
TableOfRecentCompares<uint32_t, 32> TORC4;
|
||||||
|
TableOfRecentCompares<uint64_t, 32> TORC8;
|
||||||
|
TableOfRecentCompares<Word, 32> TORCW;
|
||||||
|
MemMemTable<1024> MMT;
|
||||||
|
|
||||||
|
void RecordInitialStack();
|
||||||
|
uintptr_t GetMaxStackOffset() const;
|
||||||
|
|
||||||
|
template<class CallBack>
|
||||||
|
void ForEachObservedPC(CallBack CB) {
|
||||||
|
for (auto PC : ObservedPCs)
|
||||||
|
CB(PC);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFocusFunction(const std::string &FuncName);
|
||||||
|
bool ObservedFocusFunction();
|
||||||
|
|
||||||
|
struct PCTableEntry {
|
||||||
|
uintptr_t PC, PCFlags;
|
||||||
|
};
|
||||||
|
|
||||||
|
uintptr_t PCTableEntryIdx(const PCTableEntry *TE);
|
||||||
|
const PCTableEntry *PCTableEntryByIdx(uintptr_t Idx);
|
||||||
|
static uintptr_t GetNextInstructionPc(uintptr_t PC);
|
||||||
|
bool PcIsFuncEntry(const PCTableEntry *TE) { return TE->PCFlags & 1; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool UseCounters = false;
|
||||||
|
uint32_t UseValueProfileMask = false;
|
||||||
|
bool DoPrintNewPCs = false;
|
||||||
|
size_t NumPrintNewFuncs = 0;
|
||||||
|
|
||||||
|
// Module represents the array of 8-bit counters split into regions
|
||||||
|
// such that every region, except maybe the first and the last one, is one
|
||||||
|
// full page.
|
||||||
|
struct Module {
|
||||||
|
struct Region {
|
||||||
|
uint8_t *Start, *Stop;
|
||||||
|
bool Enabled;
|
||||||
|
bool OneFullPage;
|
||||||
|
};
|
||||||
|
Region *Regions;
|
||||||
|
size_t NumRegions;
|
||||||
|
uint8_t *Start() { return Regions[0].Start; }
|
||||||
|
uint8_t *Stop() { return Regions[NumRegions - 1].Stop; }
|
||||||
|
size_t Size() { return Stop() - Start(); }
|
||||||
|
size_t Idx(uint8_t *P) {
|
||||||
|
assert(P >= Start() && P < Stop());
|
||||||
|
return P - Start();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module Modules[4096];
|
||||||
|
size_t NumModules; // linker-initialized.
|
||||||
|
size_t NumInline8bitCounters;
|
||||||
|
|
||||||
|
template <class Callback>
|
||||||
|
void IterateCounterRegions(Callback CB) {
|
||||||
|
for (size_t m = 0; m < NumModules; m++)
|
||||||
|
for (size_t r = 0; r < Modules[m].NumRegions; r++)
|
||||||
|
CB(Modules[m].Regions[r]);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct { const PCTableEntry *Start, *Stop; } ModulePCTable[4096];
|
||||||
|
size_t NumPCTables;
|
||||||
|
size_t NumPCsInPCTables;
|
||||||
|
|
||||||
|
Set<const PCTableEntry*> ObservedPCs;
|
||||||
|
std::unordered_map<uintptr_t, uintptr_t> ObservedFuncs; // PC => Counter.
|
||||||
|
|
||||||
|
uint8_t *FocusFunctionCounterPtr = nullptr;
|
||||||
|
|
||||||
|
ValueBitMap ValueProfileMap;
|
||||||
|
uintptr_t InitialStack;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Callback>
|
||||||
|
// void Callback(size_t FirstFeature, size_t Idx, uint8_t Value);
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
size_t ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End,
|
||||||
|
size_t FirstFeature, Callback Handle8bitCounter) {
|
||||||
|
typedef uintptr_t LargeType;
|
||||||
|
const size_t Step = sizeof(LargeType) / sizeof(uint8_t);
|
||||||
|
const size_t StepMask = Step - 1;
|
||||||
|
auto P = Begin;
|
||||||
|
// Iterate by 1 byte until either the alignment boundary or the end.
|
||||||
|
for (; reinterpret_cast<uintptr_t>(P) & StepMask && P < End; P++)
|
||||||
|
if (uint8_t V = *P)
|
||||||
|
Handle8bitCounter(FirstFeature, P - Begin, V);
|
||||||
|
|
||||||
|
// Iterate by Step bytes at a time.
|
||||||
|
for (; P < End; P += Step)
|
||||||
|
if (LargeType Bundle = *reinterpret_cast<const LargeType *>(P)) {
|
||||||
|
Bundle = HostToLE(Bundle);
|
||||||
|
for (size_t I = 0; I < Step; I++, Bundle >>= 8)
|
||||||
|
if (uint8_t V = Bundle & 0xff)
|
||||||
|
Handle8bitCounter(FirstFeature, P - Begin + I, V);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate by 1 byte until the end.
|
||||||
|
for (; P < End; P++)
|
||||||
|
if (uint8_t V = *P)
|
||||||
|
Handle8bitCounter(FirstFeature, P - Begin, V);
|
||||||
|
return End - Begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a non-zero Counter returns a number in the range [0,7].
|
||||||
|
template<class T>
|
||||||
|
unsigned CounterToFeature(T Counter) {
|
||||||
|
// Returns a feature number by placing Counters into buckets as illustrated
|
||||||
|
// below.
|
||||||
|
//
|
||||||
|
// Counter bucket: [1] [2] [3] [4-7] [8-15] [16-31] [32-127] [128+]
|
||||||
|
// Feature number: 0 1 2 3 4 5 6 7
|
||||||
|
//
|
||||||
|
// This is a heuristic taken from AFL (see
|
||||||
|
// http://lcamtuf.coredump.cx/afl/technical_details.txt).
|
||||||
|
//
|
||||||
|
// This implementation may change in the future so clients should
|
||||||
|
// not rely on it.
|
||||||
|
assert(Counter);
|
||||||
|
unsigned Bit = 0;
|
||||||
|
/**/ if (Counter >= 128) Bit = 7;
|
||||||
|
else if (Counter >= 32) Bit = 6;
|
||||||
|
else if (Counter >= 16) Bit = 5;
|
||||||
|
else if (Counter >= 8) Bit = 4;
|
||||||
|
else if (Counter >= 4) Bit = 3;
|
||||||
|
else if (Counter >= 3) Bit = 2;
|
||||||
|
else if (Counter >= 2) Bit = 1;
|
||||||
|
return Bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Callback> // void Callback(size_t Feature)
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ADDRESS
|
||||||
|
ATTRIBUTE_NOINLINE
|
||||||
|
void TracePC::CollectFeatures(Callback HandleFeature) const {
|
||||||
|
auto Handle8bitCounter = [&](size_t FirstFeature,
|
||||||
|
size_t Idx, uint8_t Counter) {
|
||||||
|
if (UseCounters)
|
||||||
|
HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Counter));
|
||||||
|
else
|
||||||
|
HandleFeature(FirstFeature + Idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t FirstFeature = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NumModules; i++) {
|
||||||
|
for (size_t r = 0; r < Modules[i].NumRegions; r++) {
|
||||||
|
if (!Modules[i].Regions[r].Enabled) continue;
|
||||||
|
FirstFeature += 8 * ForEachNonZeroByte(Modules[i].Regions[r].Start,
|
||||||
|
Modules[i].Regions[r].Stop,
|
||||||
|
FirstFeature, Handle8bitCounter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FirstFeature +=
|
||||||
|
8 * ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(),
|
||||||
|
FirstFeature, Handle8bitCounter);
|
||||||
|
|
||||||
|
if (UseValueProfileMask) {
|
||||||
|
ValueProfileMap.ForEach([&](size_t Idx) {
|
||||||
|
HandleFeature(FirstFeature + Idx);
|
||||||
|
});
|
||||||
|
FirstFeature += ValueProfileMap.SizeInBits();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step function, grows similar to 8 * Log_2(A).
|
||||||
|
auto StackDepthStepFunction = [](uint32_t A) -> uint32_t {
|
||||||
|
if (!A) return A;
|
||||||
|
uint32_t Log2 = Log(A);
|
||||||
|
if (Log2 < 3) return A;
|
||||||
|
Log2 -= 3;
|
||||||
|
return (Log2 + 1) * 8 + ((A >> Log2) & 7);
|
||||||
|
};
|
||||||
|
assert(StackDepthStepFunction(1024) == 64);
|
||||||
|
assert(StackDepthStepFunction(1024 * 4) == 80);
|
||||||
|
assert(StackDepthStepFunction(1024 * 1024) == 144);
|
||||||
|
|
||||||
|
if (auto MaxStackOffset = GetMaxStackOffset())
|
||||||
|
HandleFeature(FirstFeature + StackDepthStepFunction(MaxStackOffset / 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern TracePC TPC;
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_TRACE_PC
|
314
custom_mutators/libfuzzer/FuzzerUtil.cpp
Normal file
314
custom_mutators/libfuzzer/FuzzerUtil.cpp
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Misc utils.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "FuzzerUtil.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerInternal.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstring>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
void PrintHexArray(const uint8_t *Data, size_t Size, const char *PrintAfter) {
|
||||||
|
|
||||||
|
for (size_t i = 0; i < Size; i++)
|
||||||
|
Printf("0x%x,", (unsigned)Data[i]);
|
||||||
|
Printf("%s", PrintAfter);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Print(const Unit &v, const char *PrintAfter) {
|
||||||
|
|
||||||
|
PrintHexArray(v.data(), v.size(), PrintAfter);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintASCIIByte(uint8_t Byte) {
|
||||||
|
|
||||||
|
if (Byte == '\\')
|
||||||
|
Printf("\\\\");
|
||||||
|
else if (Byte == '"')
|
||||||
|
Printf("\\\"");
|
||||||
|
else if (Byte >= 32 && Byte < 127)
|
||||||
|
Printf("%c", Byte);
|
||||||
|
else
|
||||||
|
Printf("\\x%02x", Byte);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) {
|
||||||
|
|
||||||
|
for (size_t i = 0; i < Size; i++)
|
||||||
|
PrintASCIIByte(Data[i]);
|
||||||
|
Printf("%s", PrintAfter);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintASCII(const Unit &U, const char *PrintAfter) {
|
||||||
|
|
||||||
|
PrintASCII(U.data(), U.size(), PrintAfter);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToASCII(uint8_t *Data, size_t Size) {
|
||||||
|
|
||||||
|
bool Changed = false;
|
||||||
|
for (size_t i = 0; i < Size; i++) {
|
||||||
|
|
||||||
|
uint8_t &X = Data[i];
|
||||||
|
auto NewX = X;
|
||||||
|
NewX &= 127;
|
||||||
|
if (!isspace(NewX) && !isprint(NewX)) NewX = ' ';
|
||||||
|
Changed |= NewX != X;
|
||||||
|
X = NewX;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Changed;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsASCII(const Unit &U) {
|
||||||
|
|
||||||
|
return IsASCII(U.data(), U.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsASCII(const uint8_t *Data, size_t Size) {
|
||||||
|
|
||||||
|
for (size_t i = 0; i < Size; i++)
|
||||||
|
if (!(isprint(Data[i]) || isspace(Data[i]))) return false;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) {
|
||||||
|
|
||||||
|
U->clear();
|
||||||
|
if (Str.empty()) return false;
|
||||||
|
size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R].
|
||||||
|
// Skip spaces from both sides.
|
||||||
|
while (L < R && isspace(Str[L]))
|
||||||
|
L++;
|
||||||
|
while (R > L && isspace(Str[R]))
|
||||||
|
R--;
|
||||||
|
if (R - L < 2) return false;
|
||||||
|
// Check the closing "
|
||||||
|
if (Str[R] != '"') return false;
|
||||||
|
R--;
|
||||||
|
// Find the opening "
|
||||||
|
while (L < R && Str[L] != '"')
|
||||||
|
L++;
|
||||||
|
if (L >= R) return false;
|
||||||
|
assert(Str[L] == '\"');
|
||||||
|
L++;
|
||||||
|
assert(L <= R);
|
||||||
|
for (size_t Pos = L; Pos <= R; Pos++) {
|
||||||
|
|
||||||
|
uint8_t V = (uint8_t)Str[Pos];
|
||||||
|
if (!isprint(V) && !isspace(V)) return false;
|
||||||
|
if (V == '\\') {
|
||||||
|
|
||||||
|
// Handle '\\'
|
||||||
|
if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) {
|
||||||
|
|
||||||
|
U->push_back(Str[Pos + 1]);
|
||||||
|
Pos++;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle '\xAB'
|
||||||
|
if (Pos + 3 <= R && Str[Pos + 1] == 'x' && isxdigit(Str[Pos + 2]) &&
|
||||||
|
isxdigit(Str[Pos + 3])) {
|
||||||
|
|
||||||
|
char Hex[] = "0xAA";
|
||||||
|
Hex[2] = Str[Pos + 2];
|
||||||
|
Hex[3] = Str[Pos + 3];
|
||||||
|
U->push_back(strtol(Hex, nullptr, 16));
|
||||||
|
Pos += 3;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // Invalid escape.
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Any other character.
|
||||||
|
U->push_back(V);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units) {
|
||||||
|
|
||||||
|
if (Text.empty()) {
|
||||||
|
|
||||||
|
Printf("ParseDictionaryFile: file does not exist or is empty\n");
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istringstream ISS(Text);
|
||||||
|
Units->clear();
|
||||||
|
Unit U;
|
||||||
|
int LineNo = 0;
|
||||||
|
std::string S;
|
||||||
|
while (std::getline(ISS, S, '\n')) {
|
||||||
|
|
||||||
|
LineNo++;
|
||||||
|
size_t Pos = 0;
|
||||||
|
while (Pos < S.size() && isspace(S[Pos]))
|
||||||
|
Pos++; // Skip spaces.
|
||||||
|
if (Pos == S.size()) continue; // Empty line.
|
||||||
|
if (S[Pos] == '#') continue; // Comment line.
|
||||||
|
if (ParseOneDictionaryEntry(S, &U)) {
|
||||||
|
|
||||||
|
Units->push_back(U);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo,
|
||||||
|
S.c_str());
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code duplicated (and tested) in llvm/include/llvm/Support/Base64.h
|
||||||
|
std::string Base64(const Unit &U) {
|
||||||
|
|
||||||
|
static const char Table[] =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
|
"0123456789+/";
|
||||||
|
std::string Buffer;
|
||||||
|
Buffer.resize(((U.size() + 2) / 3) * 4);
|
||||||
|
|
||||||
|
size_t i = 0, j = 0;
|
||||||
|
for (size_t n = U.size() / 3 * 3; i < n; i += 3, j += 4) {
|
||||||
|
|
||||||
|
uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8) |
|
||||||
|
(unsigned char)U[i + 2];
|
||||||
|
Buffer[j + 0] = Table[(x >> 18) & 63];
|
||||||
|
Buffer[j + 1] = Table[(x >> 12) & 63];
|
||||||
|
Buffer[j + 2] = Table[(x >> 6) & 63];
|
||||||
|
Buffer[j + 3] = Table[x & 63];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 1 == U.size()) {
|
||||||
|
|
||||||
|
uint32_t x = ((unsigned char)U[i] << 16);
|
||||||
|
Buffer[j + 0] = Table[(x >> 18) & 63];
|
||||||
|
Buffer[j + 1] = Table[(x >> 12) & 63];
|
||||||
|
Buffer[j + 2] = '=';
|
||||||
|
Buffer[j + 3] = '=';
|
||||||
|
|
||||||
|
} else if (i + 2 == U.size()) {
|
||||||
|
|
||||||
|
uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8);
|
||||||
|
Buffer[j + 0] = Table[(x >> 18) & 63];
|
||||||
|
Buffer[j + 1] = Table[(x >> 12) & 63];
|
||||||
|
Buffer[j + 2] = Table[(x >> 6) & 63];
|
||||||
|
Buffer[j + 3] = '=';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Buffer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::mutex SymbolizeMutex;
|
||||||
|
|
||||||
|
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) {
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
|
||||||
|
if (!EF->__sanitizer_symbolize_pc || !l.owns_lock())
|
||||||
|
return "<can not symbolize>";
|
||||||
|
char PcDescr[1024] = {};
|
||||||
|
EF->__sanitizer_symbolize_pc(reinterpret_cast<void *>(PC), SymbolizedFMT,
|
||||||
|
PcDescr, sizeof(PcDescr));
|
||||||
|
PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case.
|
||||||
|
return PcDescr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) {
|
||||||
|
|
||||||
|
if (EF->__sanitizer_symbolize_pc)
|
||||||
|
Printf("%s", DescribePC(SymbolizedFMT, PC).c_str());
|
||||||
|
else
|
||||||
|
Printf(FallbackFMT, PC);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintStackTrace() {
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
|
||||||
|
if (EF->__sanitizer_print_stack_trace && l.owns_lock())
|
||||||
|
EF->__sanitizer_print_stack_trace();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintMemoryProfile() {
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
|
||||||
|
if (EF->__sanitizer_print_memory_profile && l.owns_lock())
|
||||||
|
EF->__sanitizer_print_memory_profile(95, 8);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned NumberOfCpuCores() {
|
||||||
|
|
||||||
|
unsigned N = std::thread::hardware_concurrency();
|
||||||
|
if (!N) {
|
||||||
|
|
||||||
|
Printf(
|
||||||
|
"WARNING: std::thread::hardware_concurrency not well defined for "
|
||||||
|
"your platform. Assuming CPU count of 1.\n");
|
||||||
|
N = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return N;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SimpleFastHash(const uint8_t *Data, size_t Size) {
|
||||||
|
|
||||||
|
size_t Res = 0;
|
||||||
|
for (size_t i = 0; i < Size; i++)
|
||||||
|
Res = Res * 11 + Data[i];
|
||||||
|
return Res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
117
custom_mutators/libfuzzer/FuzzerUtil.h
Normal file
117
custom_mutators/libfuzzer/FuzzerUtil.h
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Util functions.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_UTIL_H
|
||||||
|
#define LLVM_FUZZER_UTIL_H
|
||||||
|
|
||||||
|
#include "FuzzerBuiltins.h"
|
||||||
|
#include "FuzzerBuiltinsMsvc.h"
|
||||||
|
#include "FuzzerCommand.h"
|
||||||
|
#include "FuzzerDefs.h"
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
void PrintHexArray(const Unit &U, const char *PrintAfter = "");
|
||||||
|
|
||||||
|
void PrintHexArray(const uint8_t *Data, size_t Size,
|
||||||
|
const char *PrintAfter = "");
|
||||||
|
|
||||||
|
void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = "");
|
||||||
|
|
||||||
|
void PrintASCII(const Unit &U, const char *PrintAfter = "");
|
||||||
|
|
||||||
|
// Changes U to contain only ASCII (isprint+isspace) characters.
|
||||||
|
// Returns true iff U has been changed.
|
||||||
|
bool ToASCII(uint8_t *Data, size_t Size);
|
||||||
|
|
||||||
|
bool IsASCII(const Unit &U);
|
||||||
|
|
||||||
|
bool IsASCII(const uint8_t *Data, size_t Size);
|
||||||
|
|
||||||
|
std::string Base64(const Unit &U);
|
||||||
|
|
||||||
|
void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC);
|
||||||
|
|
||||||
|
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC);
|
||||||
|
|
||||||
|
void PrintStackTrace();
|
||||||
|
|
||||||
|
void PrintMemoryProfile();
|
||||||
|
|
||||||
|
unsigned NumberOfCpuCores();
|
||||||
|
|
||||||
|
// Platform specific functions.
|
||||||
|
void SetSignalHandler(const FuzzingOptions& Options);
|
||||||
|
|
||||||
|
void SleepSeconds(int Seconds);
|
||||||
|
|
||||||
|
unsigned long GetPid();
|
||||||
|
|
||||||
|
size_t GetPeakRSSMb();
|
||||||
|
|
||||||
|
int ExecuteCommand(const Command &Cmd);
|
||||||
|
bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput);
|
||||||
|
|
||||||
|
// Fuchsia does not have popen/pclose.
|
||||||
|
FILE *OpenProcessPipe(const char *Command, const char *Mode);
|
||||||
|
int CloseProcessPipe(FILE *F);
|
||||||
|
|
||||||
|
const void *SearchMemory(const void *haystack, size_t haystacklen,
|
||||||
|
const void *needle, size_t needlelen);
|
||||||
|
|
||||||
|
std::string CloneArgsWithoutX(const Vector<std::string> &Args,
|
||||||
|
const char *X1, const char *X2);
|
||||||
|
|
||||||
|
inline std::string CloneArgsWithoutX(const Vector<std::string> &Args,
|
||||||
|
const char *X) {
|
||||||
|
return CloneArgsWithoutX(Args, X, X);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::pair<std::string, std::string> SplitBefore(std::string X,
|
||||||
|
std::string S) {
|
||||||
|
auto Pos = S.find(X);
|
||||||
|
if (Pos == std::string::npos)
|
||||||
|
return std::make_pair(S, "");
|
||||||
|
return std::make_pair(S.substr(0, Pos), S.substr(Pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiscardOutput(int Fd);
|
||||||
|
|
||||||
|
std::string DisassembleCmd(const std::string &FileName);
|
||||||
|
|
||||||
|
std::string SearchRegexCmd(const std::string &Regex);
|
||||||
|
|
||||||
|
size_t SimpleFastHash(const uint8_t *Data, size_t Size);
|
||||||
|
|
||||||
|
inline uint32_t Log(uint32_t X) { return 32 - Clz(X) - 1; }
|
||||||
|
|
||||||
|
inline size_t PageSize() { return 4096; }
|
||||||
|
inline uint8_t *RoundUpByPage(uint8_t *P) {
|
||||||
|
uintptr_t X = reinterpret_cast<uintptr_t>(P);
|
||||||
|
size_t Mask = PageSize() - 1;
|
||||||
|
X = (X + Mask) & ~Mask;
|
||||||
|
return reinterpret_cast<uint8_t *>(X);
|
||||||
|
}
|
||||||
|
inline uint8_t *RoundDownByPage(uint8_t *P) {
|
||||||
|
uintptr_t X = reinterpret_cast<uintptr_t>(P);
|
||||||
|
size_t Mask = PageSize() - 1;
|
||||||
|
X = X & ~Mask;
|
||||||
|
return reinterpret_cast<uint8_t *>(X);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
template <typename T> T HostToLE(T X) { return X; }
|
||||||
|
#else
|
||||||
|
template <typename T> T HostToLE(T X) { return Bswap(X); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_UTIL_H
|
205
custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp
Normal file
205
custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Misc utils for Darwin.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#if LIBFUZZER_APPLE
|
||||||
|
#include "FuzzerCommand.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include <mutex>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <spawn.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// There is no header for this on macOS so declare here
|
||||||
|
extern "C" char **environ;
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
static std::mutex SignalMutex;
|
||||||
|
// Global variables used to keep track of how signal handling should be
|
||||||
|
// restored. They should **not** be accessed without holding `SignalMutex`.
|
||||||
|
static int ActiveThreadCount = 0;
|
||||||
|
static struct sigaction OldSigIntAction;
|
||||||
|
static struct sigaction OldSigQuitAction;
|
||||||
|
static sigset_t OldBlockedSignalsSet;
|
||||||
|
|
||||||
|
// This is a reimplementation of Libc's `system()`. On Darwin the Libc
|
||||||
|
// implementation contains a mutex which prevents it from being used
|
||||||
|
// concurrently. This implementation **can** be used concurrently. It sets the
|
||||||
|
// signal handlers when the first thread enters and restores them when the last
|
||||||
|
// thread finishes execution of the function and ensures this is not racey by
|
||||||
|
// using a mutex.
|
||||||
|
int ExecuteCommand(const Command &Cmd) {
|
||||||
|
|
||||||
|
std::string CmdLine = Cmd.toString();
|
||||||
|
posix_spawnattr_t SpawnAttributes;
|
||||||
|
if (posix_spawnattr_init(&SpawnAttributes)) return -1;
|
||||||
|
// Block and ignore signals of the current process when the first thread
|
||||||
|
// enters.
|
||||||
|
{
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> Lock(SignalMutex);
|
||||||
|
if (ActiveThreadCount == 0) {
|
||||||
|
|
||||||
|
static struct sigaction IgnoreSignalAction;
|
||||||
|
sigset_t BlockedSignalsSet;
|
||||||
|
memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));
|
||||||
|
IgnoreSignalAction.sa_handler = SIG_IGN;
|
||||||
|
|
||||||
|
if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {
|
||||||
|
|
||||||
|
Printf("Failed to ignore SIGINT\n");
|
||||||
|
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {
|
||||||
|
|
||||||
|
Printf("Failed to ignore SIGQUIT\n");
|
||||||
|
// Try our best to restore the signal handlers.
|
||||||
|
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
|
||||||
|
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)sigemptyset(&BlockedSignalsSet);
|
||||||
|
(void)sigaddset(&BlockedSignalsSet, SIGCHLD);
|
||||||
|
if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==
|
||||||
|
-1) {
|
||||||
|
|
||||||
|
Printf("Failed to block SIGCHLD\n");
|
||||||
|
// Try our best to restore the signal handlers.
|
||||||
|
(void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);
|
||||||
|
(void)sigaction(SIGINT, &OldSigIntAction, NULL);
|
||||||
|
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
++ActiveThreadCount;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Do not introduce any new `return` statements past this
|
||||||
|
// point. It is important that `ActiveThreadCount` always be decremented
|
||||||
|
// when leaving this function.
|
||||||
|
|
||||||
|
// Make sure the child process uses the default handlers for the
|
||||||
|
// following signals rather than inheriting what the parent has.
|
||||||
|
sigset_t DefaultSigSet;
|
||||||
|
(void)sigemptyset(&DefaultSigSet);
|
||||||
|
(void)sigaddset(&DefaultSigSet, SIGQUIT);
|
||||||
|
(void)sigaddset(&DefaultSigSet, SIGINT);
|
||||||
|
(void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet);
|
||||||
|
// Make sure the child process doesn't block SIGCHLD
|
||||||
|
(void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);
|
||||||
|
short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
|
||||||
|
(void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);
|
||||||
|
|
||||||
|
pid_t Pid;
|
||||||
|
char ** Environ = environ; // Read from global
|
||||||
|
const char *CommandCStr = CmdLine.c_str();
|
||||||
|
char *const Argv[] = {strdup("sh"), strdup("-c"), strdup(CommandCStr), NULL};
|
||||||
|
int ErrorCode = 0, ProcessStatus = 0;
|
||||||
|
// FIXME: We probably shouldn't hardcode the shell path.
|
||||||
|
ErrorCode =
|
||||||
|
posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes, Argv, Environ);
|
||||||
|
(void)posix_spawnattr_destroy(&SpawnAttributes);
|
||||||
|
if (!ErrorCode) {
|
||||||
|
|
||||||
|
pid_t SavedPid = Pid;
|
||||||
|
do {
|
||||||
|
|
||||||
|
// Repeat until call completes uninterrupted.
|
||||||
|
Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);
|
||||||
|
|
||||||
|
} while (Pid == -1 && errno == EINTR);
|
||||||
|
|
||||||
|
if (Pid == -1) {
|
||||||
|
|
||||||
|
// Fail for some other reason.
|
||||||
|
ProcessStatus = -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {
|
||||||
|
|
||||||
|
// Fork failure.
|
||||||
|
ProcessStatus = -1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Shell execution failure.
|
||||||
|
ProcessStatus = W_EXITCODE(127, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i)
|
||||||
|
free(Argv[i]);
|
||||||
|
|
||||||
|
// Restore the signal handlers of the current process when the last thread
|
||||||
|
// using this function finishes.
|
||||||
|
{
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> Lock(SignalMutex);
|
||||||
|
--ActiveThreadCount;
|
||||||
|
if (ActiveThreadCount == 0) {
|
||||||
|
|
||||||
|
bool FailedRestore = false;
|
||||||
|
if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {
|
||||||
|
|
||||||
|
Printf("Failed to restore SIGINT handling\n");
|
||||||
|
FailedRestore = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {
|
||||||
|
|
||||||
|
Printf("Failed to restore SIGQUIT handling\n");
|
||||||
|
FailedRestore = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {
|
||||||
|
|
||||||
|
Printf("Failed to unblock SIGCHLD\n");
|
||||||
|
FailedRestore = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FailedRestore) ProcessStatus = -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProcessStatus;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiscardOutput(int Fd) {
|
||||||
|
|
||||||
|
FILE *Temp = fopen("/dev/null", "w");
|
||||||
|
if (!Temp) return;
|
||||||
|
dup2(fileno(Temp), Fd);
|
||||||
|
fclose(Temp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LIBFUZZER_APPLE
|
||||||
|
|
658
custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp
Normal file
658
custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp
Normal file
@ -0,0 +1,658 @@
|
|||||||
|
//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Misc utils implementation using Fuchsia/Zircon APIs.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
|
||||||
|
#if LIBFUZZER_FUCHSIA
|
||||||
|
|
||||||
|
#include "FuzzerInternal.h"
|
||||||
|
#include "FuzzerUtil.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <lib/fdio/fdio.h>
|
||||||
|
#include <lib/fdio/spawn.h>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <zircon/errors.h>
|
||||||
|
#include <zircon/process.h>
|
||||||
|
#include <zircon/sanitizer.h>
|
||||||
|
#include <zircon/status.h>
|
||||||
|
#include <zircon/syscalls.h>
|
||||||
|
#include <zircon/syscalls/debug.h>
|
||||||
|
#include <zircon/syscalls/exception.h>
|
||||||
|
#include <zircon/syscalls/object.h>
|
||||||
|
#include <zircon/types.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
// Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written
|
||||||
|
// around, the general approach is to spin up dedicated threads to watch for
|
||||||
|
// each requested condition (alarm, interrupt, crash). Of these, the crash
|
||||||
|
// handler is the most involved, as it requires resuming the crashed thread in
|
||||||
|
// order to invoke the sanitizers to get the needed state.
|
||||||
|
|
||||||
|
// Forward declaration of assembly trampoline needed to resume crashed threads.
|
||||||
|
// This appears to have external linkage to C++, which is why it's not in the
|
||||||
|
// anonymous namespace. The assembly definition inside MakeTrampoline()
|
||||||
|
// actually defines the symbol with internal linkage only.
|
||||||
|
void CrashTrampolineAsm() __asm__("CrashTrampolineAsm");
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Helper function to handle Zircon syscall failures.
|
||||||
|
void ExitOnErr(zx_status_t Status, const char *Syscall) {
|
||||||
|
|
||||||
|
if (Status != ZX_OK) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: %s failed: %s\n", Syscall,
|
||||||
|
_zx_status_get_string(Status));
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmHandler(int Seconds) {
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
SleepSeconds(Seconds);
|
||||||
|
Fuzzer::StaticAlarmCallback();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterruptHandler() {
|
||||||
|
|
||||||
|
fd_set readfds;
|
||||||
|
// Ctrl-C sends ETX in Zircon.
|
||||||
|
do {
|
||||||
|
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(STDIN_FILENO, &readfds);
|
||||||
|
select(STDIN_FILENO + 1, &readfds, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
} while (!FD_ISSET(STDIN_FILENO, &readfds) || getchar() != 0x03);
|
||||||
|
|
||||||
|
Fuzzer::StaticInterruptCallback();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// CFAOffset is used to reference the stack pointer before entering the
|
||||||
|
// trampoline (Stack Pointer + CFAOffset = prev Stack Pointer). Before jumping
|
||||||
|
// to the trampoline we copy all the registers onto the stack. We need to make
|
||||||
|
// sure that the new stack has enough space to store all the registers.
|
||||||
|
//
|
||||||
|
// The trampoline holds CFI information regarding the registers stored in the
|
||||||
|
// stack, which is then used by the unwinder to restore them.
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
// In x86_64 the crashing function might also be using the red zone (128 bytes
|
||||||
|
// on top of their rsp).
|
||||||
|
constexpr size_t CFAOffset = 128 + sizeof(zx_thread_state_general_regs_t);
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
// In aarch64 we need to always have the stack pointer aligned to 16 bytes, so
|
||||||
|
// we make sure that we are keeping that same alignment.
|
||||||
|
constexpr size_t CFAOffset =
|
||||||
|
(sizeof(zx_thread_state_general_regs_t) + 15) & -(uintptr_t)16;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback
|
||||||
|
// without POSIX signal handlers. To achieve this, we use an assembly
|
||||||
|
// function to add the necessary CFI unwinding information and a C function to
|
||||||
|
// bridge from that back into C++.
|
||||||
|
|
||||||
|
// FIXME: This works as a short-term solution, but this code really shouldn't
|
||||||
|
// be architecture dependent. A better long term solution is to implement
|
||||||
|
// remote unwinding and expose the necessary APIs through sanitizer_common
|
||||||
|
// and/or ASAN to allow the exception handling thread to gather the crash
|
||||||
|
// state directly.
|
||||||
|
//
|
||||||
|
// Alternatively, Fuchsia may in future actually implement basic signal
|
||||||
|
// handling for the machine trap signals.
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
#define FOREACH_REGISTER(OP_REG, OP_NUM) \
|
||||||
|
OP_REG(rax) \
|
||||||
|
OP_REG(rbx) \
|
||||||
|
OP_REG(rcx) \
|
||||||
|
OP_REG(rdx) \
|
||||||
|
OP_REG(rsi) \
|
||||||
|
OP_REG(rdi) \
|
||||||
|
OP_REG(rbp) \
|
||||||
|
OP_REG(rsp) \
|
||||||
|
OP_REG(r8) \
|
||||||
|
OP_REG(r9) \
|
||||||
|
OP_REG(r10) \
|
||||||
|
OP_REG(r11) \
|
||||||
|
OP_REG(r12) \
|
||||||
|
OP_REG(r13) \
|
||||||
|
OP_REG(r14) \
|
||||||
|
OP_REG(r15) \
|
||||||
|
OP_REG(rip)
|
||||||
|
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
#define FOREACH_REGISTER(OP_REG, OP_NUM) \
|
||||||
|
OP_NUM(0) \
|
||||||
|
OP_NUM(1) \
|
||||||
|
OP_NUM(2) \
|
||||||
|
OP_NUM(3) \
|
||||||
|
OP_NUM(4) \
|
||||||
|
OP_NUM(5) \
|
||||||
|
OP_NUM(6) \
|
||||||
|
OP_NUM(7) \
|
||||||
|
OP_NUM(8) \
|
||||||
|
OP_NUM(9) \
|
||||||
|
OP_NUM(10) \
|
||||||
|
OP_NUM(11) \
|
||||||
|
OP_NUM(12) \
|
||||||
|
OP_NUM(13) \
|
||||||
|
OP_NUM(14) \
|
||||||
|
OP_NUM(15) \
|
||||||
|
OP_NUM(16) \
|
||||||
|
OP_NUM(17) \
|
||||||
|
OP_NUM(18) \
|
||||||
|
OP_NUM(19) \
|
||||||
|
OP_NUM(20) \
|
||||||
|
OP_NUM(21) \
|
||||||
|
OP_NUM(22) \
|
||||||
|
OP_NUM(23) \
|
||||||
|
OP_NUM(24) \
|
||||||
|
OP_NUM(25) \
|
||||||
|
OP_NUM(26) \
|
||||||
|
OP_NUM(27) \
|
||||||
|
OP_NUM(28) \
|
||||||
|
OP_NUM(29) \
|
||||||
|
OP_REG(sp)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Unsupported architecture for fuzzing on Fuchsia"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Produces a CFI directive for the named or numbered register.
|
||||||
|
// The value used refers to an assembler immediate operand with the same name
|
||||||
|
// as the register (see ASM_OPERAND_REG).
|
||||||
|
#define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n"
|
||||||
|
#define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(x##num)
|
||||||
|
|
||||||
|
// Produces an assembler immediate operand for the named or numbered register.
|
||||||
|
// This operand contains the offset of the register relative to the CFA.
|
||||||
|
#define ASM_OPERAND_REG(reg) \
|
||||||
|
[reg] "i"(offsetof(zx_thread_state_general_regs_t, reg) - CFAOffset),
|
||||||
|
#define ASM_OPERAND_NUM(num) \
|
||||||
|
[x##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num]) - CFAOffset),
|
||||||
|
|
||||||
|
// Trampoline to bridge from the assembly below to the static C++ crash
|
||||||
|
// callback.
|
||||||
|
__attribute__((noreturn)) static void StaticCrashHandler() {
|
||||||
|
|
||||||
|
Fuzzer::StaticCrashSignalCallback();
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
_Exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the trampoline with the necessary CFI information to unwind through
|
||||||
|
// to the crashing call stack:
|
||||||
|
// * Defining the CFA so that it points to the stack pointer at the point
|
||||||
|
// of crash.
|
||||||
|
// * Storing all registers at the point of crash in the stack and refer to them
|
||||||
|
// via CFI information (relative to the CFA).
|
||||||
|
// * Setting the return column so the unwinder knows how to continue unwinding.
|
||||||
|
// * (x86_64) making sure rsp is aligned before calling StaticCrashHandler.
|
||||||
|
// * Calling StaticCrashHandler that will trigger the unwinder.
|
||||||
|
//
|
||||||
|
// The __attribute__((used)) is necessary because the function
|
||||||
|
// is never called; it's just a container around the assembly to allow it to
|
||||||
|
// use operands for compile-time computed constants.
|
||||||
|
__attribute__((used)) void MakeTrampoline() {
|
||||||
|
|
||||||
|
__asm__(".cfi_endproc\n"
|
||||||
|
".pushsection .text.CrashTrampolineAsm\n"
|
||||||
|
".type CrashTrampolineAsm,STT_FUNC\n"
|
||||||
|
"CrashTrampolineAsm:\n"
|
||||||
|
".cfi_startproc simple\n"
|
||||||
|
".cfi_signal_frame\n"
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
".cfi_return_column rip\n"
|
||||||
|
".cfi_def_cfa rsp, %c[CFAOffset]\n"
|
||||||
|
FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)
|
||||||
|
"mov %%rsp, %%rbp\n"
|
||||||
|
".cfi_def_cfa_register rbp\n"
|
||||||
|
"andq $-16, %%rsp\n"
|
||||||
|
"call %c[StaticCrashHandler]\n"
|
||||||
|
"ud2\n"
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
".cfi_return_column 33\n"
|
||||||
|
".cfi_def_cfa sp, %c[CFAOffset]\n"
|
||||||
|
FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)
|
||||||
|
".cfi_offset 33, %c[pc]\n"
|
||||||
|
".cfi_offset 30, %c[lr]\n"
|
||||||
|
"bl %c[StaticCrashHandler]\n"
|
||||||
|
"brk 1\n"
|
||||||
|
#else
|
||||||
|
#error "Unsupported architecture for fuzzing on Fuchsia"
|
||||||
|
#endif
|
||||||
|
".cfi_endproc\n"
|
||||||
|
".size CrashTrampolineAsm, . - CrashTrampolineAsm\n"
|
||||||
|
".popsection\n"
|
||||||
|
".cfi_startproc\n"
|
||||||
|
: // No outputs
|
||||||
|
: FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM)
|
||||||
|
#if defined(__aarch64__)
|
||||||
|
ASM_OPERAND_REG(pc)
|
||||||
|
ASM_OPERAND_REG(lr)
|
||||||
|
#endif
|
||||||
|
[StaticCrashHandler] "i" (StaticCrashHandler),
|
||||||
|
[CFAOffset] "i" (CFAOffset));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CrashHandler(zx_handle_t *Event) {
|
||||||
|
|
||||||
|
// This structure is used to ensure we close handles to objects we create in
|
||||||
|
// this handler.
|
||||||
|
struct ScopedHandle {
|
||||||
|
|
||||||
|
~ScopedHandle() {
|
||||||
|
|
||||||
|
_zx_handle_close(Handle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
zx_handle_t Handle = ZX_HANDLE_INVALID;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the exception channel. We need to claim to be a "debugger" so the
|
||||||
|
// kernel will allow us to modify and resume dying threads (see below). Once
|
||||||
|
// the channel is set, we can signal the main thread to continue and wait
|
||||||
|
// for the exception to arrive.
|
||||||
|
ScopedHandle Channel;
|
||||||
|
zx_handle_t Self = _zx_process_self();
|
||||||
|
ExitOnErr(_zx_task_create_exception_channel(
|
||||||
|
Self, ZX_EXCEPTION_CHANNEL_DEBUGGER, &Channel.Handle),
|
||||||
|
"_zx_task_create_exception_channel");
|
||||||
|
|
||||||
|
ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0),
|
||||||
|
"_zx_object_signal");
|
||||||
|
|
||||||
|
// This thread lives as long as the process in order to keep handling
|
||||||
|
// crashes. In practice, the first crashed thread to reach the end of the
|
||||||
|
// StaticCrashHandler will end the process.
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
ExitOnErr(_zx_object_wait_one(Channel.Handle, ZX_CHANNEL_READABLE,
|
||||||
|
ZX_TIME_INFINITE, nullptr),
|
||||||
|
"_zx_object_wait_one");
|
||||||
|
|
||||||
|
zx_exception_info_t ExceptionInfo;
|
||||||
|
ScopedHandle Exception;
|
||||||
|
ExitOnErr(
|
||||||
|
_zx_channel_read(Channel.Handle, 0, &ExceptionInfo, &Exception.Handle,
|
||||||
|
sizeof(ExceptionInfo), 1, nullptr, nullptr),
|
||||||
|
"_zx_channel_read");
|
||||||
|
|
||||||
|
// Ignore informational synthetic exceptions.
|
||||||
|
if (ZX_EXCP_THREAD_STARTING == ExceptionInfo.type ||
|
||||||
|
ZX_EXCP_THREAD_EXITING == ExceptionInfo.type ||
|
||||||
|
ZX_EXCP_PROCESS_STARTING == ExceptionInfo.type) {
|
||||||
|
|
||||||
|
continue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we want to get the state of the crashing thread, but
|
||||||
|
// libFuzzer and the sanitizers assume this will happen from that same
|
||||||
|
// thread via a POSIX signal handler. "Resurrecting" the thread in the
|
||||||
|
// middle of the appropriate callback is as simple as forcibly setting the
|
||||||
|
// instruction pointer/program counter, provided we NEVER EVER return from
|
||||||
|
// that function (since otherwise our stack will not be valid).
|
||||||
|
ScopedHandle Thread;
|
||||||
|
ExitOnErr(_zx_exception_get_thread(Exception.Handle, &Thread.Handle),
|
||||||
|
"_zx_exception_get_thread");
|
||||||
|
|
||||||
|
zx_thread_state_general_regs_t GeneralRegisters;
|
||||||
|
ExitOnErr(
|
||||||
|
_zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,
|
||||||
|
&GeneralRegisters, sizeof(GeneralRegisters)),
|
||||||
|
"_zx_thread_read_state");
|
||||||
|
|
||||||
|
// To unwind properly, we need to push the crashing thread's register state
|
||||||
|
// onto the stack and jump into a trampoline with CFI instructions on how
|
||||||
|
// to restore it.
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
uintptr_t StackPtr = GeneralRegisters.rsp - CFAOffset;
|
||||||
|
__unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,
|
||||||
|
sizeof(GeneralRegisters));
|
||||||
|
GeneralRegisters.rsp = StackPtr;
|
||||||
|
GeneralRegisters.rip = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);
|
||||||
|
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
uintptr_t StackPtr = GeneralRegisters.sp - CFAOffset;
|
||||||
|
__unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,
|
||||||
|
sizeof(GeneralRegisters));
|
||||||
|
GeneralRegisters.sp = StackPtr;
|
||||||
|
GeneralRegisters.pc = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Unsupported architecture for fuzzing on Fuchsia"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Now force the crashing thread's state.
|
||||||
|
ExitOnErr(
|
||||||
|
_zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,
|
||||||
|
&GeneralRegisters, sizeof(GeneralRegisters)),
|
||||||
|
"_zx_thread_write_state");
|
||||||
|
|
||||||
|
// Set the exception to HANDLED so it resumes the thread on close.
|
||||||
|
uint32_t ExceptionState = ZX_EXCEPTION_STATE_HANDLED;
|
||||||
|
ExitOnErr(_zx_object_set_property(Exception.Handle, ZX_PROP_EXCEPTION_STATE,
|
||||||
|
&ExceptionState, sizeof(ExceptionState)),
|
||||||
|
"zx_object_set_property");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Platform specific functions.
|
||||||
|
void SetSignalHandler(const FuzzingOptions &Options) {
|
||||||
|
|
||||||
|
// Make sure information from libFuzzer and the sanitizers are easy to
|
||||||
|
// reassemble. `__sanitizer_log_write` has the added benefit of ensuring the
|
||||||
|
// DSO map is always available for the symbolizer.
|
||||||
|
// A uint64_t fits in 20 chars, so 64 is plenty.
|
||||||
|
char Buf[64];
|
||||||
|
memset(Buf, 0, sizeof(Buf));
|
||||||
|
snprintf(Buf, sizeof(Buf), "==%lu== INFO: libFuzzer starting.\n", GetPid());
|
||||||
|
if (EF->__sanitizer_log_write) __sanitizer_log_write(Buf, sizeof(Buf));
|
||||||
|
Printf("%s", Buf);
|
||||||
|
|
||||||
|
// Set up alarm handler if needed.
|
||||||
|
if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) {
|
||||||
|
|
||||||
|
std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1);
|
||||||
|
T.detach();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up interrupt handler if needed.
|
||||||
|
if (Options.HandleInt || Options.HandleTerm) {
|
||||||
|
|
||||||
|
std::thread T(InterruptHandler);
|
||||||
|
T.detach();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Early exit if no crash handler needed.
|
||||||
|
if (!Options.HandleSegv && !Options.HandleBus && !Options.HandleIll &&
|
||||||
|
!Options.HandleFpe && !Options.HandleAbrt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Set up the crash handler and wait until it is ready before proceeding.
|
||||||
|
zx_handle_t Event;
|
||||||
|
ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create");
|
||||||
|
|
||||||
|
std::thread T(CrashHandler, &Event);
|
||||||
|
zx_status_t Status =
|
||||||
|
_zx_object_wait_one(Event, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr);
|
||||||
|
_zx_handle_close(Event);
|
||||||
|
ExitOnErr(Status, "_zx_object_wait_one");
|
||||||
|
|
||||||
|
T.detach();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SleepSeconds(int Seconds) {
|
||||||
|
|
||||||
|
_zx_nanosleep(_zx_deadline_after(ZX_SEC(Seconds)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long GetPid() {
|
||||||
|
|
||||||
|
zx_status_t rc;
|
||||||
|
zx_info_handle_basic_t Info;
|
||||||
|
if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,
|
||||||
|
sizeof(Info), NULL, NULL)) != ZX_OK) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: unable to get info about self: %s\n",
|
||||||
|
_zx_status_get_string(rc));
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Info.koid;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetPeakRSSMb() {
|
||||||
|
|
||||||
|
zx_status_t rc;
|
||||||
|
zx_info_task_stats_t Info;
|
||||||
|
if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info,
|
||||||
|
sizeof(Info), NULL, NULL)) != ZX_OK) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: unable to get info about self: %s\n",
|
||||||
|
_zx_status_get_string(rc));
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Info.mem_private_bytes + Info.mem_shared_bytes) >> 20;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fn>
|
||||||
|
class RunOnDestruction {
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RunOnDestruction(Fn fn) : fn_(fn) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~RunOnDestruction() {
|
||||||
|
|
||||||
|
fn_();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Fn fn_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Fn>
|
||||||
|
RunOnDestruction<Fn> at_scope_exit(Fn fn) {
|
||||||
|
|
||||||
|
return RunOnDestruction<Fn>(fn);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static fdio_spawn_action_t clone_fd_action(int localFd, int targetFd) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
.action = FDIO_SPAWN_ACTION_CLONE_FD,
|
||||||
|
.fd =
|
||||||
|
{
|
||||||
|
|
||||||
|
.local_fd = localFd,
|
||||||
|
.target_fd = targetFd,
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int ExecuteCommand(const Command &Cmd) {
|
||||||
|
|
||||||
|
zx_status_t rc;
|
||||||
|
|
||||||
|
// Convert arguments to C array
|
||||||
|
auto Args = Cmd.getArguments();
|
||||||
|
size_t Argc = Args.size();
|
||||||
|
assert(Argc != 0);
|
||||||
|
std::unique_ptr<const char *[]> Argv(new const char *[Argc + 1]);
|
||||||
|
for (size_t i = 0; i < Argc; ++i)
|
||||||
|
Argv[i] = Args[i].c_str();
|
||||||
|
Argv[Argc] = nullptr;
|
||||||
|
|
||||||
|
// Determine output. On Fuchsia, the fuzzer is typically run as a component
|
||||||
|
// that lacks a mutable working directory. Fortunately, when this is the case
|
||||||
|
// a mutable output directory must be specified using "-artifact_prefix=...",
|
||||||
|
// so write the log file(s) there.
|
||||||
|
// However, we don't want to apply this logic for absolute paths.
|
||||||
|
int FdOut = STDOUT_FILENO;
|
||||||
|
bool discardStdout = false;
|
||||||
|
bool discardStderr = false;
|
||||||
|
|
||||||
|
if (Cmd.hasOutputFile()) {
|
||||||
|
|
||||||
|
std::string Path = Cmd.getOutputFile();
|
||||||
|
if (Path == getDevNull()) {
|
||||||
|
|
||||||
|
// On Fuchsia, there's no "/dev/null" like-file, so we
|
||||||
|
// just don't copy the FDs into the spawned process.
|
||||||
|
discardStdout = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
bool IsAbsolutePath = Path.length() > 1 && Path[0] == '/';
|
||||||
|
if (!IsAbsolutePath && Cmd.hasFlag("artifact_prefix"))
|
||||||
|
Path = Cmd.getFlagValue("artifact_prefix") + "/" + Path;
|
||||||
|
|
||||||
|
FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);
|
||||||
|
if (FdOut == -1) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(),
|
||||||
|
strerror(errno));
|
||||||
|
return ZX_ERR_IO;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CloseFdOut = at_scope_exit([FdOut]() {
|
||||||
|
|
||||||
|
if (FdOut != STDOUT_FILENO) close(FdOut);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Determine stderr
|
||||||
|
int FdErr = STDERR_FILENO;
|
||||||
|
if (Cmd.isOutAndErrCombined()) {
|
||||||
|
|
||||||
|
FdErr = FdOut;
|
||||||
|
if (discardStdout) discardStderr = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone the file descriptors into the new process
|
||||||
|
std::vector<fdio_spawn_action_t> SpawnActions;
|
||||||
|
SpawnActions.push_back(clone_fd_action(STDIN_FILENO, STDIN_FILENO));
|
||||||
|
|
||||||
|
if (!discardStdout)
|
||||||
|
SpawnActions.push_back(clone_fd_action(FdOut, STDOUT_FILENO));
|
||||||
|
if (!discardStderr)
|
||||||
|
SpawnActions.push_back(clone_fd_action(FdErr, STDERR_FILENO));
|
||||||
|
|
||||||
|
// Start the process.
|
||||||
|
char ErrorMsg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
|
||||||
|
zx_handle_t ProcessHandle = ZX_HANDLE_INVALID;
|
||||||
|
rc = fdio_spawn_etc(ZX_HANDLE_INVALID,
|
||||||
|
FDIO_SPAWN_CLONE_ALL & (~FDIO_SPAWN_CLONE_STDIO), Argv[0],
|
||||||
|
Argv.get(), nullptr, SpawnActions.size(),
|
||||||
|
SpawnActions.data(), &ProcessHandle, ErrorMsg);
|
||||||
|
|
||||||
|
if (rc != ZX_OK) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: failed to launch '%s': %s, %s\n", Argv[0], ErrorMsg,
|
||||||
|
_zx_status_get_string(rc));
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CloseHandle = at_scope_exit([&]() { _zx_handle_close(ProcessHandle); });
|
||||||
|
|
||||||
|
// Now join the process and return the exit status.
|
||||||
|
if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED,
|
||||||
|
ZX_TIME_INFINITE, nullptr)) != ZX_OK) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: failed to join '%s': %s\n", Argv[0],
|
||||||
|
_zx_status_get_string(rc));
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
zx_info_process_t Info;
|
||||||
|
if ((rc = _zx_object_get_info(ProcessHandle, ZX_INFO_PROCESS, &Info,
|
||||||
|
sizeof(Info), nullptr, nullptr)) != ZX_OK) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: unable to get return code from '%s': %s\n", Argv[0],
|
||||||
|
_zx_status_get_string(rc));
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return Info.return_code;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExecuteCommand(const Command &BaseCmd, std::string *CmdOutput) {
|
||||||
|
|
||||||
|
auto LogFilePath = TempPath("SimPopenOut", ".txt");
|
||||||
|
Command Cmd(BaseCmd);
|
||||||
|
Cmd.setOutputFile(LogFilePath);
|
||||||
|
int Ret = ExecuteCommand(Cmd);
|
||||||
|
*CmdOutput = FileToString(LogFilePath);
|
||||||
|
RemoveFile(LogFilePath);
|
||||||
|
return Ret == 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||||
|
size_t PattLen) {
|
||||||
|
|
||||||
|
return memmem(Data, DataLen, Patt, PattLen);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// In fuchsia, accessing /dev/null is not supported. There's nothing
|
||||||
|
// similar to a file that discards everything that is written to it.
|
||||||
|
// The way of doing something similar in fuchsia is by using
|
||||||
|
// fdio_null_create and binding that to a file descriptor.
|
||||||
|
void DiscardOutput(int Fd) {
|
||||||
|
|
||||||
|
fdio_t *fdio_null = fdio_null_create();
|
||||||
|
if (fdio_null == nullptr) return;
|
||||||
|
int nullfd = fdio_bind_to_fd(fdio_null, -1, 0);
|
||||||
|
if (nullfd < 0) return;
|
||||||
|
dup2(nullfd, Fd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LIBFUZZER_FUCHSIA
|
||||||
|
|
43
custom_mutators/libfuzzer/FuzzerUtilLinux.cpp
Normal file
43
custom_mutators/libfuzzer/FuzzerUtilLinux.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Misc utils for Linux.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \
|
||||||
|
LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN
|
||||||
|
#include "FuzzerCommand.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
int ExecuteCommand(const Command &Cmd) {
|
||||||
|
|
||||||
|
std::string CmdLine = Cmd.toString();
|
||||||
|
int exit_code = system(CmdLine.c_str());
|
||||||
|
if (WIFEXITED(exit_code)) return WEXITSTATUS(exit_code);
|
||||||
|
return exit_code;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiscardOutput(int Fd) {
|
||||||
|
|
||||||
|
FILE *Temp = fopen("/dev/null", "w");
|
||||||
|
if (!Temp) return;
|
||||||
|
dup2(fileno(Temp), Fd);
|
||||||
|
fclose(Temp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
239
custom_mutators/libfuzzer/FuzzerUtilPosix.cpp
Normal file
239
custom_mutators/libfuzzer/FuzzerUtilPosix.cpp
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Misc utils implementation using Posix API.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#if LIBFUZZER_POSIX
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerInternal.h"
|
||||||
|
#include "FuzzerTracePC.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstring>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
static void AlarmHandler(int, siginfo_t *, void *) {
|
||||||
|
|
||||||
|
Fuzzer::StaticAlarmCallback();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void (*upstream_segv_handler)(int, siginfo_t *, void *);
|
||||||
|
|
||||||
|
static void SegvHandler(int sig, siginfo_t *si, void *ucontext) {
|
||||||
|
|
||||||
|
assert(si->si_signo == SIGSEGV);
|
||||||
|
if (upstream_segv_handler) return upstream_segv_handler(sig, si, ucontext);
|
||||||
|
Fuzzer::StaticCrashSignalCallback();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CrashHandler(int, siginfo_t *, void *) {
|
||||||
|
|
||||||
|
Fuzzer::StaticCrashSignalCallback();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InterruptHandler(int, siginfo_t *, void *) {
|
||||||
|
|
||||||
|
Fuzzer::StaticInterruptCallback();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GracefulExitHandler(int, siginfo_t *, void *) {
|
||||||
|
|
||||||
|
Fuzzer::StaticGracefulExitCallback();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FileSizeExceedHandler(int, siginfo_t *, void *) {
|
||||||
|
|
||||||
|
Fuzzer::StaticFileSizeExceedCallback();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetSigaction(int signum,
|
||||||
|
void (*callback)(int, siginfo_t *, void *)) {
|
||||||
|
|
||||||
|
struct sigaction sigact = {};
|
||||||
|
if (sigaction(signum, nullptr, &sigact)) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: sigaction failed with %d\n", errno);
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sigact.sa_flags & SA_SIGINFO) {
|
||||||
|
|
||||||
|
if (sigact.sa_sigaction) {
|
||||||
|
|
||||||
|
if (signum != SIGSEGV) return;
|
||||||
|
upstream_segv_handler = sigact.sa_sigaction;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (sigact.sa_handler != SIG_DFL && sigact.sa_handler != SIG_IGN &&
|
||||||
|
sigact.sa_handler != SIG_ERR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sigact = {};
|
||||||
|
sigact.sa_flags = SA_SIGINFO;
|
||||||
|
sigact.sa_sigaction = callback;
|
||||||
|
if (sigaction(signum, &sigact, 0)) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: sigaction failed with %d\n", errno);
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true on success, false otherwise.
|
||||||
|
bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) {
|
||||||
|
|
||||||
|
FILE *Pipe = popen(Cmd.toString().c_str(), "r");
|
||||||
|
if (!Pipe) return false;
|
||||||
|
|
||||||
|
if (CmdOutput) {
|
||||||
|
|
||||||
|
char TmpBuffer[128];
|
||||||
|
while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe))
|
||||||
|
CmdOutput->append(TmpBuffer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return pclose(Pipe) == 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimer(int Seconds) {
|
||||||
|
|
||||||
|
struct itimerval T {
|
||||||
|
|
||||||
|
{Seconds, 0}, {
|
||||||
|
|
||||||
|
Seconds, 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
if (setitimer(ITIMER_REAL, &T, nullptr)) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: setitimer failed with %d\n", errno);
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SetSigaction(SIGALRM, AlarmHandler);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSignalHandler(const FuzzingOptions &Options) {
|
||||||
|
|
||||||
|
// setitimer is not implemented in emscripten.
|
||||||
|
if (Options.HandleAlrm && Options.UnitTimeoutSec > 0 && !LIBFUZZER_EMSCRIPTEN)
|
||||||
|
SetTimer(Options.UnitTimeoutSec / 2 + 1);
|
||||||
|
if (Options.HandleInt) SetSigaction(SIGINT, InterruptHandler);
|
||||||
|
if (Options.HandleTerm) SetSigaction(SIGTERM, InterruptHandler);
|
||||||
|
if (Options.HandleSegv) SetSigaction(SIGSEGV, SegvHandler);
|
||||||
|
if (Options.HandleBus) SetSigaction(SIGBUS, CrashHandler);
|
||||||
|
if (Options.HandleAbrt) SetSigaction(SIGABRT, CrashHandler);
|
||||||
|
if (Options.HandleIll) SetSigaction(SIGILL, CrashHandler);
|
||||||
|
if (Options.HandleFpe) SetSigaction(SIGFPE, CrashHandler);
|
||||||
|
if (Options.HandleXfsz) SetSigaction(SIGXFSZ, FileSizeExceedHandler);
|
||||||
|
if (Options.HandleUsr1) SetSigaction(SIGUSR1, GracefulExitHandler);
|
||||||
|
if (Options.HandleUsr2) SetSigaction(SIGUSR2, GracefulExitHandler);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SleepSeconds(int Seconds) {
|
||||||
|
|
||||||
|
sleep(Seconds); // Use C API to avoid coverage from instrumented libc++.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long GetPid() {
|
||||||
|
|
||||||
|
return (unsigned long)getpid();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetPeakRSSMb() {
|
||||||
|
|
||||||
|
struct rusage usage;
|
||||||
|
if (getrusage(RUSAGE_SELF, &usage)) return 0;
|
||||||
|
if (LIBFUZZER_LINUX || LIBFUZZER_FREEBSD || LIBFUZZER_NETBSD ||
|
||||||
|
LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN) {
|
||||||
|
|
||||||
|
// ru_maxrss is in KiB
|
||||||
|
return usage.ru_maxrss >> 10;
|
||||||
|
|
||||||
|
} else if (LIBFUZZER_APPLE) {
|
||||||
|
|
||||||
|
// ru_maxrss is in bytes
|
||||||
|
return usage.ru_maxrss >> 20;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(0 && "GetPeakRSSMb() is not implemented for your platform");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *OpenProcessPipe(const char *Command, const char *Mode) {
|
||||||
|
|
||||||
|
return popen(Command, Mode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int CloseProcessPipe(FILE *F) {
|
||||||
|
|
||||||
|
return pclose(F);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||||
|
size_t PattLen) {
|
||||||
|
|
||||||
|
return memmem(Data, DataLen, Patt, PattLen);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DisassembleCmd(const std::string &FileName) {
|
||||||
|
|
||||||
|
return "objdump -d " + FileName;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SearchRegexCmd(const std::string &Regex) {
|
||||||
|
|
||||||
|
return "grep '" + Regex + "'";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LIBFUZZER_POSIX
|
||||||
|
|
279
custom_mutators/libfuzzer/FuzzerUtilWindows.cpp
Normal file
279
custom_mutators/libfuzzer/FuzzerUtilWindows.cpp
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Misc utils implementation for Windows.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#if LIBFUZZER_WINDOWS
|
||||||
|
#include "FuzzerCommand.h"
|
||||||
|
#include "FuzzerIO.h"
|
||||||
|
#include "FuzzerInternal.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstring>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// This must be included after windows.h.
|
||||||
|
#include <psapi.h>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
static const FuzzingOptions *HandlerOpt = nullptr;
|
||||||
|
|
||||||
|
static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
|
||||||
|
|
||||||
|
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
|
||||||
|
|
||||||
|
case EXCEPTION_ACCESS_VIOLATION:
|
||||||
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||||
|
case EXCEPTION_STACK_OVERFLOW:
|
||||||
|
if (HandlerOpt->HandleSegv) Fuzzer::StaticCrashSignalCallback();
|
||||||
|
break;
|
||||||
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||||
|
case EXCEPTION_IN_PAGE_ERROR:
|
||||||
|
if (HandlerOpt->HandleBus) Fuzzer::StaticCrashSignalCallback();
|
||||||
|
break;
|
||||||
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||||
|
case EXCEPTION_PRIV_INSTRUCTION:
|
||||||
|
if (HandlerOpt->HandleIll) Fuzzer::StaticCrashSignalCallback();
|
||||||
|
break;
|
||||||
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||||
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||||
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||||
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||||
|
case EXCEPTION_FLT_OVERFLOW:
|
||||||
|
case EXCEPTION_FLT_STACK_CHECK:
|
||||||
|
case EXCEPTION_FLT_UNDERFLOW:
|
||||||
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||||
|
case EXCEPTION_INT_OVERFLOW:
|
||||||
|
if (HandlerOpt->HandleFpe) Fuzzer::StaticCrashSignalCallback();
|
||||||
|
break;
|
||||||
|
// TODO: handle (Options.HandleXfsz)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL WINAPI CtrlHandler(DWORD dwCtrlType) {
|
||||||
|
|
||||||
|
switch (dwCtrlType) {
|
||||||
|
|
||||||
|
case CTRL_C_EVENT:
|
||||||
|
if (HandlerOpt->HandleInt) Fuzzer::StaticInterruptCallback();
|
||||||
|
return TRUE;
|
||||||
|
case CTRL_BREAK_EVENT:
|
||||||
|
if (HandlerOpt->HandleTerm) Fuzzer::StaticInterruptCallback();
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CALLBACK AlarmHandler(PVOID, BOOLEAN) {
|
||||||
|
|
||||||
|
Fuzzer::StaticAlarmCallback();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TimerQ {
|
||||||
|
|
||||||
|
HANDLE TimerQueue;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TimerQ() : TimerQueue(NULL) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~TimerQ() {
|
||||||
|
|
||||||
|
if (TimerQueue) DeleteTimerQueueEx(TimerQueue, NULL);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimer(int Seconds) {
|
||||||
|
|
||||||
|
if (!TimerQueue) {
|
||||||
|
|
||||||
|
TimerQueue = CreateTimerQueue();
|
||||||
|
if (!TimerQueue) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: CreateTimerQueue failed.\n");
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE Timer;
|
||||||
|
if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL,
|
||||||
|
Seconds * 1000, Seconds * 1000, 0)) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: CreateTimerQueueTimer failed.\n");
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static TimerQ Timer;
|
||||||
|
|
||||||
|
static void CrashHandler(int) {
|
||||||
|
|
||||||
|
Fuzzer::StaticCrashSignalCallback();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSignalHandler(const FuzzingOptions &Options) {
|
||||||
|
|
||||||
|
HandlerOpt = &Options;
|
||||||
|
|
||||||
|
if (Options.HandleAlrm && Options.UnitTimeoutSec > 0)
|
||||||
|
Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1);
|
||||||
|
|
||||||
|
if (Options.HandleInt || Options.HandleTerm)
|
||||||
|
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) {
|
||||||
|
|
||||||
|
DWORD LastError = GetLastError();
|
||||||
|
Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n",
|
||||||
|
LastError);
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Options.HandleSegv || Options.HandleBus || Options.HandleIll ||
|
||||||
|
Options.HandleFpe)
|
||||||
|
SetUnhandledExceptionFilter(ExceptionHandler);
|
||||||
|
|
||||||
|
if (Options.HandleAbrt)
|
||||||
|
if (SIG_ERR == signal(SIGABRT, CrashHandler)) {
|
||||||
|
|
||||||
|
Printf("libFuzzer: signal failed with %d\n", errno);
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SleepSeconds(int Seconds) {
|
||||||
|
|
||||||
|
Sleep(Seconds * 1000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long GetPid() {
|
||||||
|
|
||||||
|
return GetCurrentProcessId();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetPeakRSSMb() {
|
||||||
|
|
||||||
|
PROCESS_MEMORY_COUNTERS info;
|
||||||
|
if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info))) return 0;
|
||||||
|
return info.PeakWorkingSetSize >> 20;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *OpenProcessPipe(const char *Command, const char *Mode) {
|
||||||
|
|
||||||
|
return _popen(Command, Mode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int CloseProcessPipe(FILE *F) {
|
||||||
|
|
||||||
|
return _pclose(F);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int ExecuteCommand(const Command &Cmd) {
|
||||||
|
|
||||||
|
std::string CmdLine = Cmd.toString();
|
||||||
|
return system(CmdLine.c_str());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) {
|
||||||
|
|
||||||
|
FILE *Pipe = _popen(Cmd.toString().c_str(), "r");
|
||||||
|
if (!Pipe) return false;
|
||||||
|
|
||||||
|
if (CmdOutput) {
|
||||||
|
|
||||||
|
char TmpBuffer[128];
|
||||||
|
while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe))
|
||||||
|
CmdOutput->append(TmpBuffer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return _pclose(Pipe) == 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||||
|
size_t PattLen) {
|
||||||
|
|
||||||
|
// TODO: make this implementation more efficient.
|
||||||
|
const char *Cdata = (const char *)Data;
|
||||||
|
const char *Cpatt = (const char *)Patt;
|
||||||
|
|
||||||
|
if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (PattLen == 1) return memchr(Data, *Cpatt, DataLen);
|
||||||
|
|
||||||
|
const char *End = Cdata + DataLen - PattLen + 1;
|
||||||
|
|
||||||
|
for (const char *It = Cdata; It < End; ++It)
|
||||||
|
if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0) return It;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DisassembleCmd(const std::string &FileName) {
|
||||||
|
|
||||||
|
Vector<std::string> command_vector;
|
||||||
|
command_vector.push_back("dumpbin /summary > nul");
|
||||||
|
if (ExecuteCommand(Command(command_vector)) == 0)
|
||||||
|
return "dumpbin /disasm " + FileName;
|
||||||
|
Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n");
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SearchRegexCmd(const std::string &Regex) {
|
||||||
|
|
||||||
|
return "findstr /r \"" + Regex + "\"";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiscardOutput(int Fd) {
|
||||||
|
|
||||||
|
FILE *Temp = fopen("nul", "w");
|
||||||
|
if (!Temp) return;
|
||||||
|
_dup2(_fileno(Temp), Fd);
|
||||||
|
fclose(Temp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LIBFUZZER_WINDOWS
|
||||||
|
|
73
custom_mutators/libfuzzer/FuzzerValueBitMap.h
Normal file
73
custom_mutators/libfuzzer/FuzzerValueBitMap.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// ValueBitMap.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H
|
||||||
|
#define LLVM_FUZZER_VALUE_BIT_MAP_H
|
||||||
|
|
||||||
|
#include "FuzzerPlatform.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace fuzzer {
|
||||||
|
|
||||||
|
// A bit map containing kMapSizeInWords bits.
|
||||||
|
struct ValueBitMap {
|
||||||
|
static const size_t kMapSizeInBits = 1 << 16;
|
||||||
|
static const size_t kMapPrimeMod = 65371; // Largest Prime < kMapSizeInBits;
|
||||||
|
static const size_t kBitsInWord = (sizeof(uintptr_t) * 8);
|
||||||
|
static const size_t kMapSizeInWords = kMapSizeInBits / kBitsInWord;
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Clears all bits.
|
||||||
|
void Reset() { memset(Map, 0, sizeof(Map)); }
|
||||||
|
|
||||||
|
// Computes a hash function of Value and sets the corresponding bit.
|
||||||
|
// Returns true if the bit was changed from 0 to 1.
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
inline bool AddValue(uintptr_t Value) {
|
||||||
|
uintptr_t Idx = Value % kMapSizeInBits;
|
||||||
|
uintptr_t WordIdx = Idx / kBitsInWord;
|
||||||
|
uintptr_t BitIdx = Idx % kBitsInWord;
|
||||||
|
uintptr_t Old = Map[WordIdx];
|
||||||
|
uintptr_t New = Old | (1ULL << BitIdx);
|
||||||
|
Map[WordIdx] = New;
|
||||||
|
return New != Old;
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
inline bool AddValueModPrime(uintptr_t Value) {
|
||||||
|
return AddValue(Value % kMapPrimeMod);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Get(uintptr_t Idx) {
|
||||||
|
assert(Idx < kMapSizeInBits);
|
||||||
|
uintptr_t WordIdx = Idx / kBitsInWord;
|
||||||
|
uintptr_t BitIdx = Idx % kBitsInWord;
|
||||||
|
return Map[WordIdx] & (1ULL << BitIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SizeInBits() const { return kMapSizeInBits; }
|
||||||
|
|
||||||
|
template <class Callback>
|
||||||
|
ATTRIBUTE_NO_SANITIZE_ALL
|
||||||
|
void ForEach(Callback CB) const {
|
||||||
|
for (size_t i = 0; i < kMapSizeInWords; i++)
|
||||||
|
if (uintptr_t M = Map[i])
|
||||||
|
for (size_t j = 0; j < sizeof(M) * 8; j++)
|
||||||
|
if (M & ((uintptr_t)1 << j))
|
||||||
|
CB(i * sizeof(M) * 8 + j);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ATTRIBUTE_ALIGNED(512) uintptr_t Map[kMapSizeInWords];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzzer
|
||||||
|
|
||||||
|
#endif // LLVM_FUZZER_VALUE_BIT_MAP_H
|
81
custom_mutators/libfuzzer/Makefile
Normal file
81
custom_mutators/libfuzzer/Makefile
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
|
||||||
|
#CFLAGS = -O3 -funroll-loops -fPIC -fpermissive -std=c++11
|
||||||
|
CFLAGS = -g -O0 -fPIC -fpermissive -std=c++11
|
||||||
|
CC := clang++
|
||||||
|
|
||||||
|
all: libfuzzer-mutator.so
|
||||||
|
|
||||||
|
FuzzerCrossOver.o: FuzzerCrossOver.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerDataFlowTrace.o: FuzzerDataFlowTrace.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerDriver.o: FuzzerDriver.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerExtFunctionsDlsym.o: FuzzerExtFunctionsDlsym.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerExtFunctionsWeak.o: FuzzerExtFunctionsWeak.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerExtFunctionsWindows.o: FuzzerExtFunctionsWindows.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerExtraCounters.o: FuzzerExtraCounters.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerFork.o: FuzzerFork.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerIO.o: FuzzerIO.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerIOPosix.o: FuzzerIOPosix.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerIOWindows.o: FuzzerIOWindows.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerLoop.o: FuzzerLoop.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerMerge.o: FuzzerMerge.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerMutate.o: FuzzerMutate.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerSHA1.o: FuzzerSHA1.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerTracePC.o: FuzzerTracePC.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerUtil.o: FuzzerUtil.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerUtilDarwin.o: FuzzerUtilDarwin.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerUtilFuchsia.o: FuzzerUtilFuchsia.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerUtilLinux.o: FuzzerUtilLinux.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerUtilPosix.o: FuzzerUtilPosix.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
FuzzerUtilWindows.o: FuzzerUtilWindows.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
libfuzzer.o: libfuzzer.cpp
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -c $^
|
||||||
|
|
||||||
|
libfuzzer-mutator.so: FuzzerCrossOver.o FuzzerDataFlowTrace.o FuzzerDriver.o FuzzerExtFunctionsDlsym.o FuzzerExtFunctionsWeak.o FuzzerExtFunctionsWindows.o FuzzerExtraCounters.o FuzzerFork.o FuzzerIO.o FuzzerIOPosix.o FuzzerIOWindows.o FuzzerLoop.o FuzzerMerge.o FuzzerMutate.o FuzzerSHA1.o FuzzerTracePC.o FuzzerUtil.o FuzzerUtilDarwin.o FuzzerUtilFuchsia.o FuzzerUtilLinux.o FuzzerUtilPosix.o FuzzerUtilWindows.o libfuzzer.o
|
||||||
|
$(CC) $(CFLAGS) -I../../include -I. -shared -o libfuzzer-mutator.so *.o
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *~ *.so core
|
24
custom_mutators/libfuzzer/README.md
Normal file
24
custom_mutators/libfuzzer/README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# custum mutator: libfuzzer LLVMFuzzerMutate()
|
||||||
|
|
||||||
|
This uses the libfuzzer LLVMFuzzerMutate() function in llvm 12.
|
||||||
|
|
||||||
|
just type `make` to build
|
||||||
|
|
||||||
|
```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/libfuzzer/libfuzzer-mutator.so afl-fuzz ...```
|
||||||
|
|
||||||
|
Note that is is currently simple and is missing two features:
|
||||||
|
* Splicing ("Crossover")
|
||||||
|
* Dictionary support
|
||||||
|
|
||||||
|
To update the source, all that is needed is that FuzzerDriver.cpp has to receive
|
||||||
|
```
|
||||||
|
#include "libfuzzer.inc"
|
||||||
|
```
|
||||||
|
before the closing namespace bracket.
|
||||||
|
|
||||||
|
It is also libfuzzer.inc where the configuration of the libfuzzer mutations
|
||||||
|
are done.
|
||||||
|
|
||||||
|
> Original repository: https://github.com/llvm/llvm-project
|
||||||
|
> Path: compiler-rt/lib/fuzzer/*.{h|cpp}
|
||||||
|
> Source commit: d4b88ac1658d681e143482336cac27c6a74b8b24
|
147
custom_mutators/libfuzzer/libfuzzer.cpp
Normal file
147
custom_mutators/libfuzzer/libfuzzer.cpp
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
//#include "config.h"
|
||||||
|
//#include "debug.h"
|
||||||
|
#include "afl-fuzz.h"
|
||||||
|
|
||||||
|
afl_state_t *afl_struct;
|
||||||
|
|
||||||
|
extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||||
|
extern "C" int LLVMFuzzerRunDriver(int *argc, char ***argv,
|
||||||
|
int (*UserCb)(const uint8_t *Data,
|
||||||
|
size_t Size));
|
||||||
|
extern "C" void LLVMFuzzerMyInit(int (*UserCb)(const uint8_t *Data,
|
||||||
|
size_t Size),
|
||||||
|
unsigned int Seed);
|
||||||
|
|
||||||
|
typedef struct my_mutator {
|
||||||
|
|
||||||
|
afl_state_t *afl;
|
||||||
|
u8 * mutator_buf;
|
||||||
|
unsigned int seed;
|
||||||
|
unsigned int extras_cnt, a_extras_cnt;
|
||||||
|
|
||||||
|
} my_mutator_t;
|
||||||
|
|
||||||
|
extern "C" int dummy(const uint8_t *Data, size_t Size) {
|
||||||
|
|
||||||
|
(void)(Data);
|
||||||
|
(void)(Size);
|
||||||
|
fprintf(stderr, "dummy() called\n");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
|
||||||
|
|
||||||
|
my_mutator_t *data = (my_mutator_t *)calloc(1, sizeof(my_mutator_t));
|
||||||
|
if (!data) {
|
||||||
|
|
||||||
|
perror("afl_custom_init alloc");
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data->mutator_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
|
||||||
|
|
||||||
|
perror("mutator_buf alloc");
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data->afl = afl;
|
||||||
|
data->seed = seed;
|
||||||
|
afl_struct = afl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
char **argv;
|
||||||
|
argv = (char**)malloc(sizeof(size_t) * 2);
|
||||||
|
argv[0] = (char*)"foo";
|
||||||
|
argv[1] = NULL;
|
||||||
|
int eins = 1;
|
||||||
|
LLVMFuzzerRunDriver(&eins, &argv, dummy);
|
||||||
|
*/
|
||||||
|
|
||||||
|
LLVMFuzzerMyInit(dummy, seed);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When a new queue entry is added we check if there are new dictionary
|
||||||
|
entries to add to honggfuzz structure */
|
||||||
|
#if ß
|
||||||
|
extern "C" void afl_custom_queue_new_entry(my_mutator_t * data,
|
||||||
|
const uint8_t *filename_new_queue,
|
||||||
|
const uint8_t *filename_orig_queue) {
|
||||||
|
|
||||||
|
while (data->extras_cnt < afl_struct->extras_cnt) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val,
|
||||||
|
afl_struct->extras[data->extras_cnt].data,
|
||||||
|
afl_struct->extras[data->extras_cnt].len);
|
||||||
|
run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len =
|
||||||
|
afl_struct->extras[data->extras_cnt].len;
|
||||||
|
run.global->mutate.dictionaryCnt++;
|
||||||
|
*/
|
||||||
|
data->extras_cnt++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
while (data->a_extras_cnt < afl_struct->a_extras_cnt) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val,
|
||||||
|
afl_struct->a_extras[data->a_extras_cnt].data,
|
||||||
|
afl_struct->a_extras[data->a_extras_cnt].len);
|
||||||
|
run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len =
|
||||||
|
afl_struct->a_extras[data->a_extras_cnt].len;
|
||||||
|
run.global->mutate.dictionaryCnt++;
|
||||||
|
data->a_extras_cnt++;
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
/* we could set only_printable if is_ascii is set ... let's see
|
||||||
|
uint8_t afl_custom_queue_get(void *data, const uint8_t *filename) {
|
||||||
|
|
||||||
|
//run.global->cfg.only_printable = ...
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* here we run the honggfuzz mutator, which is really good */
|
||||||
|
|
||||||
|
extern "C" size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf,
|
||||||
|
size_t buf_size, u8 **out_buf,
|
||||||
|
uint8_t *add_buf, size_t add_buf_size,
|
||||||
|
size_t max_size) {
|
||||||
|
|
||||||
|
memcpy(data->mutator_buf, buf, buf_size);
|
||||||
|
size_t ret = LLVMFuzzerMutate(data->mutator_buf, buf_size, max_size);
|
||||||
|
|
||||||
|
/* return size of mutated data */
|
||||||
|
*out_buf = data->mutator_buf;
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deinitialize everything
|
||||||
|
*
|
||||||
|
* @param data The data ptr from afl_custom_init
|
||||||
|
*/
|
||||||
|
extern "C" void afl_custom_deinit(my_mutator_t *data) {
|
||||||
|
|
||||||
|
free(data->mutator_buf);
|
||||||
|
free(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
36
custom_mutators/libfuzzer/libfuzzer.inc
Normal file
36
custom_mutators/libfuzzer/libfuzzer.inc
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
|
||||||
|
extern "C" ATTRIBUTE_INTERFACE void
|
||||||
|
LLVMFuzzerMyInit(int (*Callback)(const uint8_t *Data, size_t Size), unsigned int Seed) {
|
||||||
|
Random Rand(Seed);
|
||||||
|
FuzzingOptions Options;
|
||||||
|
Options.Verbosity = 3;
|
||||||
|
Options.MaxLen = 1024000;
|
||||||
|
Options.LenControl = true;
|
||||||
|
Options.DoCrossOver = false;
|
||||||
|
Options.MutateDepth = 6;
|
||||||
|
Options.UseCounters = false;
|
||||||
|
Options.UseMemmem = false;
|
||||||
|
Options.UseCmp = false;
|
||||||
|
Options.UseValueProfile = false;
|
||||||
|
Options.Shrink = false;
|
||||||
|
Options.ReduceInputs = false;
|
||||||
|
Options.PreferSmall = false;
|
||||||
|
Options.ReloadIntervalSec = 0;
|
||||||
|
Options.OnlyASCII = false;
|
||||||
|
Options.DetectLeaks = false;
|
||||||
|
Options.PurgeAllocatorIntervalSec = 0;
|
||||||
|
Options.TraceMalloc = false;
|
||||||
|
Options.RssLimitMb = 100;
|
||||||
|
Options.MallocLimitMb = 100;
|
||||||
|
Options.MaxNumberOfRuns = 0;
|
||||||
|
Options.ReportSlowUnits = false;
|
||||||
|
Options.Entropic = false;
|
||||||
|
|
||||||
|
struct EntropicOptions Entropic;
|
||||||
|
Entropic.Enabled = Options.Entropic;
|
||||||
|
EF = new ExternalFunctions();
|
||||||
|
auto *MD = new MutationDispatcher(Rand, Options);
|
||||||
|
auto *Corpus = new InputCorpus(Options.OutputCorpus, Entropic);
|
||||||
|
auto *F = new Fuzzer(Callback, *Corpus, *MD, Options);
|
||||||
|
}
|
14
custom_mutators/symcc/Makefile
Normal file
14
custom_mutators/symcc/Makefile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
ifdef DEBUG
|
||||||
|
CFLAGS += -DDEBUG
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: symcc-mutator.so
|
||||||
|
|
||||||
|
CFLAGS += -O3 -funroll-loops
|
||||||
|
|
||||||
|
symcc-mutator.so: symcc.c
|
||||||
|
$(CC) $(CFLAGS) $(CPPFLAGS) -g -I../../include -shared -fPIC -o symcc-mutator.so symcc.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f symcc-mutator.so *.o *~ core
|
15
custom_mutators/symcc/README.md
Normal file
15
custom_mutators/symcc/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# custum mutator: symcc
|
||||||
|
|
||||||
|
This uses the excellent symcc to find new paths into the target.
|
||||||
|
|
||||||
|
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
|
||||||
|
that you are fuzzing).
|
||||||
|
|
||||||
|
The target program compiled with symcc has to be pointed to with the
|
||||||
|
`SYMCC_TARGET` environment variable.
|
||||||
|
|
||||||
|
just type `make` to build this custom mutator.
|
||||||
|
|
||||||
|
```SYMCC_TARGET=/prg/to/symcc/compiled/target AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/symcc/symcc-mutator.so afl-fuzz ...```
|
234
custom_mutators/symcc/symcc.c
Normal file
234
custom_mutators/symcc/symcc.c
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "config.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "afl-fuzz.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
afl_state_t *afl_struct;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define DBG(x...) fprintf(stderr, x)
|
||||||
|
#else
|
||||||
|
#define DBG(x...) \
|
||||||
|
{}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct my_mutator {
|
||||||
|
|
||||||
|
afl_state_t *afl;
|
||||||
|
u8 * mutator_buf;
|
||||||
|
u8 * out_dir;
|
||||||
|
u8 * target;
|
||||||
|
uint32_t seed;
|
||||||
|
|
||||||
|
} my_mutator_t;
|
||||||
|
|
||||||
|
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
|
||||||
|
|
||||||
|
if (getenv("AFL_CUSTOM_MUTATOR_ONLY"))
|
||||||
|
FATAL("the symcc module cannot be used with AFL_CUSTOM_MUTATOR_ONLY.");
|
||||||
|
|
||||||
|
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
|
||||||
|
if (!data) {
|
||||||
|
|
||||||
|
perror("afl_custom_init alloc");
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data->mutator_buf = malloc(MAX_FILE)) == NULL) {
|
||||||
|
|
||||||
|
perror("mutator_buf alloc");
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(data->target = getenv("SYMCC_TARGET")))
|
||||||
|
FATAL(
|
||||||
|
"SYMCC_TARGET not defined, this should point to the full path of the "
|
||||||
|
"symcc compiled binary.");
|
||||||
|
|
||||||
|
if (!(data->out_dir = getenv("SYMCC_OUTPUT_DIR"))) {
|
||||||
|
|
||||||
|
data->out_dir = alloc_printf("%s/symcc", afl->out_dir);
|
||||||
|
setenv("SYMCC_OUTPUT_DIR", data->out_dir, 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int pid = fork();
|
||||||
|
|
||||||
|
if (pid == -1) return NULL;
|
||||||
|
|
||||||
|
if (pid) pid = waitpid(pid, NULL, 0);
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
|
||||||
|
char *args[4];
|
||||||
|
args[0] = "/bin/rm";
|
||||||
|
args[1] = "-rf";
|
||||||
|
args[2] = data->out_dir;
|
||||||
|
args[3] = NULL;
|
||||||
|
execvp(args[0], args);
|
||||||
|
DBG("exec:FAIL\n");
|
||||||
|
exit(-1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data->afl = afl;
|
||||||
|
data->seed = seed;
|
||||||
|
afl_struct = afl;
|
||||||
|
|
||||||
|
if (mkdir(data->out_dir, 0755))
|
||||||
|
PFATAL("Could not create directory %s", data->out_dir);
|
||||||
|
DBG("out_dir=%s, target=%s\n", data->out_dir, data->target);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When a new queue entry is added we run this input with the symcc
|
||||||
|
instrumented binary */
|
||||||
|
void afl_custom_queue_new_entry(my_mutator_t * data,
|
||||||
|
const uint8_t *filename_new_queue,
|
||||||
|
const uint8_t *filename_orig_queue) {
|
||||||
|
|
||||||
|
int pid = fork();
|
||||||
|
|
||||||
|
if (pid == -1) return;
|
||||||
|
|
||||||
|
if (pid) pid = waitpid(pid, NULL, 0);
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
|
||||||
|
setenv("SYMCC_INPUT_FILE", afl_struct->fsrv.out_file, 1);
|
||||||
|
|
||||||
|
if (afl_struct->fsrv.use_stdin) {
|
||||||
|
|
||||||
|
u8 *fn = alloc_printf("%s/%s", afl_struct->out_dir, filename_new_queue);
|
||||||
|
int fd = open(fn, O_RDONLY);
|
||||||
|
|
||||||
|
if (fd >= 0) {
|
||||||
|
|
||||||
|
ssize_t r = read(fd, data->mutator_buf, MAX_FILE);
|
||||||
|
close(fd);
|
||||||
|
DBG("fn=%s, fd=%d, size=%ld\n", fn, fd, r);
|
||||||
|
if (r <= 0) return;
|
||||||
|
close(0);
|
||||||
|
ck_write(0, data->mutator_buf, r, fn);
|
||||||
|
ck_free(fn);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG("exec=%s\n", data->target);
|
||||||
|
close(1);
|
||||||
|
close(2);
|
||||||
|
dup2(afl_struct->fsrv.dev_null_fd, 1);
|
||||||
|
dup2(afl_struct->fsrv.dev_null_fd, 2);
|
||||||
|
execvp(data->target, afl_struct->argv);
|
||||||
|
DBG("exec=FAIL\n");
|
||||||
|
exit(-1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t afl_custom_fuzz_count(my_mutator_t *data, const u8 *buf,
|
||||||
|
size_t buf_size) {
|
||||||
|
|
||||||
|
uint32_t count = 0, i;
|
||||||
|
struct dirent **nl;
|
||||||
|
int32_t items = scandir(data->out_dir, &nl, NULL, NULL);
|
||||||
|
|
||||||
|
if (items > 0) {
|
||||||
|
|
||||||
|
for (i = 0; i < (u32)items; ++i) {
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
u8 * fn = alloc_printf("%s/%s", data->out_dir, nl[i]->d_name);
|
||||||
|
DBG("test=%s\n", fn);
|
||||||
|
if (stat(fn, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) {
|
||||||
|
|
||||||
|
DBG("found=%s\n", fn);
|
||||||
|
count++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ck_free(fn);
|
||||||
|
free(nl[i]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
free(nl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG("dir=%s, count=%u\n", data->out_dir, count);
|
||||||
|
return count;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* here we actualy just read the files generated from symcc */
|
||||||
|
size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
|
||||||
|
u8 **out_buf, uint8_t *add_buf, size_t add_buf_size,
|
||||||
|
size_t max_size) {
|
||||||
|
|
||||||
|
struct dirent **nl;
|
||||||
|
int32_t i, done = 0, items = scandir(data->out_dir, &nl, NULL, NULL);
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
if (items <= 0) return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < (u32)items; ++i) {
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
u8 * fn = alloc_printf("%s/%s", data->out_dir, nl[i]->d_name);
|
||||||
|
|
||||||
|
if (done == 0) {
|
||||||
|
|
||||||
|
if (stat(fn, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) {
|
||||||
|
|
||||||
|
int fd = open(fn, O_RDONLY);
|
||||||
|
|
||||||
|
if (fd >= 0) {
|
||||||
|
|
||||||
|
size = read(fd, data->mutator_buf, max_size);
|
||||||
|
*out_buf = data->mutator_buf;
|
||||||
|
close(fd);
|
||||||
|
done = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unlink(fn);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ck_free(fn);
|
||||||
|
free(nl[i]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
free(nl);
|
||||||
|
DBG("FUZZ size=%lu\n", size);
|
||||||
|
return size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,34 @@ Want to stay in the loop on major new features? Join our mailing list by
|
|||||||
sending a mail to <afl-users+subscribe@googlegroups.com>.
|
sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||||
|
|
||||||
|
|
||||||
|
### Version ++3.00a (develop)
|
||||||
|
- llvm_mode/ and gcc_plugin/ moved to instrumentation/
|
||||||
|
- all compilers combined to afl-cc which emulates the previous ones
|
||||||
|
- afl-llvm/gcc-rt.o merged into afl-compiler-rt.o
|
||||||
|
- afl-fuzz
|
||||||
|
- reading testcases from -i now descends into subdirectories
|
||||||
|
- allow up to 4 -x command line options
|
||||||
|
- loaded extras now have a duplicate protection
|
||||||
|
- If test cases are too large we do a partial read on the maximum
|
||||||
|
supported size
|
||||||
|
- longer seeds with the same trace information will now be ignored
|
||||||
|
for fuzzing but still be used for splicing
|
||||||
|
- crashing seeds are now not prohibiting a run anymore but are
|
||||||
|
skipped. They are used for splicing though.
|
||||||
|
- set the default power schedule to the superiour "seek" schedule
|
||||||
|
- instrumentation
|
||||||
|
- We received an enhanced gcc_plugin module from AdaCore, thank you
|
||||||
|
very much!!
|
||||||
|
- not overriding -Ox or -fno-unroll-loops anymore
|
||||||
|
- new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz
|
||||||
|
-x dictionary of string comparisons found during compilation
|
||||||
|
- LTO autodict now also collects interesting cmp comparisons,
|
||||||
|
std::string compare + find + ==, bcmp
|
||||||
|
- added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/
|
||||||
|
- added a new custom mutator: libfuzzer that integrates libfuzzer mutations
|
||||||
|
- Our afl++ Grammar-Mutator is now better integrated into custom_mutators/
|
||||||
|
|
||||||
|
|
||||||
### Version ++2.68c (release)
|
### Version ++2.68c (release)
|
||||||
- added the GSoC excellent afl++ grammar mutator by Shengtuo to our
|
- added the GSoC excellent afl++ grammar mutator by Shengtuo to our
|
||||||
custom_mutators/ (see custom_mutators/README.md) - or get it here:
|
custom_mutators/ (see custom_mutators/README.md) - or get it here:
|
||||||
|
104
docs/FAQ.md
104
docs/FAQ.md
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
* [What is the difference between afl and afl++?](#what-is-the-difference-between-afl-and-afl)
|
* [What is the difference between afl and afl++?](#what-is-the-difference-between-afl-and-afl)
|
||||||
* [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed)
|
* [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed)
|
||||||
* [How do I fuzz a network service?](#how-do-i-fuzz-a-network-service)
|
* [How do I fuzz a network service?](#how-to-fuzz-a-network-service)
|
||||||
* [How do I fuzz a GUI program?](#how-do-i-fuzz-a-gui-program)
|
* [How do I fuzz a GUI program?](#how-to-fuzz-a-gui-program)
|
||||||
* [What is an edge?](#what-is-an-edge)
|
* [What is an edge?](#what-is-an-edge)
|
||||||
* [Why is my stability below 100%?](#why-is-my-stability-below-100)
|
* [Why is my stability below 100%?](#why-is-my-stability-below-100)
|
||||||
* [How can I improve the stability value?](#how-can-i-improve-the-stability-value)
|
* [How can I improve the stability value](#how-can-i-improve-the-stability-value)
|
||||||
|
|
||||||
If you find an interesting or important question missing, submit it via
|
If you find an interesting or important question missing, submit it via
|
||||||
[https://github.com/AFLplusplus/AFLplusplus/issues](https://github.com/AFLplusplus/AFLplusplus/issues)
|
[https://github.com/AFLplusplus/AFLplusplus/issues](https://github.com/AFLplusplus/AFLplusplus/issues)
|
||||||
@ -18,52 +18,51 @@ If you find an interesting or important question missing, submit it via
|
|||||||
American Fuzzy Lop (AFL) was developed by Michał "lcamtuf" Zalewski starting in
|
American Fuzzy Lop (AFL) was developed by Michał "lcamtuf" Zalewski starting in
|
||||||
2013/2014, and when he left Google end of 2017 he stopped developing it.
|
2013/2014, and when he left Google end of 2017 he stopped developing it.
|
||||||
|
|
||||||
At the end of 2019 the Google fuzzing team took over maintenance of AFL, however
|
At the end of 2019 the Google fuzzing team took over maintance of AFL, however
|
||||||
it is only accepting PRs from the community and is not developing enhancements
|
it is only accepting PR from the community and is not developing enhancements
|
||||||
anymore.
|
anymore.
|
||||||
|
|
||||||
In the second quarter of 2019, 1 1/2 year later when no further development of
|
In the second quarter of 2019, 1 1/2 years after no further development of
|
||||||
AFL had happened and it became clear there would none be coming, afl++
|
AFL had happened and it became clear there would be none coming, afl++
|
||||||
was born, where initially community patches were collected and applied
|
was born, where initially first community patches were collected and applied
|
||||||
for bug fixes and enhancements. Then from various AFL spin-offs - mostly academic
|
for bugs and enhancements. Then from various AFL spin-offs - mostly academic
|
||||||
research - features were integrated. This already resulted in a much advanced
|
research - features were integrated. This already resulted in a much advanced
|
||||||
AFL.
|
AFL.
|
||||||
|
|
||||||
Until the end of 2019 the afl++ team had grown to four active developers which
|
Until the end of 2019 the afl++ team had grown to four active developers which
|
||||||
then implemented their own research and features, making it now by far the most
|
then implemented their own research and feature, making it now by far the most
|
||||||
flexible and feature rich guided fuzzer available as open source.
|
flexible and feature rich guided fuzzer available as open source.
|
||||||
And in independent fuzzing benchmarks it is one of the best fuzzers available,
|
And in independent fuzzing benchmarks it is one of the best fuzzers available,
|
||||||
e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html)
|
e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html)
|
||||||
|
|
||||||
## How to improve the fuzzing speed?
|
## How to improve the fuzzing speed
|
||||||
|
|
||||||
1. Use [llvm_mode](docs/llvm_mode/README.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended)
|
1. use [instrumentation](docs/README.llvm.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended)
|
||||||
2. Use [persistent mode](llvm_mode/README.persistent_mode.md) (x2-x20 speed increase)
|
2. Use [persistent mode](instrumentation/README.persistent_mode.md) (x2-x20 speed increase)
|
||||||
3. Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase)
|
3. Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase)
|
||||||
4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to put the input file directory on a tempfs location, see [docs/env_variables.md](docs/env_variables.md)
|
4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to point the input file on a tempfs location, see [docs/env_variables.md](docs/env_variables.md)
|
||||||
5. Improve Linux kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system less secure)
|
5. Improve kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system more insecure)
|
||||||
6. Running on an `ext2` filesystem with `noatime` mount option will be a bit faster than on any other journaling filesystem
|
6. Running on an `ext2` filesystem with `noatime` mount option will be a bit faster than on any other journaling filesystem
|
||||||
7. Use your cores! [README.md:3.b) Using multiple cores/threads](../README.md#b-using-multiple-coresthreads)
|
7. Use your cores! [README.md:3.b) Using multiple cores/threads](../README.md#b-using-multiple-coresthreads)
|
||||||
|
|
||||||
## How do I fuzz a network service?
|
## How do I fuzz a network service?
|
||||||
|
|
||||||
The short answer is - you cannot, at least not "out of the box".
|
The short answer is - you cannot, at least "out of the box".
|
||||||
|
|
||||||
Using a network channel is inadequate for several reasons:
|
Using network has a slow-down of x10-20 on the fuzzing speed, does not scale,
|
||||||
- it has a slow-down of x10-20 on the fuzzing speed
|
and finally usually it is more than one initial data packet but a back-and-forth
|
||||||
- it does not scale to fuzzing multiple instances easily,
|
which is totally unsupported by most coverage aware fuzzers.
|
||||||
- instead of one initial data packet often a back-and-forth interplay of packets is needed for stateful protocols (which is totally unsupported by most coverage aware fuzzers).
|
|
||||||
|
|
||||||
The established method to fuzz network services is to modify the source code
|
The established method to fuzz network services is to modify the source code
|
||||||
to read from a file or stdin (fd 0) (or even faster via shared memory, combine
|
to read from a file or stdin (fd 0) (or even faster via shared memory, combine
|
||||||
this with persistent mode [llvm_mode/README.persistent_mode.md](llvm_mode/README.persistent_mode.md)
|
this with persistent mode [instrumentation/README.persistent_mode.md](instrumentation/README.persistent_mode.md)
|
||||||
and you have a performance gain of x10 instead of a performance loss of over
|
and you have a performance gain of x10 instead of a performance loss of over
|
||||||
x10 - that is a x100 difference!).
|
x10 - that is a x100 difference!
|
||||||
|
|
||||||
If modifying the source is not an option (e.g. because you only have a binary
|
If modifying the source is not an option (e.g. because you only have a binary
|
||||||
and perform binary fuzzing) you can also use a shared library with AFL_PRELOAD
|
and perform binary fuzzing) you can also use a shared library with AFL_PRELOAD
|
||||||
to emulate the network. This is also much faster than the real network would be.
|
to emulate the network. This is also much faster than network would be.
|
||||||
See [examples/socket_fuzzing/](../examples/socket_fuzzing/).
|
See [examples/socket_fuzzing/](../examples/socket_fuzzing/)
|
||||||
|
|
||||||
There is an outdated afl++ branch that implements networking if you are
|
There is an outdated afl++ branch that implements networking if you are
|
||||||
desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) -
|
desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) -
|
||||||
@ -74,7 +73,7 @@ which allows you to define network state with different type of data packets.
|
|||||||
|
|
||||||
If the GUI program can read the fuzz data from a file (via the command line,
|
If the GUI program can read the fuzz data from a file (via the command line,
|
||||||
a fixed location or via an environment variable) without needing any user
|
a fixed location or via an environment variable) without needing any user
|
||||||
interaction then it would be suitable for fuzzing.
|
interaction then then yes.
|
||||||
|
|
||||||
Otherwise it is not possible without modifying the source code - which is a
|
Otherwise it is not possible without modifying the source code - which is a
|
||||||
very good idea anyway as the GUI functionality is a huge CPU/time overhead
|
very good idea anyway as the GUI functionality is a huge CPU/time overhead
|
||||||
@ -83,13 +82,13 @@ for the fuzzing.
|
|||||||
So create a new `main()` that just reads the test case and calls the
|
So create a new `main()` that just reads the test case and calls the
|
||||||
functionality for processing the input that the GUI program is using.
|
functionality for processing the input that the GUI program is using.
|
||||||
|
|
||||||
## What is an "edge"?
|
## What is an "edge"
|
||||||
|
|
||||||
A program contains `functions`, `functions` contain the compiled machine code.
|
A program contains `functions`, `functions` contain the compiled machine code.
|
||||||
The compiled machine code in a `function` can be in a single or many `basic blocks`.
|
The compiled machine code in a `function` can be in a single or many `basic blocks`.
|
||||||
A `basic block` is the largest possible number of subsequent machine code
|
A `basic block` is the largest possible number of subsequent machine code
|
||||||
instructions that has exactly one entrypoint (which can be be entered by multiple other basic blocks)
|
instructions that runs independent, meaning it does not split up to different
|
||||||
and runs linearly without branching or jumping to other addresses (except at the end).
|
locations nor is it jumped into it from a different location:
|
||||||
```
|
```
|
||||||
function() {
|
function() {
|
||||||
A:
|
A:
|
||||||
@ -99,7 +98,7 @@ function() {
|
|||||||
if (x) goto C; else goto D;
|
if (x) goto C; else goto D;
|
||||||
C:
|
C:
|
||||||
some code
|
some code
|
||||||
goto E
|
goto D
|
||||||
D:
|
D:
|
||||||
some code
|
some code
|
||||||
goto B
|
goto B
|
||||||
@ -109,7 +108,7 @@ function() {
|
|||||||
```
|
```
|
||||||
Every code block between two jump locations is a `basic block`.
|
Every code block between two jump locations is a `basic block`.
|
||||||
|
|
||||||
An `edge` is then the unique relationship between two directly connected `basic blocks` (from the
|
An `edge` is then the unique relationship between two `basic blocks` (from the
|
||||||
code example above):
|
code example above):
|
||||||
```
|
```
|
||||||
Block A
|
Block A
|
||||||
@ -124,9 +123,8 @@ code example above):
|
|||||||
Block E
|
Block E
|
||||||
```
|
```
|
||||||
Every line between two blocks is an `edge`.
|
Every line between two blocks is an `edge`.
|
||||||
Note that a few basic block loop to itself, this too would be an edge.
|
|
||||||
|
|
||||||
## Why is my stability below 100%?
|
## Why is my stability below 100%
|
||||||
|
|
||||||
Stability is measured by how many percent of the edges in the target are
|
Stability is measured by how many percent of the edges in the target are
|
||||||
"stable". Sending the same input again and again should take the exact same
|
"stable". Sending the same input again and again should take the exact same
|
||||||
@ -134,37 +132,37 @@ path through the target every time. If that is the case, the stability is 100%.
|
|||||||
|
|
||||||
If however randomness happens, e.g. a thread reading other external data,
|
If however randomness happens, e.g. a thread reading other external data,
|
||||||
reaction to timing, etc. then in some of the re-executions with the same data
|
reaction to timing, etc. then in some of the re-executions with the same data
|
||||||
the edge coverage result will be different accross runs.
|
the result in the edge information will be different accross runs.
|
||||||
Those edges that change are then flagged "unstable".
|
Those edges that change are then flagged "unstable".
|
||||||
|
|
||||||
The more "unstable" edges, the more difficult for afl++ to identify valid new
|
The more "unstable" edges, the more difficult for afl++ to identify valid new
|
||||||
paths.
|
paths.
|
||||||
|
|
||||||
A value above 90% is usually fine and a value above 80% is also still ok, and
|
A value above 90% is usually fine and a value above 80% is also still ok, and
|
||||||
even a value above 20% can still result in successful finds of bugs.
|
even above 20% can still result in successful finds of bugs.
|
||||||
However, it is recommended that for values below 90% or 80% you should take
|
However, it is recommended that below 90% or 80% you should take measures to
|
||||||
countermeasures to improve stability.
|
improve the stability.
|
||||||
|
|
||||||
## How can I improve the stability value?
|
## How can I improve the stability value
|
||||||
|
|
||||||
For fuzzing a 100% stable target that covers all edges is the best case.
|
For fuzzing a 100% stable target that covers all edges is the best.
|
||||||
A 90% stable target that covers all edges is however better than a 100% stable
|
A 90% stable target that covers all edges is however better than a 100% stable
|
||||||
target that ignores 10% of the edges.
|
target that ignores 10% of the edges.
|
||||||
|
|
||||||
With instability you basically have a partial coverage loss on an edge, with
|
With instability you basically have a partial coverage loss on an edge, with
|
||||||
ignored functions you have a full loss on that edges.
|
ignore you have a full loss on that edge.
|
||||||
|
|
||||||
There are functions that are unstable, but also provide value to coverage, eg
|
There are functions that are unstable, but also provide value to coverage, eg
|
||||||
init functions that use fuzz data as input for example.
|
init functions that use fuzz data as input for example.
|
||||||
If however a function that has nothing to do with the input data is the
|
If however it is a function that has nothing to do with the input data is the
|
||||||
source of instability, e.g. checking jitter, or is a hash map function etc.
|
source, e.g. checking jitter, or is a hash map function etc. then it should
|
||||||
then it should not be instrumented.
|
not be instrumented.
|
||||||
|
|
||||||
To be able to exclude these functions (based on AFL++'s measured stability)
|
To be able to make this decision the following process will allow you to
|
||||||
the following process will allow to identify functions with variable edges.
|
identify the functions with variable edges so you can make this decision.
|
||||||
|
|
||||||
Four steps are required to do this and it also requires quite some knowledge
|
Four steps are required to do this and requires quite some knowledge of
|
||||||
of coding and/or disassembly and is effectively possible only with
|
coding and/or disassembly and it is only effectively possible with
|
||||||
afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation.
|
afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation.
|
||||||
|
|
||||||
1. First step: Identify which edge ID numbers are unstable
|
1. First step: Identify which edge ID numbers are unstable
|
||||||
@ -173,7 +171,7 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation.
|
|||||||
The out/fuzzer_stats file will then show the edge IDs that were identified
|
The out/fuzzer_stats file will then show the edge IDs that were identified
|
||||||
as unstable.
|
as unstable.
|
||||||
|
|
||||||
2. Second step: Find the responsible function(s).
|
2. Second step: Find the responsible function.
|
||||||
|
|
||||||
a) For LTO instrumented binaries this can be documented during compile
|
a) For LTO instrumented binaries this can be documented during compile
|
||||||
time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/a/file`.
|
time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/a/file`.
|
||||||
@ -182,10 +180,10 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation.
|
|||||||
|
|
||||||
b) For PCGUARD instrumented binaries it is much more difficult. Here you
|
b) For PCGUARD instrumented binaries it is much more difficult. Here you
|
||||||
can either modify the __sanitizer_cov_trace_pc_guard function in
|
can either modify the __sanitizer_cov_trace_pc_guard function in
|
||||||
llvm_mode/afl-llvm-rt.o.c to write a backtrace to a file if the ID in
|
instrumentation/afl-llvm-rt.o.c to write a backtrace to a file if the ID in
|
||||||
__afl_area_ptr[*guard] is one of the unstable edge IDs.
|
__afl_area_ptr[*guard] is one of the unstable edge IDs.
|
||||||
(Example code is already there).
|
(Example code is already there).
|
||||||
Then recompile and reinstall llvm_mode and rebuild your target.
|
Then recompile and reinstall instrumentation and rebuild your target.
|
||||||
Run the recompiled target with afl-fuzz for a while and then check the
|
Run the recompiled target with afl-fuzz for a while and then check the
|
||||||
file that you wrote with the backtrace information.
|
file that you wrote with the backtrace information.
|
||||||
Alternatively you can use `gdb` to hook __sanitizer_cov_trace_pc_guard_init
|
Alternatively you can use `gdb` to hook __sanitizer_cov_trace_pc_guard_init
|
||||||
@ -193,20 +191,20 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation.
|
|||||||
and set a write breakpoint to that address (`watch 0x.....`).
|
and set a write breakpoint to that address (`watch 0x.....`).
|
||||||
|
|
||||||
c) in all other instrumentation types this is not possible. So just
|
c) in all other instrumentation types this is not possible. So just
|
||||||
recompile with the two mentioned above. This is just for
|
recompile with the the two mentioned above. This is just for
|
||||||
identifying the functions that have unstable edges.
|
identifying the functions that have unstable edges.
|
||||||
|
|
||||||
3. Third step: create a text file with the filenames/functions
|
3. Third step: create a text file with the filenames/functions
|
||||||
|
|
||||||
Identify which source code files contain the functions that you need to
|
Identify which source code files contain the functions that you need to
|
||||||
remove from instrumentation, or just specify the functions you want to
|
remove from instrumentation, or just specify the functions you want to
|
||||||
skip for instrumentation. Note that optimization might inline functions!
|
skip instrumenting. Note that optimization might inline functions!
|
||||||
|
|
||||||
Simply follow this document on how to do this: [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md)
|
Simply follow this document on how to do this: [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md)
|
||||||
If PCGUARD is used, then you need to follow this guide (needs llvm 12+!):
|
If PCGUARD is used, then you need to follow this guide (needs llvm 12+!):
|
||||||
[http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation)
|
[http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation)
|
||||||
|
|
||||||
Only exclude those functions from instrumentation that provide no value
|
Only deny those functions from instrumentation that provide no value
|
||||||
for coverage - that is if it does not process any fuzz data directly
|
for coverage - that is if it does not process any fuzz data directly
|
||||||
or indirectly (e.g. hash maps, thread management etc.).
|
or indirectly (e.g. hash maps, thread management etc.).
|
||||||
If however a function directly or indirectly handles fuzz data then you
|
If however a function directly or indirectly handles fuzz data then you
|
||||||
|
@ -24,10 +24,12 @@ There are no special dependencies to speak of; you will need GNU make and a
|
|||||||
working compiler (gcc or clang). Some of the optional scripts bundled with the
|
working compiler (gcc or clang). Some of the optional scripts bundled with the
|
||||||
program may depend on bash, gdb, and similar basic tools.
|
program may depend on bash, gdb, and similar basic tools.
|
||||||
|
|
||||||
If you are using clang, please review llvm_mode/README.md; the LLVM
|
If you are using clang, please review README.llvm.md; the LLVM
|
||||||
integration mode can offer substantial performance gains compared to the
|
integration mode can offer substantial performance gains compared to the
|
||||||
traditional approach.
|
traditional approach.
|
||||||
|
|
||||||
|
Likewise, if you are using GCC, please review instrumentation/README.gcc_plugin.md.
|
||||||
|
|
||||||
You may have to change several settings to get optimal results (most notably,
|
You may have to change several settings to get optimal results (most notably,
|
||||||
disable crash reporting utilities and switch to a different CPU governor), but
|
disable crash reporting utilities and switch to a different CPU governor), but
|
||||||
afl-fuzz will guide you through that if necessary.
|
afl-fuzz will guide you through that if necessary.
|
||||||
@ -52,10 +54,10 @@ sudo gmake install
|
|||||||
Keep in mind that if you are using csh as your shell, the syntax of some of the
|
Keep in mind that if you are using csh as your shell, the syntax of some of the
|
||||||
shell commands given in the README.md and other docs will be different.
|
shell commands given in the README.md and other docs will be different.
|
||||||
|
|
||||||
The `llvm_mode` requires a dynamically linked, fully-operational installation of
|
The `llvm` requires a dynamically linked, fully-operational installation of
|
||||||
clang. At least on FreeBSD, the clang binaries are static and do not include
|
clang. At least on FreeBSD, the clang binaries are static and do not include
|
||||||
some of the essential tools, so if you want to make it work, you may need to
|
some of the essential tools, so if you want to make it work, you may need to
|
||||||
follow the instructions in llvm_mode/README.md.
|
follow the instructions in README.llvm.md.
|
||||||
|
|
||||||
Beyond that, everything should work as advertised.
|
Beyond that, everything should work as advertised.
|
||||||
|
|
||||||
@ -97,27 +99,24 @@ and definitely don't look POSIX-compliant. This means two things:
|
|||||||
User emulation mode of QEMU does not appear to be supported on MacOS X, so
|
User emulation mode of QEMU does not appear to be supported on MacOS X, so
|
||||||
black-box instrumentation mode (`-Q`) will not work.
|
black-box instrumentation mode (`-Q`) will not work.
|
||||||
|
|
||||||
The llvm_mode requires a fully-operational installation of clang. The one that
|
The llvm instrumentation requires a fully-operational installation of clang. The one that
|
||||||
comes with Xcode is missing some of the essential headers and helper tools.
|
comes with Xcode is missing some of the essential headers and helper tools.
|
||||||
See llvm_mode/README.md for advice on how to build the compiler from scratch.
|
See README.llvm.md for advice on how to build the compiler from scratch.
|
||||||
|
|
||||||
## 4. Linux or *BSD on non-x86 systems
|
## 4. Linux or *BSD on non-x86 systems
|
||||||
|
|
||||||
Standard build will fail on non-x86 systems, but you should be able to
|
Standard build will fail on non-x86 systems, but you should be able to
|
||||||
leverage two other options:
|
leverage two other options:
|
||||||
|
|
||||||
- The LLVM mode (see llvm_mode/README.md), which does not rely on
|
- The LLVM mode (see README.llvm.md), which does not rely on
|
||||||
x86-specific assembly shims. It's fast and robust, but requires a
|
x86-specific assembly shims. It's fast and robust, but requires a
|
||||||
complete installation of clang.
|
complete installation of clang.
|
||||||
- The QEMU mode (see qemu_mode/README.md), which can be also used for
|
- The QEMU mode (see qemu_mode/README.md), which can be also used for
|
||||||
fuzzing cross-platform binaries. It's slower and more fragile, but
|
fuzzing cross-platform binaries. It's slower and more fragile, but
|
||||||
can be used even when you don't have the source for the tested app.
|
can be used even when you don't have the source for the tested app.
|
||||||
|
|
||||||
If you're not sure what you need, you need the LLVM mode. To get it, try:
|
If you're not sure what you need, you need the LLVM mode, which is built by
|
||||||
|
default.
|
||||||
```bash
|
|
||||||
AFL_NO_X86=1 gmake && gmake -C llvm_mode
|
|
||||||
```
|
|
||||||
|
|
||||||
...and compile your target program with afl-clang-fast or afl-clang-fast++
|
...and compile your target program with afl-clang-fast or afl-clang-fast++
|
||||||
instead of the traditional afl-gcc or afl-clang wrappers.
|
instead of the traditional afl-gcc or afl-clang wrappers.
|
||||||
@ -160,7 +159,8 @@ instrumentation mode (`-Q`) will not work.
|
|||||||
## 6. Everything else
|
## 6. Everything else
|
||||||
|
|
||||||
You're on your own. On POSIX-compliant systems, you may be able to compile and
|
You're on your own. On POSIX-compliant systems, you may be able to compile and
|
||||||
run the fuzzer; and the LLVM mode may offer a way to instrument non-x86 code.
|
run the fuzzer; and the LLVM and GCC plugin modes may offer a way to instrument
|
||||||
|
non-x86 code.
|
||||||
|
|
||||||
The fuzzer will run on Windows in WSL only. It will not work under Cygwin on in the normal Windows world. It
|
The fuzzer will run on Windows in WSL only. It will not work under Cygwin on in the normal Windows world. It
|
||||||
could be ported to the latter platform fairly easily, but it's a pretty bad
|
could be ported to the latter platform fairly easily, but it's a pretty bad
|
||||||
|
@ -5,13 +5,25 @@
|
|||||||
users or for some types of custom fuzzing setups. See README.md for the general
|
users or for some types of custom fuzzing setups. See README.md for the general
|
||||||
instruction manual.
|
instruction manual.
|
||||||
|
|
||||||
## 1) Settings for afl-gcc, afl-clang, and afl-as - and gcc_plugin afl-gcc-fast
|
## 1) Settings for all compilers
|
||||||
|
|
||||||
Because they can't directly accept command-line options, the compile-time
|
Starting with afl++ 3.0 there is only one compiler: afl-cc
|
||||||
tools make fairly broad use of environmental variables:
|
To select the different instrumentation modes this can be done by
|
||||||
|
1. passing --afl-MODE command line options to the compiler
|
||||||
|
2. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++,
|
||||||
|
afl-clang-fast, afl-clang-fast++, afl-clang-lto, afl-clang-lto++,
|
||||||
|
afl-gcc-fast, afl-g++-fast
|
||||||
|
3. using the environment variable AFL_CC_COMPILER with MODE
|
||||||
|
|
||||||
- Most afl tools do not print any output if stdout/stderr are redirected.
|
MODE can one of LTO (afl-clang-lto*), LLVM (afl-clang-fast*), GCC_PLUGIN
|
||||||
If you want to save the output in a file then set the AFL_DEBUG
|
(afl-g*-fast) or GCC (afl-gcc/afl-g++).
|
||||||
|
|
||||||
|
Because beside the --afl-MODE command no afl specific command-line options
|
||||||
|
are accepted, the compile-time tools make fairly broad use of environmental
|
||||||
|
variables:
|
||||||
|
|
||||||
|
- Most afl tools do not print any ouput if stout/stderr are redirected.
|
||||||
|
If you want to have the output into a file then set the AFL_DEBUG
|
||||||
environment variable.
|
environment variable.
|
||||||
This is sadly necessary for various build processes which fail otherwise.
|
This is sadly necessary for various build processes which fail otherwise.
|
||||||
|
|
||||||
@ -24,6 +36,8 @@ tools make fairly broad use of environmental variables:
|
|||||||
will cause problems in programs built with -Werror, simply because -O3
|
will cause problems in programs built with -Werror, simply because -O3
|
||||||
enables more thorough code analysis and can spew out additional warnings.
|
enables more thorough code analysis and can spew out additional warnings.
|
||||||
To disable optimizations, set AFL_DONT_OPTIMIZE.
|
To disable optimizations, set AFL_DONT_OPTIMIZE.
|
||||||
|
However if -O... and/or -fno-unroll-loops are set, these are not
|
||||||
|
overriden.
|
||||||
|
|
||||||
- Setting AFL_USE_ASAN automatically enables ASAN, provided that your
|
- Setting AFL_USE_ASAN automatically enables ASAN, provided that your
|
||||||
compiler supports that. Note that fuzzing with ASAN is mildly challenging
|
compiler supports that. Note that fuzzing with ASAN is mildly challenging
|
||||||
@ -44,7 +58,7 @@ tools make fairly broad use of environmental variables:
|
|||||||
you instrument hand-written assembly when compiling clang code by plugging
|
you instrument hand-written assembly when compiling clang code by plugging
|
||||||
a normalizer into the chain. (There is no equivalent feature for GCC.)
|
a normalizer into the chain. (There is no equivalent feature for GCC.)
|
||||||
|
|
||||||
- Setting AFL_INST_RATIO to a percentage between 0% and 100% controls the
|
- Setting AFL_INST_RATIO to a percentage between 0 and 100% controls the
|
||||||
probability of instrumenting every branch. This is (very rarely) useful
|
probability of instrumenting every branch. This is (very rarely) useful
|
||||||
when dealing with exceptionally complex programs that saturate the output
|
when dealing with exceptionally complex programs that saturate the output
|
||||||
bitmap. Examples include v8, ffmpeg, and perl.
|
bitmap. Examples include v8, ffmpeg, and perl.
|
||||||
@ -55,19 +69,16 @@ tools make fairly broad use of environmental variables:
|
|||||||
Setting AFL_INST_RATIO to 0 is a valid choice. This will instrument only
|
Setting AFL_INST_RATIO to 0 is a valid choice. This will instrument only
|
||||||
the transitions between function entry points, but not individual branches.
|
the transitions between function entry points, but not individual branches.
|
||||||
|
|
||||||
|
Note that this is an outdated variable. A few instances (e.g. afl-gcc)
|
||||||
|
still support these, but state-of-the-art (e.g. LLVM LTO and LLVM PCGUARD)
|
||||||
|
do not need this.
|
||||||
|
|
||||||
- AFL_NO_BUILTIN causes the compiler to generate code suitable for use with
|
- AFL_NO_BUILTIN causes the compiler to generate code suitable for use with
|
||||||
libtokencap.so (but perhaps running a bit slower than without the flag).
|
libtokencap.so (but perhaps running a bit slower than without the flag).
|
||||||
|
|
||||||
- TMPDIR is used by afl-as for temporary files; if this variable is not set,
|
- TMPDIR is used by afl-as for temporary files; if this variable is not set,
|
||||||
the tool defaults to /tmp.
|
the tool defaults to /tmp.
|
||||||
|
|
||||||
- Setting AFL_KEEP_ASSEMBLY prevents afl-as from deleting instrumented
|
|
||||||
assembly files. Useful for troubleshooting problems or understanding how
|
|
||||||
the tool works. To get them in a predictable place, try something like:
|
|
||||||
|
|
||||||
mkdir assembly_here
|
|
||||||
TMPDIR=$PWD/assembly_here AFL_KEEP_ASSEMBLY=1 make clean all
|
|
||||||
|
|
||||||
- If you are a weird person that wants to compile and instrument asm
|
- If you are a weird person that wants to compile and instrument asm
|
||||||
text files then use the AFL_AS_FORCE_INSTRUMENT variable:
|
text files then use the AFL_AS_FORCE_INSTRUMENT variable:
|
||||||
AFL_AS_FORCE_INSTRUMENT=1 afl-gcc foo.s -o foo
|
AFL_AS_FORCE_INSTRUMENT=1 afl-gcc foo.s -o foo
|
||||||
@ -78,19 +89,24 @@ tools make fairly broad use of environmental variables:
|
|||||||
- Setting AFL_CAL_FAST will speed up the initial calibration, if the
|
- Setting AFL_CAL_FAST will speed up the initial calibration, if the
|
||||||
application is very slow
|
application is very slow
|
||||||
|
|
||||||
## 2) Settings for afl-clang-fast / afl-clang-fast++ / afl-gcc-fast / afl-g++-fast
|
## 2) Settings for LLVM and LTO: afl-clang-fast / afl-clang-fast++ / afl-clang-lto / afl-clang-lto++
|
||||||
|
|
||||||
The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset
|
The native instrumentation helpers (instrumentation and gcc_plugin) accept a subset
|
||||||
of the settings discussed in section #1, with the exception of:
|
of the settings discussed in section #1, with the exception of:
|
||||||
|
|
||||||
|
- LLVM modes support `AFL_LLVM_DICT2FILE=/absolute/path/file.txt` which will
|
||||||
|
write all constant string comparisons to this file to be used with
|
||||||
|
afl-fuzz' `-x` option.
|
||||||
|
|
||||||
- AFL_AS, since this toolchain does not directly invoke GNU as.
|
- AFL_AS, since this toolchain does not directly invoke GNU as.
|
||||||
|
|
||||||
- TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are
|
- TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are
|
||||||
created.
|
created.
|
||||||
|
|
||||||
- AFL_INST_RATIO, as we by default use collision free instrumentation.
|
- AFL_INST_RATIO, as we by default collision free instrumentation is used.
|
||||||
|
Not all passes support this option though as it is an outdated feature.
|
||||||
|
|
||||||
Then there are a few specific features that are only available in llvm_mode:
|
Then there are a few specific features that are only available in instrumentation:
|
||||||
|
|
||||||
### Select the instrumentation mode
|
### Select the instrumentation mode
|
||||||
|
|
||||||
@ -121,7 +137,7 @@ Then there are a few specific features that are only available in llvm_mode:
|
|||||||
|
|
||||||
None of the following options are necessary to be used and are rather for
|
None of the following options are necessary to be used and are rather for
|
||||||
manual use (which only ever the author of this LTO implementation will use).
|
manual use (which only ever the author of this LTO implementation will use).
|
||||||
These are used if several seperated instrumentations are performed which
|
These are used if several seperated instrumentation are performed which
|
||||||
are then later combined.
|
are then later combined.
|
||||||
|
|
||||||
- AFL_LLVM_DOCUMENT_IDS=file will document to a file which edge ID was given
|
- AFL_LLVM_DOCUMENT_IDS=file will document to a file which edge ID was given
|
||||||
@ -136,7 +152,7 @@ Then there are a few specific features that are only available in llvm_mode:
|
|||||||
- AFL_LLVM_LTO_DONTWRITEID prevents that the highest location ID written
|
- AFL_LLVM_LTO_DONTWRITEID prevents that the highest location ID written
|
||||||
into the instrumentation is set in a global variable
|
into the instrumentation is set in a global variable
|
||||||
|
|
||||||
See llvm_mode/README.LTO.md for more information.
|
See instrumentation/README.LTO.md for more information.
|
||||||
|
|
||||||
### INSTRIM
|
### INSTRIM
|
||||||
|
|
||||||
@ -154,7 +170,7 @@ Then there are a few specific features that are only available in llvm_mode:
|
|||||||
afl-fuzz will only be able to see the path the loop took, but not how
|
afl-fuzz will only be able to see the path the loop took, but not how
|
||||||
many times it was called (unless it is a complex loop).
|
many times it was called (unless it is a complex loop).
|
||||||
|
|
||||||
See llvm_mode/README.instrim.md
|
See instrumentation/README.instrim.md
|
||||||
|
|
||||||
### NGRAM
|
### NGRAM
|
||||||
|
|
||||||
@ -165,7 +181,7 @@ Then there are a few specific features that are only available in llvm_mode:
|
|||||||
config.h to at least 18 and maybe up to 20 for this as otherwise too
|
config.h to at least 18 and maybe up to 20 for this as otherwise too
|
||||||
many map collisions occur.
|
many map collisions occur.
|
||||||
|
|
||||||
See llvm_mode/README.ctx.md
|
See instrumentation/README.ctx.md
|
||||||
|
|
||||||
### CTX
|
### CTX
|
||||||
|
|
||||||
@ -176,7 +192,7 @@ Then there are a few specific features that are only available in llvm_mode:
|
|||||||
config.h to at least 18 and maybe up to 20 for this as otherwise too
|
config.h to at least 18 and maybe up to 20 for this as otherwise too
|
||||||
many map collisions occur.
|
many map collisions occur.
|
||||||
|
|
||||||
See llvm_mode/README.ngram.md
|
See instrumentation/README.ngram.md
|
||||||
|
|
||||||
### LAF-INTEL
|
### LAF-INTEL
|
||||||
|
|
||||||
@ -196,17 +212,17 @@ Then there are a few specific features that are only available in llvm_mode:
|
|||||||
|
|
||||||
- Setting AFL_LLVM_LAF_ALL sets all of the above
|
- Setting AFL_LLVM_LAF_ALL sets all of the above
|
||||||
|
|
||||||
See llvm_mode/README.laf-intel.md for more information.
|
See instrumentation/README.laf-intel.md for more information.
|
||||||
|
|
||||||
### INSTRUMENT LIST (selectively instrument files and functions)
|
### INSTRUMENT LIST (selectively instrument files and functions)
|
||||||
|
|
||||||
This feature allows selective instrumentation of the source
|
This feature allows selectively instrumentation of the source
|
||||||
|
|
||||||
- Setting AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST with a filenames and/or
|
- Setting AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST with a filenames and/or
|
||||||
function will only instrument (or skip) those files that match the names
|
function will only instrument (or skip) those files that match the names
|
||||||
listed in the specified file.
|
listed in the specified file.
|
||||||
|
|
||||||
See llvm_mode/README.instrument_list.md for more information.
|
See instrumentation/README.instrument_list.md for more information.
|
||||||
|
|
||||||
### NOT_ZERO
|
### NOT_ZERO
|
||||||
|
|
||||||
@ -220,27 +236,34 @@ Then there are a few specific features that are only available in llvm_mode:
|
|||||||
test. If the target performs only few loops then this will give a
|
test. If the target performs only few loops then this will give a
|
||||||
small performance boost.
|
small performance boost.
|
||||||
|
|
||||||
See llvm_mode/README.neverzero.md
|
See instrumentation/README.neverzero.md
|
||||||
|
|
||||||
### CMPLOG
|
### CMPLOG
|
||||||
|
|
||||||
- Setting AFL_LLVM_CMPLOG=1 during compilation will tell afl-clang-fast to
|
- Setting AFL_LLVM_CMPLOG=1 during compilation will tell afl-clang-fast to
|
||||||
produce a CmpLog binary. See llvm_mode/README.cmplog.md
|
produce a CmpLog binary. See instrumentation/README.cmplog.md
|
||||||
|
|
||||||
See llvm_mode/README.neverzero.md
|
See instrumentation/README.neverzero.md
|
||||||
|
|
||||||
Then there are a few specific features that are only available in the gcc_plugin:
|
## 3) Settings for GCC / GCC_PLUGIN modes
|
||||||
|
|
||||||
### INSTRUMENT_FILE
|
Then there are a few specific features that are only available in GCC and
|
||||||
|
GCC_PLUGIN mode.
|
||||||
|
|
||||||
This feature allows selective instrumentation of the source
|
- Setting AFL_KEEP_ASSEMBLY prevents afl-as from deleting instrumented
|
||||||
|
assembly files. Useful for troubleshooting problems or understanding how
|
||||||
|
the tool works. (GCC mode only)
|
||||||
|
To get them in a predictable place, try something like:
|
||||||
|
|
||||||
- Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those
|
mkdir assembly_here
|
||||||
files that match the names listed in this file (one filename per line).
|
TMPDIR=$PWD/assembly_here AFL_KEEP_ASSEMBLY=1 make clean all
|
||||||
|
|
||||||
|
- Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those
|
||||||
|
files that match the names listed in this file (one filename per line).
|
||||||
See gcc_plugin/README.instrument_list.md for more information.
|
See gcc_plugin/README.instrument_list.md for more information.
|
||||||
|
(GCC_PLUGIN mode only)
|
||||||
|
|
||||||
## 3) Settings for afl-fuzz
|
## 4) Settings for afl-fuzz
|
||||||
|
|
||||||
The main fuzzer binary accepts several options that disable a couple of sanity
|
The main fuzzer binary accepts several options that disable a couple of sanity
|
||||||
checks or alter some of the more exotic semantics of the tool:
|
checks or alter some of the more exotic semantics of the tool:
|
||||||
@ -278,14 +301,6 @@ checks or alter some of the more exotic semantics of the tool:
|
|||||||
don't want AFL to spend too much time classifying that stuff and just
|
don't want AFL to spend too much time classifying that stuff and just
|
||||||
rapidly put all timeouts in that bin.
|
rapidly put all timeouts in that bin.
|
||||||
|
|
||||||
- Setting AFL_FORKSRV_INIT_TMOUT allows yout to specify a different timeout
|
|
||||||
to wait for the forkserver to spin up. The default is the `-t` value times
|
|
||||||
`FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the
|
|
||||||
default would wait `1000` milis. Setting a different time here is useful
|
|
||||||
if the target has a very slow startup time, for example when doing
|
|
||||||
full-system fuzzing or emulation, but you don't want the actual runs
|
|
||||||
to wait too long for timeouts.
|
|
||||||
|
|
||||||
- AFL_NO_ARITH causes AFL to skip most of the deterministic arithmetics.
|
- AFL_NO_ARITH causes AFL to skip most of the deterministic arithmetics.
|
||||||
This can be useful to speed up the fuzzing of text-based file formats.
|
This can be useful to speed up the fuzzing of text-based file formats.
|
||||||
|
|
||||||
@ -377,22 +392,12 @@ checks or alter some of the more exotic semantics of the tool:
|
|||||||
Note that this setting inhibits some of the user-friendly diagnostics
|
Note that this setting inhibits some of the user-friendly diagnostics
|
||||||
normally done when starting up the forkserver and causes a pretty
|
normally done when starting up the forkserver and causes a pretty
|
||||||
significant performance drop.
|
significant performance drop.
|
||||||
|
|
||||||
- Setting AFL_MAX_DET_EXTRAS changes the count of dictionary entries/extras
|
|
||||||
(default 200), after which the entries will be used probabilistically.
|
|
||||||
So, if the dict/extras file (`-x`) contains more tokens than this threshold,
|
|
||||||
not all of the tokens will be used in each fuzzing step, every time.
|
|
||||||
Instead, there is a chance that the entry will be skipped during fuzzing.
|
|
||||||
This makes sure that the fuzzer doesn't spend all its time only inserting
|
|
||||||
the extras, but will still do other mutations. However, it decreases the
|
|
||||||
likelihood for each token to be inserted, before the next queue entry is fuzzed.
|
|
||||||
Either way, all tokens will be used eventually, in a longer fuzzing campaign.
|
|
||||||
|
|
||||||
- Outdated environment variables that are that not supported anymore:
|
- Outdated environment variables that are that not supported anymore:
|
||||||
AFL_DEFER_FORKSRV
|
AFL_DEFER_FORKSRV
|
||||||
AFL_PERSISTENT
|
AFL_PERSISTENT
|
||||||
|
|
||||||
## 4) Settings for afl-qemu-trace
|
## 5) Settings for afl-qemu-trace
|
||||||
|
|
||||||
The QEMU wrapper used to instrument binary-only code supports several settings:
|
The QEMU wrapper used to instrument binary-only code supports several settings:
|
||||||
|
|
||||||
@ -446,7 +451,7 @@ The QEMU wrapper used to instrument binary-only code supports several settings:
|
|||||||
stack pointer in which QEMU can find the return address when `start addr` is
|
stack pointer in which QEMU can find the return address when `start addr` is
|
||||||
hitted.
|
hitted.
|
||||||
|
|
||||||
## 5) Settings for afl-cmin
|
## 6) Settings for afl-cmin
|
||||||
|
|
||||||
The corpus minimization script offers very little customization:
|
The corpus minimization script offers very little customization:
|
||||||
|
|
||||||
@ -472,12 +477,12 @@ to match when minimizing crashes. This will make minimization less useful, but
|
|||||||
may prevent the tool from "jumping" from one crashing condition to another in
|
may prevent the tool from "jumping" from one crashing condition to another in
|
||||||
very buggy software. You probably want to combine it with the -e flag.
|
very buggy software. You probably want to combine it with the -e flag.
|
||||||
|
|
||||||
## 7) Settings for afl-analyze
|
## 8) Settings for afl-analyze
|
||||||
|
|
||||||
You can set AFL_ANALYZE_HEX to get file offsets printed as hexadecimal instead
|
You can set AFL_ANALYZE_HEX to get file offsets printed as hexadecimal instead
|
||||||
of decimal.
|
of decimal.
|
||||||
|
|
||||||
## 8) Settings for libdislocator
|
## 9) Settings for libdislocator
|
||||||
|
|
||||||
The library honors these environmental variables:
|
The library honors these environmental variables:
|
||||||
|
|
||||||
@ -499,12 +504,12 @@ The library honors these environmental variables:
|
|||||||
- AFL_ALIGNED_ALLOC=1 will force the alignment of the allocation size to
|
- AFL_ALIGNED_ALLOC=1 will force the alignment of the allocation size to
|
||||||
max_align_t to be compliant with the C standard.
|
max_align_t to be compliant with the C standard.
|
||||||
|
|
||||||
## 9) Settings for libtokencap
|
## 10) Settings for libtokencap
|
||||||
|
|
||||||
This library accepts AFL_TOKEN_FILE to indicate the location to which the
|
This library accepts AFL_TOKEN_FILE to indicate the location to which the
|
||||||
discovered tokens should be written.
|
discovered tokens should be written.
|
||||||
|
|
||||||
## 10) Third-party variables set by afl-fuzz & other tools
|
## 11) Third-party variables set by afl-fuzz & other tools
|
||||||
|
|
||||||
Several variables are not directly interpreted by afl-fuzz, but are set to
|
Several variables are not directly interpreted by afl-fuzz, but are set to
|
||||||
optimal values if not already present in the environment:
|
optimal values if not already present in the environment:
|
||||||
|
@ -3,48 +3,18 @@
|
|||||||
In the following, we describe a variety of ideas that could be implemented
|
In the following, we describe a variety of ideas that could be implemented
|
||||||
for future AFL++ versions.
|
for future AFL++ versions.
|
||||||
|
|
||||||
For GSOC2020 interested students please see
|
## Analysis software
|
||||||
[https://github.com/AFLplusplus/AFLplusplus/issues/208](https://github.com/AFLplusplus/AFLplusplus/issues/208)
|
|
||||||
|
|
||||||
## Flexible Grammar Mutator (currently in development)
|
Currently analysis is done by using afl-plot, which is rather outdated.
|
||||||
|
A GTK or browser tool to create run-time analysis based on fuzzer_stats,
|
||||||
Currently, AFL++'s mutation does not have deeper knowledge about the fuzzed
|
queue/id* information and plot_data that allows for zooming in and out,
|
||||||
binary, apart from feedback, even though the developer may have insights
|
changing min/max display values etc. and doing that for a single run,
|
||||||
about the target.
|
different runs and campaigns vs campaigns.
|
||||||
|
Interesting values are execs, and execs/s, edges discovered (total, when
|
||||||
A developer may choose to provide dictionaries and implement own mutations
|
each edge was discovered and which other fuzzer share finding that edge),
|
||||||
in python or C, but an easy mutator that behaves according to a given grammar,
|
test cases executed.
|
||||||
does not exist.
|
It should be clickable which value is X and Y axis, zoom factor, log scaling
|
||||||
|
on-off, etc.
|
||||||
State-of-the-art research on grammar fuzzing has some problems in their
|
|
||||||
implementations like code quality, scalability, or ease of use and other
|
|
||||||
common issues of the academic code.
|
|
||||||
|
|
||||||
We aim to develop a pluggable grammar mutator for afl++ that combines
|
|
||||||
various results.
|
|
||||||
|
|
||||||
Mentor: andreafioraldi
|
|
||||||
|
|
||||||
## perf-fuzz Linux Kernel Module
|
|
||||||
|
|
||||||
Expand on [snapshot LKM](https://github.com/AFLplusplus/AFL-Snapshot-LKM)
|
|
||||||
To make it thread safe, can snapshot several processes at once and increase
|
|
||||||
overall performance.
|
|
||||||
|
|
||||||
Mentor: any
|
|
||||||
|
|
||||||
## QEMU 5-based Instrumentation
|
|
||||||
|
|
||||||
First tests to use QEMU 4 for binary-only AFL++ showed that caching behavior
|
|
||||||
changed, which vastly decreases fuzzing speeds.
|
|
||||||
|
|
||||||
In this task test if QEMU 5 performs better and port the afl++ QEMU 3.1
|
|
||||||
patches to QEMU 5.
|
|
||||||
|
|
||||||
Understanding the current instrumentation and fixing the current caching
|
|
||||||
issues will be needed.
|
|
||||||
|
|
||||||
Mentor: andreafioraldi
|
|
||||||
|
|
||||||
## WASM Instrumentation
|
## WASM Instrumentation
|
||||||
|
|
||||||
@ -66,20 +36,6 @@ Either improve a single mutator thorugh learning of many different bugs
|
|||||||
|
|
||||||
Mentor: domenukk
|
Mentor: domenukk
|
||||||
|
|
||||||
## Reengineer `afl-fuzz` as Thread Safe, Embeddable Library (currently in development)
|
|
||||||
|
|
||||||
Right now, afl-fuzz is single threaded, cannot safely be embedded in tools,
|
|
||||||
and not multi-threaded. It makes use of a large number of globals, must always
|
|
||||||
be the parent process and exec child processes.
|
|
||||||
Instead, afl-fuzz could be refactored to contain no global state and globals.
|
|
||||||
This allows for different use cases that could be implemented during this
|
|
||||||
project.
|
|
||||||
Note that in the mean time a lot has happened here already, but e.g. making
|
|
||||||
it all work and implement multithreading in afl-fuzz ... there is still quite
|
|
||||||
some work to do.
|
|
||||||
|
|
||||||
Mentor: hexcoder- or vanhauser-thc
|
|
||||||
|
|
||||||
## Collision-free Binary-Only Maps
|
## Collision-free Binary-Only Maps
|
||||||
|
|
||||||
AFL++ supports collison-free maps using an LTO (link-time-optimization) pass.
|
AFL++ supports collison-free maps using an LTO (link-time-optimization) pass.
|
||||||
|
@ -30,10 +30,10 @@ Check out the `fuzzer_stats` file in the AFL output dir or try `afl-whatsup`.
|
|||||||
It could be important - consult docs/status_screen.md right away!
|
It could be important - consult docs/status_screen.md right away!
|
||||||
|
|
||||||
## Know your target? Convert it to persistent mode for a huge performance gain!
|
## Know your target? Convert it to persistent mode for a huge performance gain!
|
||||||
Consult section #5 in llvm_mode/README.md for tips.
|
Consult section #5 in README.llvm.md for tips.
|
||||||
|
|
||||||
## Using clang?
|
## Using clang?
|
||||||
Check out llvm_mode/ for a faster alternative to afl-gcc!
|
Check out instrumentation/ for a faster alternative to afl-gcc!
|
||||||
|
|
||||||
## Did you know that AFL can fuzz closed-source or cross-platform binaries?
|
## Did you know that AFL can fuzz closed-source or cross-platform binaries?
|
||||||
Check out qemu_mode/README.md and unicorn_mode/README.md for more.
|
Check out qemu_mode/README.md and unicorn_mode/README.md for more.
|
||||||
|
@ -51,7 +51,7 @@ a file.
|
|||||||
## 3. Use LLVM instrumentation
|
## 3. Use LLVM instrumentation
|
||||||
|
|
||||||
When fuzzing slow targets, you can gain 20-100% performance improvement by
|
When fuzzing slow targets, you can gain 20-100% performance improvement by
|
||||||
using the LLVM-based instrumentation mode described in [the llvm_mode README](../llvm_mode/README.md).
|
using the LLVM-based instrumentation mode described in [the instrumentation README](../instrumentation/README.llvm.md).
|
||||||
Note that this mode requires the use of clang and will not work with GCC.
|
Note that this mode requires the use of clang and will not work with GCC.
|
||||||
|
|
||||||
The LLVM mode also offers a "persistent", in-process fuzzing mode that can
|
The LLVM mode also offers a "persistent", in-process fuzzing mode that can
|
||||||
@ -62,12 +62,12 @@ modes require you to edit the source code of the fuzzed program, but the
|
|||||||
changes often amount to just strategically placing a single line or two.
|
changes often amount to just strategically placing a single line or two.
|
||||||
|
|
||||||
If there are important data comparisons performed (e.g. `strcmp(ptr, MAGIC_HDR)`)
|
If there are important data comparisons performed (e.g. `strcmp(ptr, MAGIC_HDR)`)
|
||||||
then using laf-intel (see llvm_mode/README.laf-intel.md) will help `afl-fuzz` a lot
|
then using laf-intel (see instrumentation/README.laf-intel.md) will help `afl-fuzz` a lot
|
||||||
to get to the important parts in the code.
|
to get to the important parts in the code.
|
||||||
|
|
||||||
If you are only interested in specific parts of the code being fuzzed, you can
|
If you are only interested in specific parts of the code being fuzzed, you can
|
||||||
instrument_files the files that are actually relevant. This improves the speed and
|
instrument_files the files that are actually relevant. This improves the speed and
|
||||||
accuracy of afl. See llvm_mode/README.instrument_list.md
|
accuracy of afl. See instrumentation/README.instrument_list.md
|
||||||
|
|
||||||
Also use the InsTrim mode on larger binaries, this improves performance and
|
Also use the InsTrim mode on larger binaries, this improves performance and
|
||||||
coverage a lot.
|
coverage a lot.
|
||||||
@ -110,7 +110,7 @@ e.g.:
|
|||||||
https://launchpad.net/libeatmydata
|
https://launchpad.net/libeatmydata
|
||||||
|
|
||||||
In programs that are slow due to unavoidable initialization overhead, you may
|
In programs that are slow due to unavoidable initialization overhead, you may
|
||||||
want to try the LLVM deferred forkserver mode (see llvm_mode/README.md),
|
want to try the LLVM deferred forkserver mode (see README.llvm.md),
|
||||||
which can give you speed gains up to 10x, as mentioned above.
|
which can give you speed gains up to 10x, as mentioned above.
|
||||||
|
|
||||||
Last but not least, if you are using ASAN and the performance is unacceptable,
|
Last but not least, if you are using ASAN and the performance is unacceptable,
|
||||||
|
@ -52,7 +52,7 @@ options.
|
|||||||
Provides an evolutionary instrumentation-guided fuzzing harness that allows
|
Provides an evolutionary instrumentation-guided fuzzing harness that allows
|
||||||
some programs to be fuzzed without the fork / execve overhead. (Similar
|
some programs to be fuzzed without the fork / execve overhead. (Similar
|
||||||
functionality is now available as the "persistent" feature described in
|
functionality is now available as the "persistent" feature described in
|
||||||
[the llvm_mode readme](../llvm_mode/README.md))
|
[the llvm_mode readme](../instrumentation/README.llvm.md))
|
||||||
|
|
||||||
http://llvm.org/docs/LibFuzzer.html
|
http://llvm.org/docs/LibFuzzer.html
|
||||||
|
|
||||||
@ -245,7 +245,7 @@ https://code.google.com/p/address-sanitizer/wiki/AsanCoverage#Coverage_counters
|
|||||||
### AFL JS (Han Choongwoo)
|
### AFL JS (Han Choongwoo)
|
||||||
|
|
||||||
One-off optimizations to speed up the fuzzing of JavaScriptCore (now likely
|
One-off optimizations to speed up the fuzzing of JavaScriptCore (now likely
|
||||||
superseded by LLVM deferred forkserver init - see llvm_mode/README.md).
|
superseded by LLVM deferred forkserver init - see README.llvm.md).
|
||||||
|
|
||||||
https://github.com/tunz/afl-fuzz-js
|
https://github.com/tunz/afl-fuzz-js
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@ there are several things to look at:
|
|||||||
- Multiple threads executing at once in semi-random order. This is harmless
|
- Multiple threads executing at once in semi-random order. This is harmless
|
||||||
when the 'stability' metric stays over 90% or so, but can become an issue
|
when the 'stability' metric stays over 90% or so, but can become an issue
|
||||||
if not. Here's what to try:
|
if not. Here's what to try:
|
||||||
* Use afl-clang-fast from [llvm_mode](../llvm_mode/) - it uses a thread-local tracking
|
* Use afl-clang-fast from [instrumentation](../instrumentation/) - it uses a thread-local tracking
|
||||||
model that is less prone to concurrency issues,
|
model that is less prone to concurrency issues,
|
||||||
* See if the target can be compiled or run without threads. Common
|
* See if the target can be compiled or run without threads. Common
|
||||||
`./configure` options include `--without-threads`, `--disable-pthreads`, or
|
`./configure` options include `--without-threads`, `--disable-pthreads`, or
|
||||||
|
@ -47,7 +47,7 @@ Here's a quick overview of the stuff you can find in this directory:
|
|||||||
|
|
||||||
Note that the minimize_corpus.sh tool has graduated from the examples/
|
Note that the minimize_corpus.sh tool has graduated from the examples/
|
||||||
directory and is now available as ../afl-cmin. The LLVM mode has likewise
|
directory and is now available as ../afl-cmin. The LLVM mode has likewise
|
||||||
graduated to ../llvm_mode/*.
|
graduated to ../instrumentation/*.
|
||||||
|
|
||||||
Most of the tools in this directory are meant chiefly as examples that need to
|
Most of the tools in this directory are meant chiefly as examples that need to
|
||||||
be tweaked for your specific needs. They come with some basic documentation,
|
be tweaked for your specific needs. They come with some basic documentation,
|
||||||
|
@ -27,7 +27,7 @@ EOF
|
|||||||
# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang.
|
# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang.
|
||||||
clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c
|
clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c
|
||||||
# Build afl-llvm-rt.o.c from the AFL distribution.
|
# Build afl-llvm-rt.o.c from the AFL distribution.
|
||||||
clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c
|
clang -c -w $AFL_HOME/instrumentation/afl-llvm-rt.o.c
|
||||||
# Build this file, link it with afl-llvm-rt.o.o and the target code.
|
# Build this file, link it with afl-llvm-rt.o.o and the target code.
|
||||||
clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o
|
clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o
|
||||||
# Run AFL:
|
# Run AFL:
|
||||||
|
122
examples/analysis_scripts/queue2csv.sh
Executable file
122
examples/analysis_scripts/queue2csv.sh
Executable file
@ -0,0 +1,122 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
test -z "$1" -o -z "$2" -o "$1" = "-h" -o "$1" = "-hh" -o "$1" = "--help" -o '!' -d "$1" && {
|
||||||
|
echo "Syntax: [-n] $0 out-directory file.csv [\"tools/target --opt @@\"]"
|
||||||
|
echo Option -n will suppress the CSV header.
|
||||||
|
echo If the target execution command is supplied then also edge coverage is gathered.
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function getval() {
|
||||||
|
VAL=""
|
||||||
|
if [ "$file" != "${file/$1/}" ]; then
|
||||||
|
TMP="${file/*$1:/}"
|
||||||
|
VAL="${TMP/,*/}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
SKIP=
|
||||||
|
if [ "$1" = "-n" ]; then
|
||||||
|
SKIP=1
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
test -n "$4" && { echo "Error: too many commandline options. Target command and options including @@ have to be passed within \"\"!"; exit 1; }
|
||||||
|
|
||||||
|
test -d "$1"/queue && OUT="$1/queue" || OUT="$1"
|
||||||
|
|
||||||
|
OK=`ls $OUT/id:000000,time:0,orig:* 2> /dev/null`
|
||||||
|
if [ -n "$OK" ]; then
|
||||||
|
LISTCMD="ls $OUT/id:"*
|
||||||
|
else
|
||||||
|
LISTCMD="ls -tr $OUT/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ID=;SRC=;TIME=;OP=;POS=;REP=;EDGES=;EDGES_TOTAL=;
|
||||||
|
DIR="$OUT/../stats"
|
||||||
|
rm -rf "$DIR"
|
||||||
|
> "$2" || exit 1
|
||||||
|
mkdir "$DIR" || exit 1
|
||||||
|
> "$DIR/../edges.txt" || exit 1
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
if [ -z "$SKIP" ]; then
|
||||||
|
echo "time;\"filename\";id;src;new_cov;edges;total_edges;\"op\";pos;rep;unique_edges"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$LISTCMD | grep -v ,sync: | sed 's/.*id:/id:/g' | while read file; do
|
||||||
|
|
||||||
|
if [ -n "$3" ]; then
|
||||||
|
|
||||||
|
TMP=${3/@@/$OUT/$file}
|
||||||
|
|
||||||
|
if [ "$TMP" = "$3" ]; then
|
||||||
|
|
||||||
|
cat "$OUT/$file" | afl-showmap -o "$DIR/$file" -q -- $3 >/dev/null 2>&1
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
afl-showmap -o "$DIR/$file" -q -- $TMP >/dev/null 2>&1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
{ cat "$DIR/$file" | sed 's/:.*//' ; cat "$DIR/../edges.txt" ; } | sort -nu > $DIR/../edges.txt.tmp
|
||||||
|
mv $DIR/../edges.txt.tmp $DIR/../edges.txt
|
||||||
|
EDGES=$(cat "$DIR/$file" | wc -l)
|
||||||
|
EDGES_TOTAL=$(cat "$DIR/../edges.txt" | wc -l)
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
getval id; ID="$VAL"
|
||||||
|
getval src; SRC="$VAL"
|
||||||
|
getval time; TIME="$VAL"
|
||||||
|
getval op; OP="$VAL"
|
||||||
|
getval pos; POS="$VAL"
|
||||||
|
getval rep; REP="$VAL"
|
||||||
|
if [ "$file" != "${file/+cov/}" ]; then
|
||||||
|
COV=1
|
||||||
|
else
|
||||||
|
COV=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then
|
||||||
|
echo "$TIME;\"$file\";$ID;$SRC;$COV;$EDGES;$EDGES_TOTAL;\"$OP\";$POS;$REP;UNIQUE$file"
|
||||||
|
else
|
||||||
|
echo "$TIME;\"$file\";$ID;$SRC;$COV;;;\"$OP\";$POS;$REP;"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
} | tee "$DIR/../queue.csv" > "$2" || exit 1
|
||||||
|
|
||||||
|
if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then
|
||||||
|
|
||||||
|
cat "$DIR/"* | sed 's/:.*//' | sort -n | uniq -c | egrep '^[ \t]*1 ' | awk '{print$2}' > $DIR/../unique.txt
|
||||||
|
|
||||||
|
if [ -s "$DIR/../unique.txt" ]; then
|
||||||
|
|
||||||
|
ls "$DIR/id:"* | grep -v ",sync:" |sed 's/.*\/id:/id:/g' | while read file; do
|
||||||
|
|
||||||
|
CNT=$(sed 's/:.*//' "$DIR/$file" | tee "$DIR/../tmp.txt" | wc -l)
|
||||||
|
DIFF=$(diff -u "$DIR/../tmp.txt" "$DIR/../unique.txt" | egrep '^-[0-9]' | wc -l)
|
||||||
|
UNIQUE=$(($CNT - $DIFF))
|
||||||
|
sed -i "s/;UNIQUE$file/;$UNIQUE/" "$DIR/../queue.csv" "$2"
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f "$DIR/../tmp.txt"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
sed -i 's/;UNIQUE.*/;/' "$DIR/../queue.csv" "$2"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv "$DIR/../queue.csv" "$DIR/queue.csv"
|
||||||
|
if [ -e "$DIR/../edges.txt" ]; then mv "$DIR/../edges.txt" "$DIR/edges.txt"; fi
|
||||||
|
if [ -e "$DIR/../unique.txt" ]; then mv "$DIR/../unique.txt" "$DIR/unique.txt"; fi
|
||||||
|
|
||||||
|
echo "Created $2"
|
@ -1,159 +0,0 @@
|
|||||||
#
|
|
||||||
# american fuzzy lop++ - GCC plugin instrumentation
|
|
||||||
# -----------------------------------------------
|
|
||||||
#
|
|
||||||
# Written by Austin Seipp <aseipp@pobox.com> and
|
|
||||||
# Laszlo Szekeres <lszekeres@google.com> and
|
|
||||||
# Michal Zalewski and
|
|
||||||
# Heiko Eißfeldt <heiko@hexco.de>
|
|
||||||
#
|
|
||||||
# GCC integration design is based on the LLVM design, which comes
|
|
||||||
# from Laszlo Szekeres.
|
|
||||||
#
|
|
||||||
# Copyright 2015 Google Inc. All rights reserved.
|
|
||||||
# Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
|
||||||
#
|
|
||||||
# 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:
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
|
|
||||||
PREFIX ?= /usr/local
|
|
||||||
HELPER_PATH ?= $(PREFIX)/lib/afl
|
|
||||||
BIN_PATH ?= $(PREFIX)/bin
|
|
||||||
DOC_PATH ?= $(PREFIX)/share/doc/afl
|
|
||||||
MAN_PATH ?= $(PREFIX)/share/man/man8
|
|
||||||
|
|
||||||
VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2)
|
|
||||||
VERSION:sh= grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2
|
|
||||||
|
|
||||||
CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2
|
|
||||||
CFLAGS = -Wall -I../include -Wno-pointer-sign \
|
|
||||||
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
|
||||||
-DGCC_VERSION=\"$(GCCVER)\" -DGCC_BINDIR=\"$(GCCBINDIR)\" \
|
|
||||||
-Wno-unused-function
|
|
||||||
|
|
||||||
CXXFLAGS = -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2
|
|
||||||
CXXEFLAGS = $(CXXFLAGS) -Wall
|
|
||||||
|
|
||||||
CC = gcc
|
|
||||||
CXX = g++
|
|
||||||
|
|
||||||
MYCC=$(CC:clang=gcc)
|
|
||||||
MYCXX=$(CXX:clang++=g++)
|
|
||||||
|
|
||||||
PLUGIN_PATH = $(shell $(MYCC) -print-file-name=plugin)
|
|
||||||
PLUGIN_PATH:sh= $(MYCC) -print-file-name=plugin
|
|
||||||
PLUGIN_FLAGS = -fPIC -fno-rtti -I"$(PLUGIN_PATH)/include"
|
|
||||||
HASH=\#
|
|
||||||
|
|
||||||
GCCVER = $(shell $(MYCC) --version 2>/dev/null | awk 'NR == 1 {print $$NF}')
|
|
||||||
GCCVER:sh= gcc --version 2>/dev/null | awk 'NR == 1 {print $$NF}'
|
|
||||||
GCCBINDIR = $(shell dirname `command -v $(MYCC)` 2>/dev/null )
|
|
||||||
GCCBINDIR:sh= dirname `command -v $(MYCC)` 2>/dev/null
|
|
||||||
|
|
||||||
_SHMAT_OK= $(shell echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(MYCC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )
|
|
||||||
_SHMAT_OK:sh= echo '$(HASH)include <sys/ipc.h>@$(HASH)include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(MYCC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 ; rm -f .test2
|
|
||||||
|
|
||||||
IGNORE_MMAP=$(TEST_MMAP:1=0)
|
|
||||||
__SHMAT_OK=$(_SHMAT_OK)$(IGNORE_MMAP)
|
|
||||||
___SHMAT_OK=$(__SHMAT_OK:10=0)
|
|
||||||
SHMAT_OK=$(___SHMAT_OK:1=1)
|
|
||||||
_CFLAGS_ADD=$(SHMAT_OK:1=)
|
|
||||||
CFLAGS_ADD=$(_CFLAGS_ADD:0=-DUSEMMAP=1)
|
|
||||||
|
|
||||||
_LDFLAGS_ADD=$(SHMAT_OK:1=)
|
|
||||||
LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-lrt)
|
|
||||||
|
|
||||||
CFLAGS += $(CFLAGS_ADD)
|
|
||||||
LDFLAGS += $(LDFLAGS_ADD)
|
|
||||||
|
|
||||||
PROGS = ../afl-gcc-pass.so ../afl-gcc-fast ../afl-gcc-rt.o
|
|
||||||
|
|
||||||
all: test_shm test_deps $(PROGS) ../afl-gcc-fast.8 test_build all_done
|
|
||||||
|
|
||||||
debug:
|
|
||||||
@echo _SHMAT_OK = $(_SHMAT_OK)
|
|
||||||
@echo IGNORE_MMAP = $(IGNORE_MMAP)
|
|
||||||
@echo __SHMAT_OK = $(__SHMAT_OK)
|
|
||||||
@echo ___SHMAT_OK = $(___SHMAT_OK)
|
|
||||||
@echo SHMAT_OK = $(SHMAT_OK)
|
|
||||||
|
|
||||||
test_shm:
|
|
||||||
@if [ "$(SHMAT_OK)" == "1" ]; then \
|
|
||||||
echo "[+] shmat seems to be working."; \
|
|
||||||
rm -f .test2; \
|
|
||||||
else \
|
|
||||||
echo "[-] shmat seems not to be working, switching to mmap implementation"; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
test_deps:
|
|
||||||
@echo "[*] Checking for working '$(MYCC)'..."
|
|
||||||
@type $(MYCC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(MYCC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
|
|
||||||
# @echo "[*] Checking for gcc for plugin support..."
|
|
||||||
# @$(MYCC) -v 2>&1 | grep -q -- --enable-plugin || ( echo "[-] Oops, this gcc has not been configured with plugin support."; exit 1 )
|
|
||||||
@echo "[*] Checking for gcc plugin development header files..."
|
|
||||||
@test -d `$(MYCC) -print-file-name=plugin`/include || ( echo "[-] Oops, can't find gcc header files. Be sure to install 'gcc-X-plugin-dev'."; exit 1 )
|
|
||||||
@echo "[*] Checking for '../afl-showmap'..."
|
|
||||||
@test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 )
|
|
||||||
@echo "[+] All set and ready to build."
|
|
||||||
|
|
||||||
afl-common.o: ../src/afl-common.c
|
|
||||||
$(MYCC) $(CFLAGS) -c $< -o $@ $(LDFLAGS)
|
|
||||||
|
|
||||||
../afl-gcc-fast: afl-gcc-fast.c afl-common.o
|
|
||||||
$(MYCC) -DAFL_GCC_CC=\"$(MYCC)\" -DAFL_GCC_CXX=\"$(MYCXX)\" $(CFLAGS) afl-gcc-fast.c afl-common.o -o $@ $(LDFLAGS)
|
|
||||||
ln -sf afl-gcc-fast ../afl-g++-fast
|
|
||||||
|
|
||||||
../afl-gcc-pass.so: afl-gcc-pass.so.cc
|
|
||||||
$(MYCXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared afl-gcc-pass.so.cc -o $@
|
|
||||||
|
|
||||||
../afl-gcc-rt.o: afl-gcc-rt.o.c
|
|
||||||
$(MYCC) $(CFLAGS) -fPIC -c afl-gcc-rt.o.c -o $@
|
|
||||||
|
|
||||||
test_build: $(PROGS)
|
|
||||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
|
||||||
@unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
|
||||||
# unset AFL_USE_ASAN AFL_USE_MSAN; AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
|
||||||
@ASAN_OPTIONS=detect_leaks=0 ../afl-showmap -m none -q -o .test-instr0 ./test-instr </dev/null
|
|
||||||
@ASAN_OPTIONS=detect_leaks=0 echo 1 | ../afl-showmap -m none -q -o .test-instr1 ./test-instr
|
|
||||||
@rm -f test-instr
|
|
||||||
@trap 'rm .test-instr0 .test-instr1' 0;if cmp -s .test-instr0 .test-instr1; then echo; echo "Oops, the instrumentation 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
|
|
||||||
@echo "[+] All right, the instrumentation seems to be working!"
|
|
||||||
|
|
||||||
all_done: test_build
|
|
||||||
@echo "[+] All done! You can now use '../afl-gcc-fast' to compile programs."
|
|
||||||
|
|
||||||
.NOTPARALLEL: clean
|
|
||||||
|
|
||||||
../afl-gcc-fast.8: ../afl-gcc-fast
|
|
||||||
@echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ../$@
|
|
||||||
@echo .SH NAME >> ../$@
|
|
||||||
@echo .B $* >> ../$@
|
|
||||||
@echo >> ../$@
|
|
||||||
@echo .SH SYNOPSIS >> ../$@
|
|
||||||
@../$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ../$@
|
|
||||||
@echo >> ../$@
|
|
||||||
@echo .SH OPTIONS >> ../$@
|
|
||||||
@echo .nf >> ../$@
|
|
||||||
@../$* -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 >> ../$@
|
|
||||||
@echo .SH LICENSE >> ../$@
|
|
||||||
@echo Apache License Version 2.0, January 2004 >> ../$@
|
|
||||||
ln -sf afl-gcc-fast.8 ../afl-g++-fast.8
|
|
||||||
|
|
||||||
install: all
|
|
||||||
install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH)
|
|
||||||
install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH)
|
|
||||||
install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md
|
|
||||||
install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2
|
|
||||||
rm -f $(PROGS) afl-common.o ../afl-g++-fast ../afl-g*-fast.8
|
|
@ -1,73 +0,0 @@
|
|||||||
========================================
|
|
||||||
Using afl++ with partial instrumentation
|
|
||||||
========================================
|
|
||||||
|
|
||||||
This file describes how you can selectively instrument only the source files
|
|
||||||
that are interesting to you using the gcc instrumentation provided by
|
|
||||||
afl++.
|
|
||||||
|
|
||||||
Plugin by hexcoder-.
|
|
||||||
|
|
||||||
|
|
||||||
## 1) Description and purpose
|
|
||||||
|
|
||||||
When building and testing complex programs where only a part of the program is
|
|
||||||
the fuzzing target, it often helps to only instrument the necessary parts of
|
|
||||||
the program, leaving the rest uninstrumented. This helps to focus the fuzzer
|
|
||||||
on the important parts of the program, avoiding undesired noise and
|
|
||||||
disturbance by uninteresting code being exercised.
|
|
||||||
|
|
||||||
For this purpose, I have added a "partial instrumentation" support to the gcc
|
|
||||||
plugin of AFLFuzz that allows you to specify on a source file level which files
|
|
||||||
should be compiled with or without instrumentation.
|
|
||||||
|
|
||||||
|
|
||||||
## 2) Building the gcc plugin
|
|
||||||
|
|
||||||
The new code is part of the existing afl++ gcc plugin in the gcc_plugin/
|
|
||||||
subdirectory. There is nothing specifically to do :)
|
|
||||||
|
|
||||||
|
|
||||||
## 3) How to use the partial instrumentation mode
|
|
||||||
|
|
||||||
In order to build with partial instrumentation, you need to build with
|
|
||||||
afl-gcc-fast and afl-g++-fast respectively. The only required change is
|
|
||||||
that you need to set the environment variable AFL_GCC_INSTRUMENT_FILE when calling
|
|
||||||
the compiler.
|
|
||||||
|
|
||||||
The environment variable must point to a file containing all the filenames
|
|
||||||
that should be instrumented. For matching, the filename that is being compiled
|
|
||||||
must end in the filename entry contained in this instrument list (to avoid breaking
|
|
||||||
the matching when absolute paths are used during compilation).
|
|
||||||
|
|
||||||
For example if your source tree looks like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
project/
|
|
||||||
project/feature_a/a1.cpp
|
|
||||||
project/feature_a/a2.cpp
|
|
||||||
project/feature_b/b1.cpp
|
|
||||||
project/feature_b/b2.cpp
|
|
||||||
```
|
|
||||||
|
|
||||||
and you only want to test feature_a, then create a instrument list file containing:
|
|
||||||
|
|
||||||
```
|
|
||||||
feature_a/a1.cpp
|
|
||||||
feature_a/a2.cpp
|
|
||||||
```
|
|
||||||
|
|
||||||
However if the instrument list file contains only this, it works as well:
|
|
||||||
|
|
||||||
```
|
|
||||||
a1.cpp
|
|
||||||
a2.cpp
|
|
||||||
```
|
|
||||||
|
|
||||||
but it might lead to files being unwantedly instrumented if the same filename
|
|
||||||
exists somewhere else in the project directories.
|
|
||||||
|
|
||||||
The created instrument list file is then set to AFL_GCC_INSTRUMENT_FILE when you compile
|
|
||||||
your program. For each file that didn't match the instrument list, the compiler will
|
|
||||||
issue a warning at the end stating that no blocks were instrumented. If you
|
|
||||||
didn't intend to instrument that file, then you can safely ignore that warning.
|
|
@ -1,406 +0,0 @@
|
|||||||
/*
|
|
||||||
american fuzzy lop++ - GCC wrapper for GCC plugin
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
Written by Austin Seipp <aseipp@pobox.com> and
|
|
||||||
Laszlo Szekeres <lszekeres@google.com> and
|
|
||||||
Michal Zalewski
|
|
||||||
|
|
||||||
GCC integration design is based on the LLVM design, which comes
|
|
||||||
from Laszlo Szekeres.
|
|
||||||
|
|
||||||
Copyright 2015 Google Inc. All rights reserved.
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
This program is a drop-in replacement for gcc, similar in most
|
|
||||||
respects to ../afl-gcc, but with compiler instrumentation through a
|
|
||||||
plugin. It tries to figure out compilation mode, adds a bunch of
|
|
||||||
flags, and then calls the real compiler.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define AFL_MAIN
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "types.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "alloc-inl.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
static u8 * obj_path; /* Path to runtime libraries */
|
|
||||||
static u8 **cc_params; /* Parameters passed to the real CC */
|
|
||||||
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
|
|
||||||
u8 use_stdin = 0; /* dummy */
|
|
||||||
|
|
||||||
/* Try to find the runtime libraries. If that fails, abort. */
|
|
||||||
|
|
||||||
static void find_obj(u8 *argv0) {
|
|
||||||
|
|
||||||
u8 *afl_path = getenv("AFL_PATH");
|
|
||||||
u8 *slash, *tmp;
|
|
||||||
|
|
||||||
if (afl_path) {
|
|
||||||
|
|
||||||
tmp = alloc_printf("%s/afl-gcc-rt.o", afl_path);
|
|
||||||
|
|
||||||
if (!access(tmp, R_OK)) {
|
|
||||||
|
|
||||||
obj_path = afl_path;
|
|
||||||
ck_free(tmp);
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ck_free(tmp);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
slash = strrchr(argv0, '/');
|
|
||||||
|
|
||||||
if (slash) {
|
|
||||||
|
|
||||||
u8 *dir;
|
|
||||||
|
|
||||||
*slash = 0;
|
|
||||||
dir = ck_strdup(argv0);
|
|
||||||
*slash = '/';
|
|
||||||
|
|
||||||
tmp = alloc_printf("%s/afl-gcc-rt.o", dir);
|
|
||||||
|
|
||||||
if (!access(tmp, R_OK)) {
|
|
||||||
|
|
||||||
obj_path = dir;
|
|
||||||
ck_free(tmp);
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ck_free(tmp);
|
|
||||||
ck_free(dir);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!access(AFL_PATH "/afl-gcc-rt.o", R_OK)) {
|
|
||||||
|
|
||||||
obj_path = AFL_PATH;
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
FATAL(
|
|
||||||
"Unable to find 'afl-gcc-rt.o' or 'afl-gcc-pass.so'. Please set "
|
|
||||||
"AFL_PATH");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy argv to cc_params, making the necessary edits. */
|
|
||||||
|
|
||||||
static void edit_params(u32 argc, char **argv) {
|
|
||||||
|
|
||||||
u8 fortify_set = 0, asan_set = 0, x_set = 0, maybe_linking = 1;
|
|
||||||
u8 *name;
|
|
||||||
|
|
||||||
cc_params = ck_alloc((argc + 128) * sizeof(u8 *));
|
|
||||||
|
|
||||||
name = strrchr(argv[0], '/');
|
|
||||||
if (!name)
|
|
||||||
name = argv[0];
|
|
||||||
else
|
|
||||||
++name;
|
|
||||||
|
|
||||||
if (!strcmp(name, "afl-g++-fast")) {
|
|
||||||
|
|
||||||
u8 *alt_cxx = getenv("AFL_CXX");
|
|
||||||
cc_params[0] = alt_cxx && *alt_cxx ? alt_cxx : (u8 *)AFL_GCC_CXX;
|
|
||||||
|
|
||||||
} else if (!strcmp(name, "afl-gcc-fast")) {
|
|
||||||
|
|
||||||
u8 *alt_cc = getenv("AFL_CC");
|
|
||||||
cc_params[0] = alt_cc && *alt_cc ? alt_cc : (u8 *)AFL_GCC_CC;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
fprintf(stderr, "Name of the binary: %s\n", argv[0]);
|
|
||||||
FATAL(
|
|
||||||
"Name of the binary is not a known name, expected afl-(gcc|g++)-fast");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
char *fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path);
|
|
||||||
cc_params[cc_par_cnt++] = fplugin_arg;
|
|
||||||
|
|
||||||
/* Detect stray -v calls from ./configure scripts. */
|
|
||||||
|
|
||||||
if (argc == 1 && !strcmp(argv[1], "-v")) maybe_linking = 0;
|
|
||||||
|
|
||||||
while (--argc) {
|
|
||||||
|
|
||||||
u8 *cur = *(++argv);
|
|
||||||
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
if (!strcmp(cur, "-m32")) FATAL("-m32 is not supported");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!strcmp(cur, "-x")) x_set = 1;
|
|
||||||
|
|
||||||
if (!strcmp(cur, "-c") || !strcmp(cur, "-S") || !strcmp(cur, "-E") ||
|
|
||||||
!strcmp(cur, "-v"))
|
|
||||||
maybe_linking = 0;
|
|
||||||
|
|
||||||
if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
|
|
||||||
asan_set = 1;
|
|
||||||
|
|
||||||
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
|
|
||||||
|
|
||||||
if (!strcmp(cur, "-shared")) maybe_linking = 0;
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = cur;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getenv("AFL_HARDEN")) {
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-fstack-protector-all";
|
|
||||||
|
|
||||||
if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!asan_set) {
|
|
||||||
|
|
||||||
if (getenv("AFL_USE_ASAN")) {
|
|
||||||
|
|
||||||
if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive");
|
|
||||||
|
|
||||||
if (getenv("AFL_HARDEN"))
|
|
||||||
FATAL("ASAN and AFL_HARDEN are mutually exclusive");
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
|
|
||||||
cc_params[cc_par_cnt++] = "-fsanitize=address";
|
|
||||||
|
|
||||||
} else if (getenv("AFL_USE_MSAN")) {
|
|
||||||
|
|
||||||
if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive");
|
|
||||||
|
|
||||||
if (getenv("AFL_HARDEN"))
|
|
||||||
FATAL("MSAN and AFL_HARDEN are mutually exclusive");
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
|
|
||||||
cc_params[cc_par_cnt++] = "-fsanitize=memory";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getenv("AFL_USE_UBSAN")) {
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-fsanitize=undefined";
|
|
||||||
cc_params[cc_par_cnt++] = "-fsanitize-undefined-trap-on-error";
|
|
||||||
cc_params[cc_par_cnt++] = "-fno-sanitize-recover=all";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!getenv("AFL_DONT_OPTIMIZE")) {
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-g";
|
|
||||||
cc_params[cc_par_cnt++] = "-O3";
|
|
||||||
cc_params[cc_par_cnt++] = "-funroll-loops";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getenv("AFL_NO_BUILTIN")) {
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-fno-builtin-strcmp";
|
|
||||||
cc_params[cc_par_cnt++] = "-fno-builtin-strncmp";
|
|
||||||
cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp";
|
|
||||||
cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp";
|
|
||||||
cc_params[cc_par_cnt++] = "-fno-builtin-memcmp";
|
|
||||||
cc_params[cc_par_cnt++] = "-fno-builtin-bcmp";
|
|
||||||
cc_params[cc_par_cnt++] = "-fno-builtin-strstr";
|
|
||||||
cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(USEMMAP) && !defined(__HAIKU__)
|
|
||||||
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";
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
to call a function from the runtime .o file. This is unnecessarily
|
|
||||||
painful for three reasons:
|
|
||||||
|
|
||||||
1) We need to convince the compiler not to optimize out the signature.
|
|
||||||
This is done with __attribute__((used)).
|
|
||||||
|
|
||||||
2) We need to convince the linker, when called with -Wl,--gc-sections,
|
|
||||||
not to do the same. This is done by forcing an assignment to a
|
|
||||||
'volatile' pointer.
|
|
||||||
|
|
||||||
3) We need to declare __afl_persistent_loop() in the global namespace,
|
|
||||||
but doing this within a method in a class is hard - :: and extern "C"
|
|
||||||
are forbidden and __attribute__((alias(...))) doesn't work. Hence the
|
|
||||||
__asm__ aliasing trick.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] =
|
|
||||||
"-D__AFL_LOOP(_A)="
|
|
||||||
"({ static volatile char *_B __attribute__((used)); "
|
|
||||||
" _B = (char*)\"" PERSIST_SIG
|
|
||||||
"\"; "
|
|
||||||
#ifdef __APPLE__
|
|
||||||
"int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
|
|
||||||
#else
|
|
||||||
"int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
|
|
||||||
#endif /* ^__APPLE__ */
|
|
||||||
"_L(_A); })";
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] =
|
|
||||||
"-D__AFL_INIT()="
|
|
||||||
"do { static volatile char *_A __attribute__((used)); "
|
|
||||||
" _A = (char*)\"" DEFER_SIG
|
|
||||||
"\"; "
|
|
||||||
#ifdef __APPLE__
|
|
||||||
"void _I(void) __asm__(\"___afl_manual_init\"); "
|
|
||||||
#else
|
|
||||||
"void _I(void) __asm__(\"__afl_manual_init\"); "
|
|
||||||
#endif /* ^__APPLE__ */
|
|
||||||
"_I(); } while (0)";
|
|
||||||
|
|
||||||
if (maybe_linking) {
|
|
||||||
|
|
||||||
if (x_set) {
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-x";
|
|
||||||
cc_params[cc_par_cnt++] = "none";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-gcc-rt.o", obj_path);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_params[cc_par_cnt] = NULL;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main entry point */
|
|
||||||
|
|
||||||
int main(int argc, char **argv, char **envp) {
|
|
||||||
|
|
||||||
if (argc < 2 || strcmp(argv[1], "-h") == 0) {
|
|
||||||
|
|
||||||
printf(cCYA
|
|
||||||
"afl-gcc-fast" VERSION cRST
|
|
||||||
" initially by <aseipp@pobox.com>, maintainer: hexcoder-\n"
|
|
||||||
"\n"
|
|
||||||
"afl-gcc-fast [options]\n"
|
|
||||||
"\n"
|
|
||||||
"This is a helper application for afl-fuzz. It serves as a drop-in "
|
|
||||||
"replacement\n"
|
|
||||||
"for gcc, letting you recompile third-party code with the required "
|
|
||||||
"runtime\n"
|
|
||||||
"instrumentation. A common use pattern would be one of the "
|
|
||||||
"following:\n\n"
|
|
||||||
|
|
||||||
" CC=%s/afl-gcc-fast ./configure\n"
|
|
||||||
" CXX=%s/afl-g++-fast ./configure\n\n"
|
|
||||||
|
|
||||||
"In contrast to the traditional afl-gcc tool, this version is "
|
|
||||||
"implemented as\n"
|
|
||||||
"a GCC plugin and tends to offer improved performance with slow "
|
|
||||||
"programs\n"
|
|
||||||
"(similarly to the LLVM plugin used by afl-clang-fast).\n\n"
|
|
||||||
|
|
||||||
"Environment variables used:\n"
|
|
||||||
"AFL_CC: path to the C compiler to use\n"
|
|
||||||
"AFL_CXX: path to the C++ compiler to use\n"
|
|
||||||
"AFL_PATH: path to instrumenting pass and runtime (afl-gcc-rt.*o)\n"
|
|
||||||
"AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n"
|
|
||||||
"AFL_NO_BUILTIN: compile for use with libtokencap.so\n"
|
|
||||||
"AFL_INST_RATIO: percentage of branches to instrument\n"
|
|
||||||
"AFL_QUIET: suppress verbose output\n"
|
|
||||||
"AFL_DEBUG: enable developer debugging output\n"
|
|
||||||
"AFL_HARDEN: adds code hardening to catch memory bugs\n"
|
|
||||||
"AFL_USE_ASAN: activate address sanitizer\n"
|
|
||||||
"AFL_USE_MSAN: activate memory sanitizer\n"
|
|
||||||
"AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"
|
|
||||||
"AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by "
|
|
||||||
"filename\n"
|
|
||||||
|
|
||||||
"\nafl-gcc-fast was built for gcc %s with the gcc binary path of "
|
|
||||||
"\"%s\".\n\n",
|
|
||||||
BIN_PATH, BIN_PATH, GCC_VERSION, GCC_BINDIR);
|
|
||||||
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
} else if ((isatty(2) && !getenv("AFL_QUIET")) ||
|
|
||||||
|
|
||||||
getenv("AFL_DEBUG") != NULL) {
|
|
||||||
|
|
||||||
SAYF(cCYA "afl-gcc-fast" VERSION cRST
|
|
||||||
" initially by <aseipp@pobox.com>, maintainer: hexcoder-\n");
|
|
||||||
|
|
||||||
if (getenv("AFL_GCC_INSTRUMENT_FILE") == NULL &&
|
|
||||||
getenv("AFL_GCC_WHITELIST") == NULL) {
|
|
||||||
|
|
||||||
SAYF(
|
|
||||||
cYEL
|
|
||||||
"Warning:" cRST
|
|
||||||
" using afl-gcc-fast without using AFL_GCC_INSTRUMENT_FILE currently "
|
|
||||||
"produces worse results than afl-gcc. Even better, use "
|
|
||||||
"llvm_mode for now.\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else
|
|
||||||
|
|
||||||
be_quiet = 1;
|
|
||||||
|
|
||||||
u8 *ptr;
|
|
||||||
if (!be_quiet &&
|
|
||||||
((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE")))) {
|
|
||||||
|
|
||||||
u32 map_size = atoi(ptr);
|
|
||||||
if (map_size != MAP_SIZE)
|
|
||||||
WARNF("AFL_MAP_SIZE is not supported by afl-gcc-fast");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
check_environment_vars(envp);
|
|
||||||
|
|
||||||
find_obj(argv[0]);
|
|
||||||
|
|
||||||
edit_params(argc, argv);
|
|
||||||
/*if (isatty(2) && !getenv("AFL_QUIET")) {
|
|
||||||
|
|
||||||
printf("Calling \"%s\" with:\n", cc_params[0]);
|
|
||||||
for(int i=1; i<cc_par_cnt; i++) printf("%s\n", cc_params[i]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
execvp(cc_params[0], (char **)cc_params);
|
|
||||||
|
|
||||||
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,601 +0,0 @@
|
|||||||
//
|
|
||||||
// There are some TODOs in this file:
|
|
||||||
// - fix instrumentation via external call
|
|
||||||
// - fix inline instrumentation
|
|
||||||
// - implement instrument list feature
|
|
||||||
// - dont instrument blocks that are uninteresting
|
|
||||||
// - implement neverZero
|
|
||||||
//
|
|
||||||
|
|
||||||
/*
|
|
||||||
american fuzzy lop++ - GCC instrumentation pass
|
|
||||||
---------------------------------------------
|
|
||||||
|
|
||||||
Written by Austin Seipp <aseipp@pobox.com> with bits from
|
|
||||||
Emese Revfy <re.emese@gmail.com>
|
|
||||||
|
|
||||||
Fixed by Heiko Eißfeldt 2019-2020 for AFL++
|
|
||||||
|
|
||||||
GCC integration design is based on the LLVM design, which comes
|
|
||||||
from Laszlo Szekeres. Some of the boilerplate code below for
|
|
||||||
afl_pass to adapt to different GCC versions was taken from Emese
|
|
||||||
Revfy's Size Overflow plugin for GCC, licensed under the GPLv2/v3.
|
|
||||||
|
|
||||||
(NOTE: this plugin code is under GPLv3, in order to comply with the
|
|
||||||
GCC runtime library exception, which states that you may distribute
|
|
||||||
"Target Code" from the compiler under a license of your choice, as
|
|
||||||
long as the "Compilation Process" is "Eligible", and contains no
|
|
||||||
GPL-incompatible software in GCC "during the process of
|
|
||||||
transforming high level code to target code". In this case, the
|
|
||||||
plugin will be used to generate "Target Code" during the
|
|
||||||
"Compilation Process", and thus it must be GPLv3 to be "eligible".)
|
|
||||||
|
|
||||||
Copyright (C) 2015 Austin Seipp
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define BUILD_INLINE_INST
|
|
||||||
|
|
||||||
#include "../include/config.h"
|
|
||||||
#include "../include/debug.h"
|
|
||||||
|
|
||||||
/* clear helper macros AFL types pull in, which intervene with gcc-plugin
|
|
||||||
* headers from GCC-8 */
|
|
||||||
#ifdef likely
|
|
||||||
#undef likely
|
|
||||||
#endif
|
|
||||||
#ifdef unlikely
|
|
||||||
#undef unlikely
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <string>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include <gcc-plugin.h>
|
|
||||||
#include <plugin-version.h>
|
|
||||||
#include <diagnostic.h>
|
|
||||||
#include <tree.h>
|
|
||||||
#include <tree-ssa.h>
|
|
||||||
#include <tree-pass.h>
|
|
||||||
#include <tree-ssa-alias.h>
|
|
||||||
#include <basic-block.h>
|
|
||||||
#include <gimple-expr.h>
|
|
||||||
#include <gimple.h>
|
|
||||||
#include <gimple-iterator.h>
|
|
||||||
#include <gimple-ssa.h>
|
|
||||||
#include <version.h>
|
|
||||||
#include <toplev.h>
|
|
||||||
#include <intl.h>
|
|
||||||
#include <context.h>
|
|
||||||
#include <stringpool.h>
|
|
||||||
#include <cgraph.h>
|
|
||||||
#include <cfgloop.h>
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* -- AFL instrumentation pass ---------------------------------------------- */
|
|
||||||
|
|
||||||
static int be_quiet = 0;
|
|
||||||
static unsigned int inst_ratio = 100;
|
|
||||||
static bool inst_ext = true;
|
|
||||||
static std::list<std::string> myInstrumentList;
|
|
||||||
|
|
||||||
static unsigned int ext_call_instrument(function *fun) {
|
|
||||||
|
|
||||||
/* Instrument all the things! */
|
|
||||||
basic_block bb;
|
|
||||||
unsigned finst_blocks = 0;
|
|
||||||
unsigned fcnt_blocks = 0;
|
|
||||||
|
|
||||||
tree fntype = build_function_type_list(void_type_node, /* return */
|
|
||||||
uint32_type_node, /* args */
|
|
||||||
NULL_TREE); /* done */
|
|
||||||
tree fndecl = build_fn_decl("__afl_trace", fntype);
|
|
||||||
TREE_STATIC(fndecl) = 1; /* Defined elsewhere */
|
|
||||||
TREE_PUBLIC(fndecl) = 1; /* Public */
|
|
||||||
DECL_EXTERNAL(fndecl) = 1; /* External linkage */
|
|
||||||
DECL_ARTIFICIAL(fndecl) = 1; /* Injected by compiler */
|
|
||||||
|
|
||||||
FOR_EACH_BB_FN(bb, fun) {
|
|
||||||
|
|
||||||
gimple_seq fcall;
|
|
||||||
gimple_seq seq = NULL;
|
|
||||||
gimple_stmt_iterator bentry;
|
|
||||||
++fcnt_blocks;
|
|
||||||
|
|
||||||
// only instrument if this basic block is the destination of a previous
|
|
||||||
// basic block that has multiple successors
|
|
||||||
// this gets rid of ~5-10% of instrumentations that are unnecessary
|
|
||||||
// result: a little more speed and less map pollution
|
|
||||||
|
|
||||||
int more_than_one = -1;
|
|
||||||
edge ep;
|
|
||||||
edge_iterator eip;
|
|
||||||
|
|
||||||
FOR_EACH_EDGE(ep, eip, bb->preds) {
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
if (more_than_one == -1) more_than_one = 0;
|
|
||||||
|
|
||||||
basic_block Pred = ep->src;
|
|
||||||
edge es;
|
|
||||||
edge_iterator eis;
|
|
||||||
FOR_EACH_EDGE(es, eis, Pred->succs) {
|
|
||||||
|
|
||||||
basic_block Succ = es->dest;
|
|
||||||
if (Succ != NULL) count++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 1) more_than_one = 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (more_than_one != 1) continue;
|
|
||||||
|
|
||||||
/* Bail on this block if we trip the specified ratio */
|
|
||||||
if (R(100) >= inst_ratio) continue;
|
|
||||||
|
|
||||||
/* Make up cur_loc */
|
|
||||||
unsigned int rand_loc = R(MAP_SIZE);
|
|
||||||
tree cur_loc = build_int_cst(uint32_type_node, rand_loc);
|
|
||||||
|
|
||||||
/* Update bitmap via external call */
|
|
||||||
/* to quote:
|
|
||||||
* /+ Trace a basic block with some ID +/
|
|
||||||
* void __afl_trace(u32 x);
|
|
||||||
*/
|
|
||||||
|
|
||||||
fcall = gimple_build_call(
|
|
||||||
fndecl, 1,
|
|
||||||
cur_loc); /* generate the function _call_ to above built reference, with
|
|
||||||
*1* parameter -> the random const for the location */
|
|
||||||
gimple_seq_add_stmt(&seq, fcall); /* and insert into a sequence */
|
|
||||||
|
|
||||||
/* Done - grab the entry to the block and insert sequence */
|
|
||||||
bentry = gsi_after_labels(bb);
|
|
||||||
gsi_insert_seq_before(&bentry, seq, GSI_SAME_STMT);
|
|
||||||
|
|
||||||
++finst_blocks;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Say something nice. */
|
|
||||||
if (!be_quiet) {
|
|
||||||
|
|
||||||
if (!finst_blocks)
|
|
||||||
WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST),
|
|
||||||
function_name(fun));
|
|
||||||
else if (finst_blocks < fcnt_blocks)
|
|
||||||
OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST),
|
|
||||||
finst_blocks, fcnt_blocks, function_name(fun));
|
|
||||||
else
|
|
||||||
OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks,
|
|
||||||
function_name(fun));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int inline_instrument(function *fun) {
|
|
||||||
|
|
||||||
/* Instrument all the things! */
|
|
||||||
basic_block bb;
|
|
||||||
unsigned finst_blocks = 0;
|
|
||||||
unsigned fcnt_blocks = 0;
|
|
||||||
tree one = build_int_cst(unsigned_char_type_node, 1);
|
|
||||||
// tree zero = build_int_cst(unsigned_char_type_node, 0);
|
|
||||||
|
|
||||||
/* Set up global type declarations */
|
|
||||||
tree map_type = build_pointer_type(unsigned_char_type_node);
|
|
||||||
tree map_ptr_g =
|
|
||||||
build_decl(UNKNOWN_LOCATION, VAR_DECL,
|
|
||||||
get_identifier_with_length("__afl_area_ptr", 14), map_type);
|
|
||||||
TREE_USED(map_ptr_g) = 1;
|
|
||||||
TREE_STATIC(map_ptr_g) = 1; /* Defined elsewhere */
|
|
||||||
DECL_EXTERNAL(map_ptr_g) = 1; /* External linkage */
|
|
||||||
DECL_PRESERVE_P(map_ptr_g) = 1;
|
|
||||||
DECL_ARTIFICIAL(map_ptr_g) = 1; /* Injected by compiler */
|
|
||||||
rest_of_decl_compilation(map_ptr_g, 1, 0);
|
|
||||||
|
|
||||||
tree prev_loc_g = build_decl(UNKNOWN_LOCATION, VAR_DECL,
|
|
||||||
get_identifier_with_length("__afl_prev_loc", 14),
|
|
||||||
uint32_type_node);
|
|
||||||
TREE_USED(prev_loc_g) = 1;
|
|
||||||
TREE_STATIC(prev_loc_g) = 1; /* Defined elsewhere */
|
|
||||||
DECL_EXTERNAL(prev_loc_g) = 1; /* External linkage */
|
|
||||||
DECL_PRESERVE_P(prev_loc_g) = 1;
|
|
||||||
DECL_ARTIFICIAL(prev_loc_g) = 1; /* Injected by compiler */
|
|
||||||
set_decl_tls_model(prev_loc_g, TLS_MODEL_REAL); /* TLS attribute */
|
|
||||||
rest_of_decl_compilation(prev_loc_g, 1, 0);
|
|
||||||
|
|
||||||
FOR_EACH_BB_FN(bb, fun) {
|
|
||||||
|
|
||||||
gimple_seq seq = NULL;
|
|
||||||
gimple_stmt_iterator bentry;
|
|
||||||
++fcnt_blocks;
|
|
||||||
|
|
||||||
// only instrument if this basic block is the destination of a previous
|
|
||||||
// basic block that has multiple successors
|
|
||||||
// this gets rid of ~5-10% of instrumentations that are unnecessary
|
|
||||||
// result: a little more speed and less map pollution
|
|
||||||
|
|
||||||
int more_than_one = -1;
|
|
||||||
edge ep;
|
|
||||||
edge_iterator eip;
|
|
||||||
FOR_EACH_EDGE(ep, eip, bb->preds) {
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
if (more_than_one == -1) more_than_one = 0;
|
|
||||||
|
|
||||||
basic_block Pred = ep->src;
|
|
||||||
edge es;
|
|
||||||
edge_iterator eis;
|
|
||||||
FOR_EACH_EDGE(es, eis, Pred->succs) {
|
|
||||||
|
|
||||||
basic_block Succ = es->dest;
|
|
||||||
if (Succ != NULL) count++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 1) more_than_one = 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (more_than_one != 1) continue;
|
|
||||||
|
|
||||||
/* Bail on this block if we trip the specified ratio */
|
|
||||||
if (R(100) >= inst_ratio) continue;
|
|
||||||
|
|
||||||
/* Make up cur_loc */
|
|
||||||
|
|
||||||
unsigned int rand_loc = R(MAP_SIZE);
|
|
||||||
tree cur_loc = build_int_cst(uint32_type_node, rand_loc);
|
|
||||||
|
|
||||||
/* Load prev_loc, xor with cur_loc */
|
|
||||||
// gimple_assign <var_decl, prev_loc.0_1, prev_loc, NULL, NULL>
|
|
||||||
tree prev_loc = create_tmp_var_raw(uint32_type_node, "prev_loc");
|
|
||||||
gassign *g = gimple_build_assign(prev_loc, VAR_DECL, prev_loc_g);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // load prev_loc
|
|
||||||
update_stmt(g);
|
|
||||||
|
|
||||||
// gimple_assign <bit_xor_expr, _2, prev_loc.0_1, 47231, NULL>
|
|
||||||
tree area_off = create_tmp_var_raw(uint32_type_node, "area_off");
|
|
||||||
g = gimple_build_assign(area_off, BIT_XOR_EXPR, prev_loc, cur_loc);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // area_off = prev_loc ^ cur_loc
|
|
||||||
update_stmt(g);
|
|
||||||
|
|
||||||
/* Update bitmap */
|
|
||||||
|
|
||||||
// gimple_assign <addr_expr, p_6, &map[_2], NULL, NULL>
|
|
||||||
tree map_ptr = create_tmp_var(map_type, "map_ptr");
|
|
||||||
tree map_ptr2 = create_tmp_var(map_type, "map_ptr2");
|
|
||||||
|
|
||||||
g = gimple_build_assign(map_ptr, map_ptr_g);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // map_ptr = __afl_area_ptr
|
|
||||||
update_stmt(g);
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
#if 0
|
|
||||||
tree addr = build2(ADDR_EXPR, map_type, map_ptr, area_off);
|
|
||||||
g = gimple_build_assign(map_ptr2, MODIFY_EXPR, addr);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off
|
|
||||||
update_stmt(g);
|
|
||||||
#else
|
|
||||||
g = gimple_build_assign(map_ptr2, PLUS_EXPR, map_ptr, area_off);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off
|
|
||||||
update_stmt(g);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// gimple_assign <mem_ref, _3, *p_6, NULL, NULL>
|
|
||||||
tree tmp1 = create_tmp_var_raw(unsigned_char_type_node, "tmp1");
|
|
||||||
g = gimple_build_assign(tmp1, MEM_REF, map_ptr2);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // tmp1 = *map_ptr2
|
|
||||||
update_stmt(g);
|
|
||||||
#else
|
|
||||||
tree atIndex = build2(PLUS_EXPR, uint32_type_node, map_ptr, area_off);
|
|
||||||
tree array_address = build1(ADDR_EXPR, map_type, atIndex);
|
|
||||||
tree array_access = build1(INDIRECT_REF, map_type, array_address);
|
|
||||||
tree tmp1 = create_tmp_var(unsigned_char_type_node, "tmp1");
|
|
||||||
g = gimple_build_assign(tmp1, array_access);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // tmp1 = *(map_ptr + area_off)
|
|
||||||
update_stmt(g);
|
|
||||||
#endif
|
|
||||||
// gimple_assign <plus_expr, _4, _3, 1, NULL>
|
|
||||||
tree tmp2 = create_tmp_var_raw(unsigned_char_type_node, "tmp2");
|
|
||||||
g = gimple_build_assign(tmp2, PLUS_EXPR, tmp1, one);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // tmp2 = tmp1 + 1
|
|
||||||
update_stmt(g);
|
|
||||||
|
|
||||||
// TODO: neverZero: here we have to check if tmp3 == 0
|
|
||||||
// and add 1 if so
|
|
||||||
|
|
||||||
// gimple_assign <ssa_name, *p_6, _4, NULL, NULL>
|
|
||||||
// tree map_ptr3 = create_tmp_var_raw(map_type, "map_ptr3");
|
|
||||||
g = gimple_build_assign(map_ptr2, INDIRECT_REF, tmp2);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // *map_ptr2 = tmp2
|
|
||||||
update_stmt(g);
|
|
||||||
|
|
||||||
/* Set prev_loc to cur_loc >> 1 */
|
|
||||||
|
|
||||||
// gimple_assign <integer_cst, prev_loc, 23615, NULL, NULL>
|
|
||||||
tree shifted_loc = build_int_cst(TREE_TYPE(prev_loc_g), rand_loc >> 1);
|
|
||||||
tree prev_loc2 = create_tmp_var_raw(uint32_type_node, "prev_loc2");
|
|
||||||
g = gimple_build_assign(prev_loc2, shifted_loc);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1
|
|
||||||
update_stmt(g);
|
|
||||||
g = gimple_build_assign(prev_loc_g, prev_loc2);
|
|
||||||
gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1
|
|
||||||
update_stmt(g);
|
|
||||||
|
|
||||||
/* Done - grab the entry to the block and insert sequence */
|
|
||||||
|
|
||||||
bentry = gsi_after_labels(bb);
|
|
||||||
gsi_insert_seq_before(&bentry, seq, GSI_NEW_STMT);
|
|
||||||
|
|
||||||
++finst_blocks;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Say something nice. */
|
|
||||||
if (!be_quiet) {
|
|
||||||
|
|
||||||
if (!finst_blocks)
|
|
||||||
WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST),
|
|
||||||
function_name(fun));
|
|
||||||
else if (finst_blocks < fcnt_blocks)
|
|
||||||
OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST),
|
|
||||||
finst_blocks, fcnt_blocks, function_name(fun));
|
|
||||||
else
|
|
||||||
OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks,
|
|
||||||
function_name(fun));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* -- Boilerplate and initialization ---------------------------------------- */
|
|
||||||
|
|
||||||
static const struct pass_data afl_pass_data = {
|
|
||||||
|
|
||||||
.type = GIMPLE_PASS,
|
|
||||||
.name = "afl-inst",
|
|
||||||
.optinfo_flags = OPTGROUP_NONE,
|
|
||||||
|
|
||||||
.tv_id = TV_NONE,
|
|
||||||
.properties_required = 0,
|
|
||||||
.properties_provided = 0,
|
|
||||||
.properties_destroyed = 0,
|
|
||||||
.todo_flags_start = 0,
|
|
||||||
// NOTE(aseipp): it's very, very important to include
|
|
||||||
// at least 'TODO_update_ssa' here so that GCC will
|
|
||||||
// properly update the resulting SSA form, e.g., to
|
|
||||||
// include new PHI nodes for newly added symbols or
|
|
||||||
// names. Do not remove this. Do not taunt Happy Fun
|
|
||||||
// Ball.
|
|
||||||
.todo_flags_finish = TODO_update_ssa | TODO_verify_il | TODO_cleanup_cfg,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class afl_pass : public gimple_opt_pass {
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool do_ext_call;
|
|
||||||
|
|
||||||
public:
|
|
||||||
afl_pass(bool ext_call, gcc::context *g)
|
|
||||||
: gimple_opt_pass(afl_pass_data, g), do_ext_call(ext_call) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int execute(function *fun) override {
|
|
||||||
|
|
||||||
if (!myInstrumentList.empty()) {
|
|
||||||
|
|
||||||
bool instrumentBlock = false;
|
|
||||||
std::string instFilename;
|
|
||||||
unsigned int instLine = 0;
|
|
||||||
|
|
||||||
/* EXPR_FILENAME
|
|
||||||
This macro returns the name of the file in which the entity was declared,
|
|
||||||
as a char*. For an entity declared implicitly by the compiler (like
|
|
||||||
__builtin_ memcpy), this will be the string "<internal>".
|
|
||||||
*/
|
|
||||||
const char *fname = DECL_SOURCE_FILE(fun->decl);
|
|
||||||
|
|
||||||
if (0 != strncmp("<internal>", fname, 10) &&
|
|
||||||
0 != strncmp("<built-in>", fname, 10)) {
|
|
||||||
|
|
||||||
instFilename = fname;
|
|
||||||
instLine = DECL_SOURCE_LINE(fun->decl);
|
|
||||||
|
|
||||||
/* Continue only if we know where we actually are */
|
|
||||||
if (!instFilename.empty()) {
|
|
||||||
|
|
||||||
for (std::list<std::string>::iterator it = myInstrumentList.begin();
|
|
||||||
it != myInstrumentList.end(); ++it) {
|
|
||||||
|
|
||||||
/* We don't check for filename equality here because
|
|
||||||
* filenames might actually be full paths. Instead we
|
|
||||||
* check that the actual filename ends in the filename
|
|
||||||
* specified in the list. */
|
|
||||||
if (instFilename.length() >= it->length()) {
|
|
||||||
|
|
||||||
if (instFilename.compare(instFilename.length() - it->length(),
|
|
||||||
it->length(), *it) == 0) {
|
|
||||||
|
|
||||||
instrumentBlock = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Either we couldn't figure out our location or the location is
|
|
||||||
* not in the instrument list, so we skip instrumentation. */
|
|
||||||
if (!instrumentBlock) {
|
|
||||||
|
|
||||||
if (!be_quiet) {
|
|
||||||
|
|
||||||
if (!instFilename.empty())
|
|
||||||
SAYF(cYEL "[!] " cBRI
|
|
||||||
"Not in instrument list, skipping %s line %u...\n",
|
|
||||||
instFilename.c_str(), instLine);
|
|
||||||
else
|
|
||||||
SAYF(cYEL "[!] " cBRI "No filename information found, skipping it");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return do_ext_call ? ext_call_instrument(fun) : inline_instrument(fun);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}; /* class afl_pass */
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
static struct opt_pass *make_afl_pass(bool ext_call, gcc::context *ctxt) {
|
|
||||||
|
|
||||||
return new afl_pass(ext_call, ctxt);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* -- Initialization -------------------------------------------------------- */
|
|
||||||
|
|
||||||
int plugin_is_GPL_compatible = 1;
|
|
||||||
|
|
||||||
static struct plugin_info afl_plugin_info = {
|
|
||||||
|
|
||||||
.version = "20200519",
|
|
||||||
.help = "AFL++ gcc plugin\n",
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
int plugin_init(struct plugin_name_args * plugin_info,
|
|
||||||
struct plugin_gcc_version *version) {
|
|
||||||
|
|
||||||
struct register_pass_info afl_pass_info;
|
|
||||||
struct timeval tv;
|
|
||||||
struct timezone tz;
|
|
||||||
u32 rand_seed;
|
|
||||||
|
|
||||||
/* Setup random() so we get Actually Random(TM) outputs from R() */
|
|
||||||
gettimeofday(&tv, &tz);
|
|
||||||
rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
|
|
||||||
SR(rand_seed);
|
|
||||||
|
|
||||||
/* Pass information */
|
|
||||||
afl_pass_info.pass = make_afl_pass(inst_ext, g);
|
|
||||||
afl_pass_info.reference_pass_name = "ssa";
|
|
||||||
afl_pass_info.ref_pass_instance_number = 1;
|
|
||||||
afl_pass_info.pos_op = PASS_POS_INSERT_AFTER;
|
|
||||||
|
|
||||||
if (!plugin_default_version_check(version, &gcc_version)) {
|
|
||||||
|
|
||||||
FATAL(G_("Incompatible gcc/plugin versions! Expected GCC %d.%d"),
|
|
||||||
GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Show a banner */
|
|
||||||
if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) {
|
|
||||||
|
|
||||||
SAYF(G_(cCYA "afl-gcc-pass" VERSION cRST
|
|
||||||
" initially by <aseipp@pobox.com>, maintainer: hexcoder-\n"));
|
|
||||||
|
|
||||||
} else
|
|
||||||
|
|
||||||
be_quiet = 1;
|
|
||||||
|
|
||||||
/* Decide instrumentation ratio */
|
|
||||||
char *inst_ratio_str = getenv("AFL_INST_RATIO");
|
|
||||||
|
|
||||||
if (inst_ratio_str) {
|
|
||||||
|
|
||||||
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio ||
|
|
||||||
inst_ratio > 100)
|
|
||||||
FATAL(G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)"));
|
|
||||||
else {
|
|
||||||
|
|
||||||
if (!be_quiet)
|
|
||||||
ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."),
|
|
||||||
inst_ext ? G_("Call-based") : G_("Inline"), inst_ratio,
|
|
||||||
getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened"));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
char *instInstrumentListFilename = getenv("AFL_GCC_INSTRUMENT_FILE");
|
|
||||||
if (!instInstrumentListFilename)
|
|
||||||
instInstrumentListFilename = getenv("AFL_GCC_WHITELIST");
|
|
||||||
if (instInstrumentListFilename) {
|
|
||||||
|
|
||||||
std::string line;
|
|
||||||
std::ifstream fileStream;
|
|
||||||
fileStream.open(instInstrumentListFilename);
|
|
||||||
if (!fileStream) PFATAL("Unable to open AFL_GCC_INSTRUMENT_FILE");
|
|
||||||
getline(fileStream, line);
|
|
||||||
while (fileStream) {
|
|
||||||
|
|
||||||
myInstrumentList.push_back(line);
|
|
||||||
getline(fileStream, line);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (!be_quiet && (getenv("AFL_LLVM_WHITELIST") ||
|
|
||||||
|
|
||||||
getenv("AFL_LLVM_INSTRUMENT_FILE"))) {
|
|
||||||
|
|
||||||
SAYF(cYEL "[-] " cRST
|
|
||||||
"AFL_LLVM_INSTRUMENT_FILE environment variable detected - did "
|
|
||||||
"you mean AFL_GCC_INSTRUMENT_FILE?\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Go go gadget */
|
|
||||||
register_callback(plugin_info->base_name, PLUGIN_INFO, NULL,
|
|
||||||
&afl_plugin_info);
|
|
||||||
register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
|
|
||||||
&afl_pass_info);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,315 +0,0 @@
|
|||||||
/*
|
|
||||||
american fuzzy lop++ - GCC plugin instrumentation bootstrap
|
|
||||||
---------------------------------------------------------
|
|
||||||
|
|
||||||
Written by Austin Seipp <aseipp@pobox.com> and
|
|
||||||
Laszlo Szekeres <lszekeres@google.com> and
|
|
||||||
Michal Zalewski
|
|
||||||
|
|
||||||
GCC integration design is based on the LLVM design, which comes
|
|
||||||
from Laszlo Szekeres.
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
This code is the rewrite of afl-as.h's main_payload.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
#include "android-ashmem.h"
|
|
||||||
#endif
|
|
||||||
#include "../config.h"
|
|
||||||
#include "../types.h"
|
|
||||||
|
|
||||||
#ifdef USEMMAP
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#ifndef USEMMAP
|
|
||||||
#include <sys/shm.h>
|
|
||||||
#endif
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
/* Globals needed by the injected instrumentation. The __afl_area_initial region
|
|
||||||
is used for instrumentation output before __afl_map_shm() has a chance to
|
|
||||||
run. It will end up as .comm, so it shouldn't be too wasteful. */
|
|
||||||
|
|
||||||
u8 __afl_area_initial[MAP_SIZE];
|
|
||||||
u8 *__afl_area_ptr = __afl_area_initial;
|
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
u32 __afl_prev_loc;
|
|
||||||
u32 __afl_final_loc;
|
|
||||||
#else
|
|
||||||
__thread u32 __afl_prev_loc;
|
|
||||||
__thread u32 __afl_final_loc;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Trace a basic block with some ID */
|
|
||||||
void __afl_trace(const u32 x) {
|
|
||||||
|
|
||||||
#if 1 /* enable for neverZero feature. */
|
|
||||||
__afl_area_ptr[__afl_prev_loc ^ x] +=
|
|
||||||
1 + ((u8)(1 + __afl_area_ptr[__afl_prev_loc ^ x]) == 0);
|
|
||||||
#else
|
|
||||||
++__afl_area_ptr[__afl_prev_loc ^ x];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
__afl_prev_loc = (x >> 1);
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Running in persistent mode? */
|
|
||||||
|
|
||||||
static u8 is_persistent;
|
|
||||||
|
|
||||||
/* SHM setup. */
|
|
||||||
|
|
||||||
static void __afl_map_shm(void) {
|
|
||||||
|
|
||||||
u8 *id_str = getenv(SHM_ENV_VAR);
|
|
||||||
|
|
||||||
/* If we're running under AFL, attach to the appropriate region, replacing the
|
|
||||||
early-stage __afl_area_initial region that is needed to allow some really
|
|
||||||
hacky .init code to work correctly in projects such as OpenSSL. */
|
|
||||||
|
|
||||||
if (id_str) {
|
|
||||||
|
|
||||||
#ifdef USEMMAP
|
|
||||||
const char * shm_file_path = id_str;
|
|
||||||
int shm_fd = -1;
|
|
||||||
unsigned char *shm_base = NULL;
|
|
||||||
|
|
||||||
/* create the shared memory segment as if it was a file */
|
|
||||||
shm_fd = shm_open(shm_file_path, O_RDWR, 0600);
|
|
||||||
if (shm_fd == -1) {
|
|
||||||
|
|
||||||
fprintf(stderr, "shm_open() failed\n");
|
|
||||||
exit(1);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* map the shared memory segment to the address space of the process */
|
|
||||||
shm_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
|
|
||||||
if (shm_base == MAP_FAILED) {
|
|
||||||
|
|
||||||
close(shm_fd);
|
|
||||||
shm_fd = -1;
|
|
||||||
|
|
||||||
fprintf(stderr, "mmap() failed\n");
|
|
||||||
exit(2);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
__afl_area_ptr = shm_base;
|
|
||||||
#else
|
|
||||||
u32 shm_id = atoi(id_str);
|
|
||||||
|
|
||||||
__afl_area_ptr = shmat(shm_id, NULL, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Whooooops. */
|
|
||||||
|
|
||||||
if (__afl_area_ptr == (void *)-1) exit(1);
|
|
||||||
|
|
||||||
/* Write something into the bitmap so that even with low AFL_INST_RATIO,
|
|
||||||
our parent doesn't give up on us. */
|
|
||||||
|
|
||||||
__afl_area_ptr[0] = 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fork server logic. */
|
|
||||||
|
|
||||||
static void __afl_start_forkserver(void) {
|
|
||||||
|
|
||||||
u8 tmp[4] = {0, 0, 0, 0};
|
|
||||||
u32 map_size = MAP_SIZE;
|
|
||||||
s32 child_pid;
|
|
||||||
|
|
||||||
u8 child_stopped = 0;
|
|
||||||
|
|
||||||
void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL);
|
|
||||||
|
|
||||||
/* Phone home and tell the parent that we're OK. If parent isn't there,
|
|
||||||
assume we're not running in forkserver mode and just execute program. */
|
|
||||||
|
|
||||||
if (MAP_SIZE <= 0x800000) {
|
|
||||||
|
|
||||||
map_size = (FS_OPT_ENABLED | FS_OPT_MAPSIZE | FS_OPT_SET_MAPSIZE(MAP_SIZE));
|
|
||||||
memcpy(tmp, &map_size, 4);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
u32 was_killed;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
/* Wait for parent by reading from the pipe. Abort if read fails. */
|
|
||||||
|
|
||||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(1);
|
|
||||||
|
|
||||||
/* If we stopped the child in persistent mode, but there was a race
|
|
||||||
condition and afl-fuzz already issued SIGKILL, write off the old
|
|
||||||
process. */
|
|
||||||
|
|
||||||
if (child_stopped && was_killed) {
|
|
||||||
|
|
||||||
child_stopped = 0;
|
|
||||||
if (waitpid(child_pid, &status, 0) < 0) exit(1);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!child_stopped) {
|
|
||||||
|
|
||||||
/* Once woken up, create a clone of our process. */
|
|
||||||
|
|
||||||
child_pid = fork();
|
|
||||||
if (child_pid < 0) exit(1);
|
|
||||||
|
|
||||||
/* In child process: close fds, resume execution. */
|
|
||||||
|
|
||||||
if (!child_pid) {
|
|
||||||
|
|
||||||
signal(SIGCHLD, old_sigchld_handler);
|
|
||||||
|
|
||||||
close(FORKSRV_FD);
|
|
||||||
close(FORKSRV_FD + 1);
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/* Special handling for persistent mode: if the child is alive but
|
|
||||||
currently stopped, simply restart it with SIGCONT. */
|
|
||||||
|
|
||||||
kill(child_pid, SIGCONT);
|
|
||||||
child_stopped = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* In parent process: write PID to pipe, then wait for child. */
|
|
||||||
|
|
||||||
if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(1);
|
|
||||||
|
|
||||||
if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) exit(1);
|
|
||||||
|
|
||||||
/* In persistent mode, the child stops itself with SIGSTOP to indicate
|
|
||||||
a successful run. In this case, we want to wake it up without forking
|
|
||||||
again. */
|
|
||||||
|
|
||||||
if (WIFSTOPPED(status)) child_stopped = 1;
|
|
||||||
|
|
||||||
/* Relay wait status to pipe, then loop back. */
|
|
||||||
|
|
||||||
if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A simplified persistent mode handler, used as explained in README.md. */
|
|
||||||
|
|
||||||
int __afl_persistent_loop(unsigned int max_cnt) {
|
|
||||||
|
|
||||||
static u8 first_pass = 1;
|
|
||||||
static u32 cycle_cnt;
|
|
||||||
|
|
||||||
if (first_pass) {
|
|
||||||
|
|
||||||
/* Make sure that every iteration of __AFL_LOOP() starts with a clean slate.
|
|
||||||
On subsequent calls, the parent will take care of that, but on the first
|
|
||||||
iteration, it's our job to erase any trace of whatever happened
|
|
||||||
before the loop. */
|
|
||||||
|
|
||||||
if (is_persistent) {
|
|
||||||
|
|
||||||
memset(__afl_area_ptr, 0, MAP_SIZE);
|
|
||||||
__afl_area_ptr[0] = 1;
|
|
||||||
__afl_prev_loc = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
cycle_cnt = max_cnt;
|
|
||||||
first_pass = 0;
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_persistent) {
|
|
||||||
|
|
||||||
if (--cycle_cnt) {
|
|
||||||
|
|
||||||
raise(SIGSTOP);
|
|
||||||
|
|
||||||
__afl_area_ptr[0] = 1;
|
|
||||||
__afl_prev_loc = 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/* When exiting __AFL_LOOP(), make sure that the subsequent code that
|
|
||||||
follows the loop is not traced. We do that by pivoting back to the
|
|
||||||
dummy output region. */
|
|
||||||
|
|
||||||
__afl_area_ptr = __afl_area_initial;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This one can be called from user code when deferred forkserver mode
|
|
||||||
is enabled. */
|
|
||||||
|
|
||||||
void __afl_manual_init(void) {
|
|
||||||
|
|
||||||
static u8 init_done;
|
|
||||||
|
|
||||||
if (!init_done) {
|
|
||||||
|
|
||||||
__afl_map_shm();
|
|
||||||
__afl_start_forkserver();
|
|
||||||
init_done = 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Proper initialization routine. */
|
|
||||||
|
|
||||||
__attribute__((constructor(101))) void __afl_auto_init(void) {
|
|
||||||
|
|
||||||
is_persistent = !!getenv(PERSIST_ENV_VAR);
|
|
||||||
|
|
||||||
if (getenv(DEFER_ENV_VAR)) return;
|
|
||||||
|
|
||||||
__afl_manual_init();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -165,8 +165,7 @@ struct queue_entry {
|
|||||||
u8 *trace_mini; /* Trace bytes, if kept */
|
u8 *trace_mini; /* Trace bytes, if kept */
|
||||||
u32 tc_ref; /* Trace bytes ref count */
|
u32 tc_ref; /* Trace bytes ref count */
|
||||||
|
|
||||||
struct queue_entry *next, /* Next element, if any */
|
struct queue_entry *next; /* Next element, if any */
|
||||||
*next_100; /* 100 elements ahead */
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -578,8 +577,7 @@ typedef struct afl_state {
|
|||||||
|
|
||||||
struct queue_entry *queue, /* Fuzzing queue (linked list) */
|
struct queue_entry *queue, /* Fuzzing queue (linked list) */
|
||||||
*queue_cur, /* Current offset within the queue */
|
*queue_cur, /* Current offset within the queue */
|
||||||
*queue_top, /* Top of the list */
|
*queue_top; /* Top of the list */
|
||||||
*q_prev100; /* Previous 100 marker */
|
|
||||||
|
|
||||||
// growing buf
|
// growing buf
|
||||||
struct queue_entry **queue_buf;
|
struct queue_entry **queue_buf;
|
||||||
@ -942,6 +940,7 @@ u8 has_new_bits(afl_state_t *, u8 *);
|
|||||||
|
|
||||||
void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32);
|
void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32);
|
||||||
void load_extras(afl_state_t *, u8 *);
|
void load_extras(afl_state_t *, u8 *);
|
||||||
|
void dedup_extras(afl_state_t *);
|
||||||
void add_extra(afl_state_t *afl, u8 *mem, u32 len);
|
void add_extra(afl_state_t *afl, u8 *mem, u32 len);
|
||||||
void maybe_add_auto(afl_state_t *, u8 *, u32);
|
void maybe_add_auto(afl_state_t *, u8 *, u32);
|
||||||
void save_auto(afl_state_t *);
|
void save_auto(afl_state_t *);
|
||||||
@ -985,7 +984,7 @@ u8 fuzz_one(afl_state_t *);
|
|||||||
void bind_to_free_cpu(afl_state_t *);
|
void bind_to_free_cpu(afl_state_t *);
|
||||||
#endif
|
#endif
|
||||||
void setup_post(afl_state_t *);
|
void setup_post(afl_state_t *);
|
||||||
void read_testcases(afl_state_t *);
|
void read_testcases(afl_state_t *, u8 *);
|
||||||
void perform_dry_run(afl_state_t *);
|
void perform_dry_run(afl_state_t *);
|
||||||
void pivot_inputs(afl_state_t *);
|
void pivot_inputs(afl_state_t *);
|
||||||
u32 find_start_position(afl_state_t *);
|
u32 find_start_position(afl_state_t *);
|
||||||
|
@ -60,7 +60,7 @@ typedef enum prealloc_status {
|
|||||||
\
|
\
|
||||||
if ((prealloc_counter) >= (prealloc_size)) { \
|
if ((prealloc_counter) >= (prealloc_size)) { \
|
||||||
\
|
\
|
||||||
el_ptr = (void *)malloc(sizeof(*el_ptr)); \
|
el_ptr = (element_t *)malloc(sizeof(*el_ptr)); \
|
||||||
if (!el_ptr) { FATAL("error in list.h -> out of memory for element!"); } \
|
if (!el_ptr) { FATAL("error in list.h -> out of memory for element!"); } \
|
||||||
el_ptr->pre_status = PRE_STATUS_MALLOC; \
|
el_ptr->pre_status = PRE_STATUS_MALLOC; \
|
||||||
\
|
\
|
||||||
|
@ -668,7 +668,7 @@ static inline void *afl_realloc(void **buf, size_t size_needed) {
|
|||||||
if (likely(*buf)) {
|
if (likely(*buf)) {
|
||||||
|
|
||||||
/* the size is always stored at buf - 1*size_t */
|
/* the size is always stored at buf - 1*size_t */
|
||||||
new_buf = afl_alloc_bufptr(*buf);
|
new_buf = (struct afl_alloc_buf *)afl_alloc_bufptr(*buf);
|
||||||
current_size = new_buf->complete_size;
|
current_size = new_buf->complete_size;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -694,7 +694,7 @@ static inline void *afl_realloc(void **buf, size_t size_needed) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* alloc */
|
/* alloc */
|
||||||
new_buf = realloc(new_buf, next_size);
|
new_buf = (struct afl_alloc_buf *)realloc(new_buf, next_size);
|
||||||
if (unlikely(!new_buf)) {
|
if (unlikely(!new_buf)) {
|
||||||
|
|
||||||
*buf = NULL;
|
*buf = NULL;
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
#define _AFL_CMPLOG_H
|
#define _AFL_CMPLOG_H
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "forkserver.h"
|
|
||||||
|
|
||||||
#define CMP_MAP_W 65536
|
#define CMP_MAP_W 65536
|
||||||
#define CMP_MAP_H 256
|
#define CMP_MAP_H 256
|
||||||
@ -77,7 +76,8 @@ struct cmp_map {
|
|||||||
|
|
||||||
/* Execs the child */
|
/* Execs the child */
|
||||||
|
|
||||||
void cmplog_exec_child(afl_forkserver_t *fsrv, char **argv);
|
struct afl_forkserver;
|
||||||
|
void cmplog_exec_child(struct afl_forkserver *fsrv, char **argv);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
/* Version string: */
|
/* Version string: */
|
||||||
|
|
||||||
// c = release, d = volatile github dev, e = experimental branch
|
// c = release, d = volatile github dev, e = experimental branch
|
||||||
#define VERSION "++2.68c"
|
#define VERSION "++3.00a"
|
||||||
|
|
||||||
/******************************************************
|
/******************************************************
|
||||||
* *
|
* *
|
||||||
@ -121,12 +121,12 @@ Server config can be adjusted with AFL_STATSD_HOST and AFL_STATSD_PORT env var.
|
|||||||
/* Maximum multiplier for the above (should be a power of two, beware
|
/* Maximum multiplier for the above (should be a power of two, beware
|
||||||
of 32-bit int overflows): */
|
of 32-bit int overflows): */
|
||||||
|
|
||||||
#define HAVOC_MAX_MULT 16
|
#define HAVOC_MAX_MULT 32
|
||||||
#define HAVOC_MAX_MULT_MOPT 32
|
#define HAVOC_MAX_MULT_MOPT 32
|
||||||
|
|
||||||
/* Absolute minimum number of havoc cycles (after all adjustments): */
|
/* Absolute minimum number of havoc cycles (after all adjustments): */
|
||||||
|
|
||||||
#define HAVOC_MIN 16
|
#define HAVOC_MIN 12
|
||||||
|
|
||||||
/* Power Schedule Divisor */
|
/* Power Schedule Divisor */
|
||||||
#define POWER_BETA 1
|
#define POWER_BETA 1
|
||||||
@ -138,10 +138,10 @@ Server config can be adjusted with AFL_STATSD_HOST and AFL_STATSD_PORT env var.
|
|||||||
n = random between 1 and HAVOC_STACK_POW2
|
n = random between 1 and HAVOC_STACK_POW2
|
||||||
stacking = 2^n
|
stacking = 2^n
|
||||||
|
|
||||||
In other words, the default (n = 7) produces 2, 4, 8, 16, 32, 64, or
|
In other words, the default (n = 6) produces 2, 4, 8, 16, 32, or 64
|
||||||
128 stacked tweaks: */
|
stacked tweaks: */
|
||||||
|
|
||||||
#define HAVOC_STACK_POW2 7
|
#define HAVOC_STACK_POW2 6
|
||||||
|
|
||||||
/* Caps on block sizes for cloning and deletion operations. Each of these
|
/* Caps on block sizes for cloning and deletion operations. Each of these
|
||||||
ranges has a 33% probability of getting picked, except for the first
|
ranges has a 33% probability of getting picked, except for the first
|
||||||
@ -207,7 +207,7 @@ Server config can be adjusted with AFL_STATSD_HOST and AFL_STATSD_PORT env var.
|
|||||||
steps; past this point, the "extras/user" step will be still carried out,
|
steps; past this point, the "extras/user" step will be still carried out,
|
||||||
but with proportionally lower odds: */
|
but with proportionally lower odds: */
|
||||||
|
|
||||||
#define MAX_DET_EXTRAS 200
|
#define MAX_DET_EXTRAS 256
|
||||||
|
|
||||||
/* Maximum number of auto-extracted dictionary tokens to actually use in fuzzing
|
/* Maximum number of auto-extracted dictionary tokens to actually use in fuzzing
|
||||||
(first value), and to keep in memory as candidates. The latter should be much
|
(first value), and to keep in memory as candidates. The latter should be much
|
||||||
|
@ -45,7 +45,12 @@ static char *afl_environment_variables[] = {
|
|||||||
"AFL_EXIT_WHEN_DONE",
|
"AFL_EXIT_WHEN_DONE",
|
||||||
"AFL_FAST_CAL",
|
"AFL_FAST_CAL",
|
||||||
"AFL_FORCE_UI",
|
"AFL_FORCE_UI",
|
||||||
|
"AFL_GCC_ALLOWLIST",
|
||||||
|
"AFL_GCC_DENYLIST",
|
||||||
|
"AFL_GCC_BLOCKLIST",
|
||||||
"AFL_GCC_INSTRUMENT_FILE",
|
"AFL_GCC_INSTRUMENT_FILE",
|
||||||
|
"AFL_GCC_OUT_OF_LINE",
|
||||||
|
"AFL_GCC_SKIP_NEVERZERO",
|
||||||
"AFL_GCJ",
|
"AFL_GCJ",
|
||||||
"AFL_HANG_TMOUT",
|
"AFL_HANG_TMOUT",
|
||||||
"AFL_FORKSRV_INIT_TMOUT",
|
"AFL_FORKSRV_INIT_TMOUT",
|
||||||
@ -69,6 +74,7 @@ static char *afl_environment_variables[] = {
|
|||||||
"AFL_LLVM_CMPLOG",
|
"AFL_LLVM_CMPLOG",
|
||||||
"AFL_LLVM_INSTRIM",
|
"AFL_LLVM_INSTRIM",
|
||||||
"AFL_LLVM_CTX",
|
"AFL_LLVM_CTX",
|
||||||
|
"AFL_LLVM_DICT2FILE",
|
||||||
"AFL_LLVM_DOCUMENT_IDS",
|
"AFL_LLVM_DOCUMENT_IDS",
|
||||||
"AFL_LLVM_INSTRUMENT",
|
"AFL_LLVM_INSTRUMENT",
|
||||||
"AFL_LLVM_INSTRIM_LOOPHEAD",
|
"AFL_LLVM_INSTRIM_LOOPHEAD",
|
||||||
|
@ -81,6 +81,7 @@ static inline void list_append(list_t *list, void *el) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
element_t *el_box = NULL;
|
element_t *el_box = NULL;
|
||||||
|
|
||||||
PRE_ALLOC(el_box, list->element_prealloc_buf, LIST_PREALLOC_SIZE,
|
PRE_ALLOC(el_box, list->element_prealloc_buf, LIST_PREALLOC_SIZE,
|
||||||
list->element_prealloc_count);
|
list->element_prealloc_count);
|
||||||
if (!el_box) { FATAL("failed to allocate list element"); }
|
if (!el_box) { FATAL("failed to allocate list element"); }
|
||||||
|
@ -660,7 +660,7 @@ XXH128_hashFromCanonical(const XXH128_canonical_t *src);
|
|||||||
* These declarations should only be used with static linking.
|
* These declarations should only be used with static linking.
|
||||||
* Never use them in association with dynamic linking!
|
* Never use them in association with dynamic linking!
|
||||||
*****************************************************************************
|
*****************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These definitions are only present to allow static allocation
|
* These definitions are only present to allow static allocation
|
||||||
@ -1189,7 +1189,7 @@ static int XXH_isLittleEndian(void) {
|
|||||||
return one.c[0];
|
return one.c[0];
|
||||||
|
|
||||||
}
|
}
|
||||||
\
|
|
||||||
#define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian()
|
#define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian()
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@ -1397,7 +1397,9 @@ static xxh_u32 XXH32_avalanche(xxh_u32 h32) {
|
|||||||
|
|
||||||
static xxh_u32 XXH32_finalize(xxh_u32 h32, const xxh_u8 *ptr, size_t len,
|
static xxh_u32 XXH32_finalize(xxh_u32 h32, const xxh_u8 *ptr, size_t len,
|
||||||
XXH_alignment align) {
|
XXH_alignment align) {
|
||||||
\
|
|
||||||
|
/* dummy comment */
|
||||||
|
|
||||||
#define XXH_PROCESS1 \
|
#define XXH_PROCESS1 \
|
||||||
do { \
|
do { \
|
||||||
\
|
\
|
||||||
@ -1950,16 +1952,21 @@ XXH_FORCE_INLINE xxh_u64 XXH_readLE64_align(const void * ptr,
|
|||||||
|
|
||||||
/******* xxh64 *******/
|
/******* xxh64 *******/
|
||||||
|
|
||||||
static const xxh_u64 XXH_PRIME64_1 = 0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111
|
static const xxh_u64 XXH_PRIME64_1 =
|
||||||
*/
|
0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111
|
||||||
static const xxh_u64 XXH_PRIME64_2 = 0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111
|
*/
|
||||||
*/
|
static const xxh_u64 XXH_PRIME64_2 =
|
||||||
static const xxh_u64 XXH_PRIME64_3 = 0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001
|
0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111
|
||||||
*/
|
*/
|
||||||
static const xxh_u64 XXH_PRIME64_4 = 0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011
|
static const xxh_u64 XXH_PRIME64_3 =
|
||||||
*/
|
0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001
|
||||||
static const xxh_u64 XXH_PRIME64_5 = 0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101
|
*/
|
||||||
*/
|
static const xxh_u64 XXH_PRIME64_4 =
|
||||||
|
0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011
|
||||||
|
*/
|
||||||
|
static const xxh_u64 XXH_PRIME64_5 =
|
||||||
|
0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef XXH_OLD_NAMES
|
#ifdef XXH_OLD_NAMES
|
||||||
#define PRIME64_1 XXH_PRIME64_1
|
#define PRIME64_1 XXH_PRIME64_1
|
||||||
@ -2002,7 +2009,9 @@ static xxh_u64 XXH64_avalanche(xxh_u64 h64) {
|
|||||||
|
|
||||||
static xxh_u64 XXH64_finalize(xxh_u64 h64, const xxh_u8 *ptr, size_t len,
|
static xxh_u64 XXH64_finalize(xxh_u64 h64, const xxh_u8 *ptr, size_t len,
|
||||||
XXH_alignment align) {
|
XXH_alignment align) {
|
||||||
\
|
|
||||||
|
/* dummy comment */
|
||||||
|
|
||||||
#define XXH_PROCESS1_64 \
|
#define XXH_PROCESS1_64 \
|
||||||
do { \
|
do { \
|
||||||
\
|
\
|
||||||
@ -2752,6 +2761,7 @@ XXH64_hashFromCanonical(const XXH64_canonical_t *src) {
|
|||||||
(outHi) = vget_high_u32(vreinterpretq_u32_u64(in)); \
|
(outHi) = vget_high_u32(vreinterpretq_u32_u64(in)); \
|
||||||
\
|
\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \
|
#define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \
|
||||||
do { \
|
do { \
|
||||||
@ -2760,6 +2770,7 @@ XXH64_hashFromCanonical(const XXH64_canonical_t *src) {
|
|||||||
(outHi) = vshrn_n_u64((in), 32); \
|
(outHi) = vshrn_n_u64((in), 32); \
|
||||||
\
|
\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif /* XXH_VECTOR == XXH_NEON */
|
#endif /* XXH_VECTOR == XXH_NEON */
|
||||||
|
|
||||||
|
674
instrumentation/COPYING3
Normal file
674
instrumentation/COPYING3
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
@ -30,7 +30,7 @@ cp ./program ./program.cmplog
|
|||||||
|
|
||||||
## Use
|
## Use
|
||||||
|
|
||||||
AFL++ has the new -c option that can be used to specify a CmpLog binary (the second
|
AFL++ has the new -c option that needs to be used to specify the CmpLog binary (the second
|
||||||
build).
|
build).
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
@ -39,4 +39,4 @@ For example:
|
|||||||
afl-fuzz -i input -o output -c ./program.cmplog -m none -- ./program.afl @@
|
afl-fuzz -i input -o output -c ./program.cmplog -m none -- ./program.afl @@
|
||||||
```
|
```
|
||||||
|
|
||||||
Be careful to use -m none because CmpLog maps a lot of pages.
|
Be sure to use `-m none` because CmpLog can map a lot of pages.
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
This is an LLVM-based implementation of the context sensitive branch coverage.
|
This is an LLVM-based implementation of the context sensitive branch coverage.
|
||||||
|
|
||||||
Basically every function gets it's own ID and that ID is combined with the
|
Basically every function gets its own ID and that ID is combined with the
|
||||||
edges of the called functions.
|
edges of the called functions.
|
||||||
|
|
||||||
So if both function A and function B call a function C, the coverage
|
So if both function A and function B call a function C, the coverage
|
@ -1,12 +1,14 @@
|
|||||||
# GCC-based instrumentation for afl-fuzz
|
# GCC-based instrumentation for afl-fuzz
|
||||||
|
|
||||||
(See [../README.md](../README.md) for the general instruction manual.)
|
See [../README.md](../README.md) for the general instruction manual.
|
||||||
(See [../llvm_mode/README.md](../llvm_mode/README.md) for the LLVM-based instrumentation.)
|
See [README.llvm.md](README.llvm.md) for the LLVM-based instrumentation.
|
||||||
|
|
||||||
!!! TODO items are:
|
|
||||||
!!! => inline instrumentation has to work!
|
|
||||||
!!!
|
|
||||||
|
|
||||||
|
TLDR:
|
||||||
|
* `apt-get install gcc-VERSION-plugin-dev`
|
||||||
|
* `make`
|
||||||
|
* gcc and g++ must point to the gcc-VERSION you you have to set AFL_CC/AFL_CXX
|
||||||
|
to point to these!
|
||||||
|
* just use afl-gcc-fast/afl-g++-fast normally like you would afl-clang-fast
|
||||||
|
|
||||||
## 1) Introduction
|
## 1) Introduction
|
||||||
|
|
||||||
@ -41,12 +43,16 @@ The idea and much of the implementation comes from Laszlo Szekeres.
|
|||||||
In order to leverage this mechanism, you need to have modern enough GCC
|
In order to leverage this mechanism, you need to have modern enough GCC
|
||||||
(>= version 4.5.0) and the plugin headers installed on your system. That
|
(>= version 4.5.0) and the plugin headers installed on your system. That
|
||||||
should be all you need. On Debian machines, these headers can be acquired by
|
should be all you need. On Debian machines, these headers can be acquired by
|
||||||
installing the `gcc-<VERSION>-plugin-dev` packages.
|
installing the `gcc-VERSION-plugin-dev` packages.
|
||||||
|
|
||||||
To build the instrumentation itself, type 'make'. This will generate binaries
|
To build the instrumentation itself, type 'make'. This will generate binaries
|
||||||
called afl-gcc-fast and afl-g++-fast in the parent directory.
|
called afl-gcc-fast and afl-g++-fast in the parent directory.
|
||||||
|
|
||||||
|
The gcc and g++ compiler links have to point to gcc-VERSION - or set these
|
||||||
|
by pointing the environment variables AFL_CC/AFL_CXX to them.
|
||||||
If the CC/CXX have been overridden, those compilers will be used from
|
If the CC/CXX have been overridden, those compilers will be used from
|
||||||
those wrappers without using AFL_CXX/AFL_CC settings.
|
those wrappers without using AFL_CXX/AFL_CC settings.
|
||||||
|
|
||||||
Once this is done, you can instrument third-party code in a way similar to the
|
Once this is done, you can instrument third-party code in a way similar to the
|
||||||
standard operating mode of AFL, e.g.:
|
standard operating mode of AFL, e.g.:
|
||||||
|
|
||||||
@ -56,8 +62,8 @@ standard operating mode of AFL, e.g.:
|
|||||||
Be sure to also include CXX set to afl-g++-fast for C++ code.
|
Be sure to also include CXX set to afl-g++-fast for C++ code.
|
||||||
|
|
||||||
The tool honors roughly the same environmental variables as afl-gcc (see
|
The tool honors roughly the same environmental variables as afl-gcc (see
|
||||||
[env_variables.md](../docs/env_variables.md). This includes AFL_INST_RATIO, AFL_USE_ASAN,
|
[env_variables.md](../docs/env_variables.md). This includes AFL_INST_RATIO,
|
||||||
AFL_HARDEN, and AFL_DONT_OPTIMIZE.
|
AFL_USE_ASAN, AFL_HARDEN, and AFL_DONT_OPTIMIZE.
|
||||||
|
|
||||||
Note: if you want the GCC plugin to be installed on your system for all
|
Note: if you want the GCC plugin to be installed on your system for all
|
||||||
users, you need to build it before issuing 'make install' in the parent
|
users, you need to build it before issuing 'make install' in the parent
|
||||||
@ -66,7 +72,7 @@ directory.
|
|||||||
## 3) Gotchas, feedback, bugs
|
## 3) Gotchas, feedback, bugs
|
||||||
|
|
||||||
This is an early-stage mechanism, so field reports are welcome. You can send bug
|
This is an early-stage mechanism, so field reports are welcome. You can send bug
|
||||||
reports to <hexcoder-@github.com>.
|
reports to afl@aflplus.plus
|
||||||
|
|
||||||
## 4) Bonus feature #1: deferred initialization
|
## 4) Bonus feature #1: deferred initialization
|
||||||
|
|
30
instrumentation/README.instrim.md
Normal file
30
instrumentation/README.instrim.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# InsTrim
|
||||||
|
|
||||||
|
InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
InsTrim is the work of Chin-Chia Hsu, Che-Yu Wu, Hsu-Chun Hsiao and Shih-Kun Huang.
|
||||||
|
|
||||||
|
It uses a CFG (call flow graph) and markers to instrument just what
|
||||||
|
is necessary in the binary (ie less than llvm_mode). As a result the binary is
|
||||||
|
about 10-15% faster compared to normal llvm_mode however with some coverage loss.
|
||||||
|
It requires at least llvm version 3.8.0 to build.
|
||||||
|
If you have LLVM 7+ we recommend PCGUARD instead.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Set the environment variable `AFL_LLVM_INSTRUMENT=CFG` or `AFL_LLVM_INSTRIM=1`
|
||||||
|
during compilation of the target.
|
||||||
|
|
||||||
|
There is also special mode which instruments loops in a way so that
|
||||||
|
afl-fuzz can see which loop path has been selected but not being able to
|
||||||
|
see how often the loop has been rerun.
|
||||||
|
This again is a tradeoff for speed for less path information.
|
||||||
|
To enable this mode set `AFL_LLVM_INSTRIM_LOOPHEAD=1`.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
The paper from Chin-Chia Hsu, Che-Yu Wu, Hsu-Chun Hsiao and Shih-Kun Huang:
|
||||||
|
[InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing]
|
||||||
|
(https://www.ndss-symposium.org/wp-content/uploads/2018/07/bar2018_14_Hsu_paper.pdf)
|
@ -1,8 +1,8 @@
|
|||||||
# Using afl++ with partial instrumentation
|
# Using afl++ with partial instrumentation
|
||||||
|
|
||||||
This file describes how you can selectively instrument only the source files
|
This file describes how to selectively instrument only source files
|
||||||
or functions that are interesting to you using the LLVM instrumentation
|
or functions that are of interest to you using the LLVM and GCC_PLUGIN
|
||||||
provided by afl++
|
instrumentation provided by afl++.
|
||||||
|
|
||||||
## 1) Description and purpose
|
## 1) Description and purpose
|
||||||
|
|
||||||
@ -13,19 +13,25 @@ on the important parts of the program, avoiding undesired noise and
|
|||||||
disturbance by uninteresting code being exercised.
|
disturbance by uninteresting code being exercised.
|
||||||
|
|
||||||
For this purpose, a "partial instrumentation" support en par with llvm sancov
|
For this purpose, a "partial instrumentation" support en par with llvm sancov
|
||||||
is provided by afl++ that allows you to specify on a source file and function
|
is provided by afl++ that allows to specify on a source file and function
|
||||||
level which function should be compiled with or without instrumentation.
|
level which function should be compiled with or without instrumentation.
|
||||||
|
|
||||||
Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead:
|
Note: When using PCGUARD mode - and llvm 12+ - you can use this instead:
|
||||||
https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation
|
https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation
|
||||||
|
|
||||||
The llvm sancov list format is fully supported by afl++, however afl++ has
|
The llvm sancov list format is fully supported by afl++, however afl++ has
|
||||||
more flexibility.
|
more flexibility.
|
||||||
|
|
||||||
## 2) Building the LLVM module
|
## 2a) Building the LLVM module
|
||||||
|
|
||||||
The new code is part of the existing afl++ LLVM module in the llvm_mode/
|
The new code is part of the existing afl++ LLVM module in the instrumentation/
|
||||||
subdirectory. There is nothing specifically to do :)
|
subdirectory. There is nothing specifically to do for the build :)
|
||||||
|
|
||||||
|
## 2b) Building the GCC module
|
||||||
|
|
||||||
|
The new code is part of the existing afl++ GCC_PLUGIN module in the
|
||||||
|
instrumentation/ subdirectory. There is nothing specifically to do for
|
||||||
|
the build :)
|
||||||
|
|
||||||
## 3) How to use the partial instrumentation mode
|
## 3) How to use the partial instrumentation mode
|
||||||
|
|
||||||
@ -34,14 +40,17 @@ afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++.
|
|||||||
The only required change is that you need to set either the environment variable
|
The only required change is that you need to set either the environment variable
|
||||||
AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST set with a filename.
|
AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST set with a filename.
|
||||||
|
|
||||||
That file then contains the filenames or functions that should be instrumented
|
That file should contain the file names or functions that are to be instrumented
|
||||||
(AFL_LLVM_ALLOWLIST) or should specifically NOT be instrumented (AFL_LLVM_DENYLIST).
|
(AFL_LLVM_ALLOWLIST) or are specifically NOT to be instrumented (AFL_LLVM_DENYLIST).
|
||||||
|
|
||||||
For matching, the function/filename that is being compiled must end in the
|
GCC_PLUGIN: you can use either AFL_LLVM_ALLOWLIST or AFL_GCC_ALLOWLIST (or the
|
||||||
function/filename entry contained in this instrument file list (to avoid
|
same for _DENYLIST), both work.
|
||||||
breaking the matching when absolute paths are used during compilation).
|
|
||||||
|
|
||||||
**NOTE:** In builds with optimization enabled functions might be inlined and would not match!
|
For matching to succeed, the function/file name that is being compiled must end in the
|
||||||
|
function/file name entry contained in this instrument file list. That is to avoid
|
||||||
|
breaking the match when absolute paths are used during compilation.
|
||||||
|
|
||||||
|
**NOTE:** In builds with optimization enabled, functions might be inlined and would not match!
|
||||||
|
|
||||||
For example if your source tree looks like this:
|
For example if your source tree looks like this:
|
||||||
```
|
```
|
||||||
@ -52,13 +61,13 @@ project/feature_b/b1.cpp
|
|||||||
project/feature_b/b2.cpp
|
project/feature_b/b2.cpp
|
||||||
```
|
```
|
||||||
|
|
||||||
and you only want to test feature_a, then create a instrument file list file containing:
|
and you only want to test feature_a, then create an "instrument file list" file containing:
|
||||||
```
|
```
|
||||||
feature_a/a1.cpp
|
feature_a/a1.cpp
|
||||||
feature_a/a2.cpp
|
feature_a/a2.cpp
|
||||||
```
|
```
|
||||||
|
|
||||||
However if the instrument file list file contains only this, it works as well:
|
However if the "instrument file list" file contains only this, it works as well:
|
||||||
```
|
```
|
||||||
a1.cpp
|
a1.cpp
|
||||||
a2.cpp
|
a2.cpp
|
||||||
@ -67,9 +76,9 @@ but it might lead to files being unwantedly instrumented if the same filename
|
|||||||
exists somewhere else in the project directories.
|
exists somewhere else in the project directories.
|
||||||
|
|
||||||
You can also specify function names. Note that for C++ the function names
|
You can also specify function names. Note that for C++ the function names
|
||||||
must be mangled to match!
|
must be mangled to match! `nm` can print these names.
|
||||||
|
|
||||||
afl++ is able to identify if an entry is a filename or a function.
|
afl++ is able to identify whether an entry is a filename or a function.
|
||||||
However if you want to be sure (and compliant to the sancov allow/blocklist
|
However if you want to be sure (and compliant to the sancov allow/blocklist
|
||||||
format), you can specify source file entries like this:
|
format), you can specify source file entries like this:
|
||||||
```
|
```
|
||||||
@ -82,5 +91,6 @@ fun: MallocFoo
|
|||||||
Note that whitespace is ignored and comments (`# foo`) are supported.
|
Note that whitespace is ignored and comments (`# foo`) are supported.
|
||||||
|
|
||||||
## 4) UNIX-style pattern matching
|
## 4) UNIX-style pattern matching
|
||||||
You can add UNIX-style pattern matching in the the instrument file list entries.
|
|
||||||
|
You can add UNIX-style pattern matching in the "instrument file list" entries.
|
||||||
See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags.
|
See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags.
|
@ -1,5 +1,15 @@
|
|||||||
# laf-intel instrumentation
|
# laf-intel instrumentation
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
This originally is the work of an individual nicknamed laf-intel.
|
||||||
|
His blog [Circumventing Fuzzing Roadblocks with Compiler Transformations]
|
||||||
|
(https://lafintel.wordpress.com/) and gitlab repo [laf-llvm-pass]
|
||||||
|
(https://gitlab.com/laf-intel/laf-llvm-pass/)
|
||||||
|
describe some code transformations that
|
||||||
|
help afl++ to enter conditional blocks, where conditions consist of
|
||||||
|
comparisons of large values.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
By default these passes will not run when you compile programs using
|
By default these passes will not run when you compile programs using
|
||||||
@ -24,18 +34,22 @@ Enables the split-compares pass.
|
|||||||
By default it will
|
By default it will
|
||||||
1. simplify operators >= (and <=) into chains of > (<) and == comparisons
|
1. simplify operators >= (and <=) into chains of > (<) and == comparisons
|
||||||
2. change signed integer comparisons to a chain of sign-only comparison
|
2. change signed integer comparisons to a chain of sign-only comparison
|
||||||
and unsigned comparisons
|
and unsigned integer comparisons
|
||||||
3. split all unsigned integer comparisons with bit widths of
|
3. split all unsigned integer comparisons with bit widths of
|
||||||
64, 32 or 16 bits to chains of 8 bits comparisons.
|
64, 32 or 16 bits to chains of 8 bits comparisons.
|
||||||
|
|
||||||
You can change the behaviour of the last step by setting
|
You can change the behaviour of the last step by setting
|
||||||
`export AFL_LLVM_LAF_SPLIT_COMPARES_BITW=<bit_width>`, where
|
`export AFL_LLVM_LAF_SPLIT_COMPARES_BITW=<bit_width>`, where
|
||||||
bit_width may be 64, 32 or 16.
|
bit_width may be 64, 32 or 16. For example, a bit_width of 16
|
||||||
|
would split larger comparisons down to 16 bit comparisons.
|
||||||
|
|
||||||
A new experimental feature is splitting floating point comparisons into a
|
A new experimental feature is splitting floating point comparisons into a
|
||||||
series of sign, exponent and mantissa comparisons followed by splitting each
|
series of sign, exponent and mantissa comparisons followed by splitting each
|
||||||
of them into 8 bit comparisons when necessary.
|
of them into 8 bit comparisons when necessary.
|
||||||
It is activated with the `AFL_LLVM_LAF_SPLIT_FLOATS` setting.
|
It is activated with the `AFL_LLVM_LAF_SPLIT_FLOATS` setting.
|
||||||
|
Please note that full IEEE 754 functionality is not preserved, that is
|
||||||
|
values of nan and infinity will probably behave differently.
|
||||||
|
|
||||||
Note that setting this automatically activates `AFL_LLVM_LAF_SPLIT_COMPARES`
|
Note that setting this automatically activates `AFL_LLVM_LAF_SPLIT_COMPARES`
|
||||||
|
|
||||||
You can also set `AFL_LLVM_LAF_ALL` and have all of the above enabled :-)
|
You can also set `AFL_LLVM_LAF_ALL` and have all of the above enabled :-)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user