mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-08 16:21:32 +00:00
Merge branch 'master-upstream' into custom_mutator_docs
# Conflicts: # afl-fuzz.c
This commit is contained in:
commit
b31dff6bee
148
.clang-format
Normal file
148
.clang-format
Normal file
@ -0,0 +1,148 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
AccessModifierOffset: -1
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: true
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^<ext/.*\.h>'
|
||||
Priority: 2
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Never
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Right
|
||||
RawStringFormats:
|
||||
- Language: Cpp
|
||||
Delimiters:
|
||||
- cc
|
||||
- CC
|
||||
- cpp
|
||||
- Cpp
|
||||
- CPP
|
||||
- 'c++'
|
||||
- 'C++'
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
- Language: TextProto
|
||||
Delimiters:
|
||||
- pb
|
||||
- PB
|
||||
- proto
|
||||
- PROTO
|
||||
EnclosingFunctions:
|
||||
- EqualsProto
|
||||
- EquivToProto
|
||||
- PARSE_PARTIAL_TEXT_PROTO
|
||||
- PARSE_TEST_PROTO
|
||||
- PARSE_TEXT_PROTO
|
||||
- ParseTextOrDie
|
||||
- ParseTextProtoOrDie
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
|
102
.custom-format.py
Executable file
102
.custom-format.py
Executable file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
# string_re = re.compile('(\\"(\\\\.|[^"\\\\])*\\")') # future use
|
||||
|
||||
with open(".clang-format") as f:
|
||||
fmt = f.read()
|
||||
|
||||
CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN")
|
||||
if CLANG_FORMAT_BIN is None:
|
||||
p = subprocess.Popen(["clang-format", "--version"], stdout=subprocess.PIPE)
|
||||
o, _ = p.communicate()
|
||||
o = str(o, "utf-8")
|
||||
o = o[len("clang-format version "):].strip()
|
||||
o = o[:o.find(".")]
|
||||
o = int(o)
|
||||
if o < 7:
|
||||
if subprocess.call(['which', 'clang-format-7'], stdout=subprocess.PIPE) == 0:
|
||||
CLANG_FORMAT_BIN = 'clang-format-7'
|
||||
elif subprocess.call(['which', 'clang-format-8'], stdout=subprocess.PIPE) == 0:
|
||||
CLANG_FORMAT_BIN = 'clang-format-8'
|
||||
elif subprocess.call(['which', 'clang-format-9'], stdout=subprocess.PIPE) == 0:
|
||||
CLANG_FORMAT_BIN = 'clang-format-9'
|
||||
elif subprocess.call(['which', 'clang-format-10'], stdout=subprocess.PIPE) == 0:
|
||||
CLANG_FORMAT_BIN = 'clang-format-10'
|
||||
else:
|
||||
print ("clang-format 7 or above is needed. Aborted.")
|
||||
exit(1)
|
||||
else:
|
||||
CLANG_FORMAT_BIN = 'clang-format'
|
||||
|
||||
COLUMN_LIMIT = 80
|
||||
for line in fmt.split("\n"):
|
||||
line = line.split(":")
|
||||
if line[0].strip() == "ColumnLimit":
|
||||
COLUMN_LIMIT = int(line[1].strip())
|
||||
|
||||
|
||||
def custom_format(filename):
|
||||
p = subprocess.Popen([CLANG_FORMAT_BIN, filename], stdout=subprocess.PIPE)
|
||||
src, _ = p.communicate()
|
||||
src = str(src, "utf-8")
|
||||
|
||||
in_define = False
|
||||
last_line = None
|
||||
out = ""
|
||||
|
||||
for line in src.split("\n"):
|
||||
if line.startswith("#"):
|
||||
if line.startswith("#define"):
|
||||
in_define = True
|
||||
|
||||
if "/*" in line and not line.strip().startswith("/*") and line.endswith("*/") and len(line) < (COLUMN_LIMIT-2):
|
||||
cmt_start = line.rfind("/*")
|
||||
line = line[:cmt_start] + " " * (COLUMN_LIMIT-2 - len(line)) + line[cmt_start:]
|
||||
|
||||
define_padding = 0
|
||||
if last_line is not None and in_define and last_line.endswith("\\"):
|
||||
last_line = last_line[:-1]
|
||||
define_padding = max(0, len(last_line[last_line.rfind("\n")+1:]))
|
||||
|
||||
if last_line is not None and last_line.strip().endswith("{") and line.strip() != "":
|
||||
line = (" " * define_padding + "\\" if in_define else "") + "\n" + line
|
||||
elif last_line is not None and last_line.strip().startswith("}") and line.strip() != "":
|
||||
line = (" " * define_padding + "\\" if in_define else "") + "\n" + line
|
||||
elif line.strip().startswith("}") and last_line is not None and last_line.strip() != "":
|
||||
line = (" " * define_padding + "\\" if in_define else "") + "\n" + line
|
||||
|
||||
if not line.endswith("\\"):
|
||||
in_define = False
|
||||
|
||||
out += line + "\n"
|
||||
last_line = line
|
||||
|
||||
return (out)
|
||||
|
||||
args = sys.argv[1:]
|
||||
if len(args) == 0:
|
||||
print ("Usage: ./format.py [-i] <filename>")
|
||||
print ()
|
||||
print (" The -i option, if specified, let the script to modify in-place")
|
||||
print (" the source files. By default the results are written to stdout.")
|
||||
print()
|
||||
exit(1)
|
||||
|
||||
in_place = False
|
||||
if args[0] == "-i":
|
||||
in_place = True
|
||||
args = args[1:]
|
||||
|
||||
for filename in args:
|
||||
code = custom_format(filename)
|
||||
if in_place:
|
||||
with open(filename, "w") as f:
|
||||
f.write(code)
|
||||
else:
|
||||
print(code)
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,5 @@
|
||||
*.o
|
||||
*.so
|
||||
.gitignore
|
||||
afl-analyze
|
||||
afl-as
|
||||
afl-clang
|
||||
@ -21,3 +20,4 @@ qemu_mode/qemu-3.1.0
|
||||
qemu_mode/qemu-3.1.0.tar.xz
|
||||
unicorn_mode/unicorn
|
||||
unicorn_mode/unicorn-*
|
||||
unicorn_mode/*.tar.gz
|
||||
|
1
Android.mk
Symbolic link
1
Android.mk
Symbolic link
@ -0,0 +1 @@
|
||||
Makefile
|
14
CONTRIBUTING.md
Normal file
14
CONTRIBUTING.md
Normal file
@ -0,0 +1,14 @@
|
||||
# How to submit a Pull Request to AFLplusplus
|
||||
|
||||
Each modified source file, before merging, must be formatted.
|
||||
|
||||
```
|
||||
make code-formatter
|
||||
```
|
||||
|
||||
This should be fine if you modified one of the file already present in the
|
||||
project, otherwise run:
|
||||
|
||||
```
|
||||
./.custom-format.py -i file-that-you-have-created.c
|
||||
```
|
104
Makefile
104
Makefile
@ -17,23 +17,27 @@
|
||||
#TEST_MMAP=1
|
||||
|
||||
PROGNAME = afl
|
||||
VERSION = $(shell grep '^\#define VERSION ' config.h | cut -d '"' -f2)
|
||||
VERSION = $(shell grep '^\#define VERSION ' include/config.h | cut -d '"' -f2)
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
BIN_PATH = $(PREFIX)/bin
|
||||
HELPER_PATH = $(PREFIX)/lib/afl
|
||||
DOC_PATH = $(PREFIX)/share/doc/afl
|
||||
MISC_PATH = $(PREFIX)/share/afl
|
||||
MAN_PATH = $(PREFIX)/man/man8
|
||||
|
||||
# PROGS intentionally omit afl-as, which gets installed elsewhere.
|
||||
|
||||
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
|
||||
SH_PROGS = afl-plot afl-cmin afl-whatsup afl-system-config
|
||||
MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8)
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops
|
||||
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \
|
||||
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I include/ \
|
||||
-DAFL_PATH=\"$(HELPER_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" \
|
||||
-DBIN_PATH=\"$(BIN_PATH)\"
|
||||
-DBIN_PATH=\"$(BIN_PATH)\" -Wno-unused-function
|
||||
|
||||
AFL_FUZZ_FILES = $(wildcard src/afl-fuzz*.c)
|
||||
|
||||
PYTHON_INCLUDE ?= /usr/include/python2.7
|
||||
|
||||
@ -47,7 +51,7 @@ else
|
||||
TEST_CC = afl-clang
|
||||
endif
|
||||
|
||||
COMM_HDR = alloc-inl.h config.h debug.h types.h
|
||||
COMM_HDR = include/alloc-inl.h include/config.h include/debug.h include/types.h
|
||||
|
||||
|
||||
ifeq "$(shell echo '\#include <Python.h>@int main() {return 0; }' | tr @ '\n' | $(CC) -x c - -o .test -I$(PYTHON_INCLUDE) -lpython2.7 2>/dev/null && echo 1 || echo 0 )" "1"
|
||||
@ -123,45 +127,65 @@ endif
|
||||
ready:
|
||||
@echo "[+] Everything seems to be working, ready to compile."
|
||||
|
||||
afl-gcc: afl-gcc.c $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
||||
afl-gcc: src/afl-gcc.c $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||
set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $$i; done
|
||||
|
||||
afl-as: afl-as.c afl-as.h $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
||||
afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||
ln -sf afl-as as
|
||||
|
||||
afl-common.o : afl-common.c
|
||||
$(CC) $(CFLAGS) -c afl-common.c
|
||||
afl-common.o : src/afl-common.c include/common.h
|
||||
$(CC) $(CFLAGS) -c src/afl-common.c
|
||||
|
||||
sharedmem.o : sharedmem.c
|
||||
$(CC) $(CFLAGS) -c sharedmem.c
|
||||
afl-forkserver.o : src/afl-forkserver.c include/forkserver.h
|
||||
$(CC) $(CFLAGS) -c src/afl-forkserver.c
|
||||
|
||||
afl-fuzz: afl-fuzz.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS) $(PYFLAGS)
|
||||
afl-sharedmem.o : src/afl-sharedmem.c include/sharedmem.h
|
||||
$(CC) $(CFLAGS) -c src/afl-sharedmem.c
|
||||
|
||||
afl-showmap: afl-showmap.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS)
|
||||
afl-fuzz: include/afl-fuzz.h $(AFL_FUZZ_FILES) afl-common.o afl-sharedmem.o afl-forkserver.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $(AFL_FUZZ_FILES) afl-common.o afl-sharedmem.o afl-forkserver.o -o $@ $(LDFLAGS) $(PYFLAGS)
|
||||
|
||||
afl-tmin: afl-tmin.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS)
|
||||
afl-showmap: src/afl-showmap.c afl-common.o afl-sharedmem.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) src/$@.c afl-common.o afl-sharedmem.o -o $@ $(LDFLAGS)
|
||||
|
||||
afl-analyze: afl-analyze.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS)
|
||||
afl-tmin: src/afl-tmin.c afl-common.o afl-sharedmem.o afl-forkserver.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) src/$@.c afl-common.o afl-sharedmem.o afl-forkserver.o -o $@ $(LDFLAGS)
|
||||
|
||||
afl-gotcpu: afl-gotcpu.c $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
||||
afl-analyze: src/afl-analyze.c afl-common.o afl-sharedmem.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) src/$@.c afl-common.o afl-sharedmem.o -o $@ $(LDFLAGS)
|
||||
|
||||
afl-gotcpu: src/afl-gotcpu.c $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||
|
||||
|
||||
code-format:
|
||||
./.custom-format.py -i src/*.c
|
||||
./.custom-format.py -i include/*.h
|
||||
./.custom-format.py -i libdislocator/*.c
|
||||
./.custom-format.py -i libtokencap/*.c
|
||||
./.custom-format.py -i llvm_mode/*.c
|
||||
./.custom-format.py -i llvm_mode/*.h
|
||||
./.custom-format.py -i llvm_mode/*.cc
|
||||
./.custom-format.py -i qemu_mode/patches/*.h
|
||||
./.custom-format.py -i qemu_mode/libcompcov/*.c
|
||||
./.custom-format.py -i qemu_mode/libcompcov/*.cc
|
||||
./.custom-format.py -i qemu_mode/libcompcov/*.h
|
||||
./.custom-format.py -i unicorn_mode/patches/*.h
|
||||
./.custom-format.py -i *.h
|
||||
./.custom-format.py -i *.c
|
||||
|
||||
|
||||
ifndef AFL_NO_X86
|
||||
|
||||
test_build: afl-gcc afl-as afl-showmap
|
||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||
unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS)
|
||||
echo 0 | ./afl-showmap -m none -q -o .test-instr0 ./test-instr
|
||||
unset AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS)
|
||||
./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
|
||||
echo 1 | ./afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||
@rm -f test-instr
|
||||
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please ping <lcamtuf@google.com> 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/vanhauser-thc/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
|
||||
@echo "[+] All right, the instrumentation seems to be working!"
|
||||
|
||||
else
|
||||
@ -181,13 +205,33 @@ all_done: test_build
|
||||
.NOTPARALLEL: clean
|
||||
|
||||
clean:
|
||||
rm -f $(PROGS) afl-as as afl-g++ afl-clang afl-clang++ *.o *~ a.out core core.[1-9][0-9]* *.stackdump test .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.0.tar.xz afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast
|
||||
rm -rf out_dir qemu_mode/qemu-3.1.0
|
||||
rm -f $(PROGS) afl-as as afl-g++ afl-clang afl-clang++ *.o *~ a.out core core.[1-9][0-9]* *.stackdump test .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.0.tar.xz afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast *.so unicorn_mode/24f55a7973278f20f0de21b904851d99d4716263.tar.gz *.8
|
||||
rm -rf out_dir qemu_mode/qemu-3.1.0 unicorn_mode/unicorn
|
||||
$(MAKE) -C llvm_mode clean
|
||||
$(MAKE) -C libdislocator clean
|
||||
$(MAKE) -C libtokencap clean
|
||||
$(MAKE) -C qemu_mode/libcompcov clean
|
||||
|
||||
install: all
|
||||
%.8: %
|
||||
@echo .TH $* 8 `date --iso-8601` "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 \"hexc0der\" Eissfeldt <heiko.eissfeldt@hexco.de> and Andrea Fioraldi <andreafioraldi@gmail.com>" >> $@
|
||||
@echo The homepage of afl++ is: https://github.com/vanhauser-thc/AFLplusplus >> $@
|
||||
@echo >> $@
|
||||
@echo .SH LICENSE >> $@
|
||||
@echo Apache License Version 2.0, January 2004 >> $@
|
||||
|
||||
install: all $(MANPAGES)
|
||||
mkdir -p -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
|
||||
rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh
|
||||
install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH)
|
||||
@ -204,10 +248,14 @@ endif
|
||||
if [ -f compare-transform-pass.so ]; then set -e; install -m 755 compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f split-compares-pass.so ]; then set -e; install -m 755 split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f split-switches-pass.so ]; then set -e; install -m 755 split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi
|
||||
|
||||
set -e; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-g++
|
||||
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 $(MAN_PATH)
|
||||
install -m0644 -D *.8 $(MAN_PATH)
|
||||
|
||||
install -m 755 afl-as $${DESTDIR}$(HELPER_PATH)
|
||||
ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as
|
||||
install -m 644 docs/README.md docs/ChangeLog docs/*.txt $${DESTDIR}$(DOC_PATH)
|
||||
|
52
README.md
52
README.md
@ -1,6 +1,7 @@
|
||||
# american fuzzy lop plus plus (afl++)
|
||||
|
||||
Release Version: 2.53c
|
||||
|
||||
Github Version: 2.53d
|
||||
|
||||
|
||||
@ -8,32 +9,41 @@
|
||||
|
||||
Repository: [https://github.com/vanhauser-thc/AFLplusplus](https://github.com/vanhauser-thc/AFLplusplus)
|
||||
|
||||
afl++ is maintained by Marc Heuse <mh@mh-sec.de>, Heiko Eißfeldt
|
||||
<heiko.eissfeldt@hexco.de> and Andrea Fioraldi <andreafioraldi@gmail.com>.
|
||||
afl++ is maintained by Marc "van Hauser" Heuse <mh@mh-sec.de>,
|
||||
Heiko "hexc0der" Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>.
|
||||
|
||||
Note that although afl now has a Google afl repository [https://github.com/Google/afl](https://github.com/Google/afl),
|
||||
it is unlikely to receive any noteable enhancements: [https://twitter.com/Dor3s/status/1154737061787660288](https://twitter.com/Dor3s/status/1154737061787660288)
|
||||
|
||||
|
||||
## The enhancements compared to the original stock afl
|
||||
|
||||
Many improvements were made over the official afl release - which did not
|
||||
get any improvements since November 2017.
|
||||
|
||||
Among others afl++ has, e.g. more performant llvm_mode, supporting
|
||||
llvm up to version 8, Qemu 3.1, more speed and crashfixes for Qemu,
|
||||
laf-intel feature for Qemu (with libcompcov) and more.
|
||||
Among others afl++ has a more performant llvm_mode, supporting
|
||||
llvm up to version 9, Qemu 3.1, more speed and crashfixes for Qemu,
|
||||
better *BSD and Android support and much, much more.
|
||||
|
||||
Additionally the following patches have been integrated:
|
||||
|
||||
* AFLfast's power schedules by Marcel Böhme: [https://github.com/mboehme/aflfast](https://github.com/mboehme/aflfast)
|
||||
|
||||
* C. Hollers afl-fuzz Python mutator module and llvm_mode whitelist support: [https://github.com/choller/afl](https://github.com/choller/afl)
|
||||
|
||||
* the new excellent MOpt mutator: [https://github.com/puppet-meteor/MOpt-AFL](https://github.com/puppet-meteor/MOpt-AFL)
|
||||
|
||||
* instrim, a very effective CFG llvm_mode instrumentation implementation for large targets: [https://github.com/csienslab/instrim](https://github.com/csienslab/instrim)
|
||||
|
||||
* unicorn_mode which allows fuzzing of binaries from completely different platforms (integration provided by domenukk)
|
||||
* C. Holler's afl-fuzz Python mutator module and llvm_mode whitelist support: [https://github.com/choller/afl](https://github.com/choller/afl)
|
||||
|
||||
* Custom mutator by a library (instead of Python) by kyakdan
|
||||
|
||||
* unicorn_mode which allows fuzzing of binaries from completely different platforms (integration provided by domenukk)
|
||||
|
||||
* laf-intel (compcov) support for llvm_mode, qemu_mode and unicorn_mode
|
||||
|
||||
* neverZero patch for afl-gcc, llvm_mode, qemu_mode and unicorn_mode which prevents a wrapping map value to zero, increases coverage (by Andrea Fioraldi)
|
||||
|
||||
A more thorough list is available in the PATCHES file.
|
||||
|
||||
So all in all this is the best-of AFL that is currently out there :-)
|
||||
@ -49,7 +59,6 @@
|
||||
|
||||
|
||||
## 1) Challenges of guided fuzzing
|
||||
-------------------------------
|
||||
|
||||
Fuzzing is one of the most powerful and proven strategies for identifying
|
||||
security issues in real-world software; it is responsible for the vast
|
||||
@ -120,7 +129,7 @@ superior to blind fuzzing or coverage-only tools.
|
||||
PLEASE NOTE: llvm_mode compilation with afl-clang-fast/afl-clang-fast++
|
||||
instead of afl-gcc/afl-g++ is much faster and has a few cool features.
|
||||
See llvm_mode/ - however few code does not compile with llvm.
|
||||
We support llvm versions 4.0 to 8.
|
||||
We support llvm versions 3.8.0 to 9.
|
||||
|
||||
When source code is available, instrumentation can be injected by a companion
|
||||
tool that works as a drop-in replacement for gcc or clang in any standard build
|
||||
@ -143,7 +152,7 @@ For C++ programs, you'd would also want to set `CXX=/path/to/afl/afl-g++`.
|
||||
The clang wrappers (afl-clang and afl-clang++) can be used in the same way;
|
||||
clang users may also opt to leverage a higher-performance instrumentation mode,
|
||||
as described in [llvm_mode/README.llvm](llvm_mode/README.llvm).
|
||||
Clang/LLVM has a much better performance and works with LLVM version 4.0 to 8.
|
||||
Clang/LLVM has a much better performance and works with LLVM version 3.8.0 to 9.
|
||||
|
||||
Using the LAF Intel performance enhancements are also recommended, see
|
||||
[llvm_mode/README.laf-intel](llvm_mode/README.laf-intel)
|
||||
@ -172,7 +181,6 @@ file for important caveats.
|
||||
|
||||
|
||||
## 4) Instrumenting binary-only apps
|
||||
---------------------------------
|
||||
|
||||
When source code is *NOT* available, the fuzzer offers experimental support for
|
||||
fast, on-the-fly instrumentation of black-box binaries. This is accomplished
|
||||
@ -200,7 +208,6 @@ A more comprehensive description of these and other options can be found in
|
||||
|
||||
|
||||
## 5) Power schedules
|
||||
------------------
|
||||
|
||||
The power schedules were copied from Marcel Böhme's excellent AFLfast
|
||||
implementation and expands on the ability to discover new paths and
|
||||
@ -232,7 +239,6 @@ Computer and Communications Security (CCS'16):
|
||||
|
||||
|
||||
## 6) Choosing initial test cases
|
||||
------------------------------
|
||||
|
||||
To operate correctly, the fuzzer requires one or more starting file that
|
||||
contains a good example of the input data normally expected by the targeted
|
||||
@ -254,7 +260,6 @@ exercise different code paths in the target binary.
|
||||
|
||||
|
||||
## 7) Fuzzing binaries
|
||||
-------------------
|
||||
|
||||
The fuzzing process itself is carried out by the afl-fuzz utility. This program
|
||||
requires a read-only directory with initial test cases, a separate place to
|
||||
@ -293,7 +298,6 @@ fuzzers - add the -d option to the command line.
|
||||
|
||||
|
||||
## 8) Interpreting output
|
||||
----------------------
|
||||
|
||||
See the [docs/status_screen.txt](docs/status_screen.txt) file for information on
|
||||
how to interpret the displayed stats and monitor the health of the process. Be
|
||||
@ -355,7 +359,6 @@ see [http://lcamtuf.coredump.cx/afl/plot/](http://lcamtuf.coredump.cx/afl/plot/)
|
||||
|
||||
|
||||
## 9) Parallelized fuzzing
|
||||
-----------------------
|
||||
|
||||
Every instance of afl-fuzz takes up roughly one core. This means that on
|
||||
multi-core systems, parallelization is necessary to fully utilize the hardware.
|
||||
@ -368,7 +371,6 @@ last section of [docs/parallel_fuzzing.txt](docs/parallel_fuzzing.txt) for tips.
|
||||
|
||||
|
||||
## 10) Fuzzer dictionaries
|
||||
----------------------
|
||||
|
||||
By default, afl-fuzz mutation engine is optimized for compact data formats -
|
||||
say, images, multimedia, compressed data, regular expression syntax, or shell
|
||||
@ -405,7 +407,6 @@ utility with AFL. For that, see [libtokencap/README.tokencap](libtokencap/README
|
||||
|
||||
|
||||
## 11) Crash triage
|
||||
----------------
|
||||
|
||||
The coverage-based grouping of crashes usually produces a small data set that
|
||||
can be quickly triaged manually or with a very simple GDB or Valgrind script.
|
||||
@ -454,7 +455,6 @@ near the end of [docs/technical_details.txt](docs/technical_details.txt).
|
||||
|
||||
|
||||
## 12) Going beyond crashes
|
||||
------------------------
|
||||
|
||||
Fuzzing is a wonderful and underutilized technique for discovering non-crashing
|
||||
design and implementation errors, too. Quite a few interesting bugs have been
|
||||
@ -479,7 +479,6 @@ shared with libfuzzer) or `#ifdef __AFL_COMPILER` (this one is just for AFL).
|
||||
|
||||
|
||||
## 13) Common-sense risks
|
||||
----------------------
|
||||
|
||||
Please keep in mind that, similarly to many other computationally-intensive
|
||||
tasks, fuzzing may put strain on your hardware and on the OS. In particular:
|
||||
@ -510,7 +509,6 @@ tasks, fuzzing may put strain on your hardware and on the OS. In particular:
|
||||
|
||||
|
||||
## 14) Known limitations & areas for improvement
|
||||
---------------------------------------------
|
||||
|
||||
Here are some of the most important caveats for AFL:
|
||||
|
||||
@ -552,10 +550,9 @@ Beyond this, see INSTALL for platform-specific tips.
|
||||
|
||||
|
||||
## 15) Special thanks
|
||||
------------------
|
||||
|
||||
Many of the improvements to the original afl wouldn't be possible without
|
||||
feedback, bug reports, or patches from:
|
||||
Many of the improvements to the original afl and afl++ wouldn't be possible
|
||||
without feedback, bug reports, or patches from:
|
||||
|
||||
```
|
||||
Jann Horn Hanno Boeck
|
||||
@ -597,14 +594,15 @@ feedback, bug reports, or patches from:
|
||||
Rene Freingruber Sergey Davidoff
|
||||
Sami Liedes Craig Young
|
||||
Andrzej Jackowski Daniel Hodson
|
||||
Nathan Voss Dominik Maier
|
||||
Nathan Voss Dominik Maier
|
||||
Andrea Biondo Vincent Le Garrec
|
||||
Khaled Yakdan Kuang-che Wu
|
||||
```
|
||||
|
||||
Thank you!
|
||||
|
||||
|
||||
## 16) Contact
|
||||
-----------
|
||||
|
||||
Questions? Concerns? Bug reports? The contributors can be reached via
|
||||
[https://github.com/vanhauser-thc/AFLplusplus](https://github.com/vanhauser-thc/AFLplusplus)
|
||||
|
57
TODO
57
TODO
@ -1,34 +1,61 @@
|
||||
Roadmap 2.53d:
|
||||
==============
|
||||
- indent all the code: clang-format -style=Google
|
||||
|
||||
- update docs/sister_projects.txt
|
||||
|
||||
afl-fuzz:
|
||||
- put mutator, scheduler, forkserver and input channels in individual files
|
||||
- reuse forkserver for showmap, afl-cmin, etc.
|
||||
- custom mutator lib: example and readme
|
||||
|
||||
man:
|
||||
- man page for afl-clang-fast
|
||||
|
||||
|
||||
Roadmap 2.54d:
|
||||
==============
|
||||
|
||||
gcc_plugin:
|
||||
- needs to be rewritten
|
||||
- fix crashes when compiling :(
|
||||
- whitelist support
|
||||
- skip over uninteresting blocks
|
||||
- laf-intel
|
||||
- neverZero
|
||||
|
||||
qemu_mode:
|
||||
- deferred mode with AFL_DEFERRED_QEMU=0xaddress
|
||||
- update to 4.x (probably this will be skipped :( )
|
||||
|
||||
unit testing / or large testcase campaign
|
||||
|
||||
|
||||
Roadmap 2.54d:
|
||||
==============
|
||||
- expand MAP size to 256k (current L2 cache size on processors)
|
||||
-> 18 bit map
|
||||
- llvm_mode: dynamic map size and collission free basic block IDs
|
||||
|
||||
Problem: Average targets (tiff, jpeg, unrar) go through 1500 edges.
|
||||
At afl's default map that means ~16 collisions and ~3 wrappings.
|
||||
Solution #1: increase map size.
|
||||
every +1 decreases fuzzing speed by ~10% and halfs the collisions
|
||||
birthday paradox predicts collisions at this # of edges:
|
||||
mapsize => collisions
|
||||
2^16 = 302
|
||||
2^17 = 427
|
||||
2^18 = 603
|
||||
2^19 = 853
|
||||
2^20 = 1207
|
||||
2^21 = 1706
|
||||
2^22 = 2412
|
||||
2^23 = 3411
|
||||
2^24 = 4823
|
||||
Increasing the map is an easy solution but also not a good one.
|
||||
Solution #2: use dynamic map size and collision free basic block IDs
|
||||
This only works in llvm_mode and llvm >= 9 though
|
||||
A potential good future solution. Heiko/hexcoder follows this up
|
||||
Solution #3: write instruction pointers to a big shared map
|
||||
512kb/1MB shared map and the instrumented code writes the instruction
|
||||
pointer into the map. Map must be big enough but could be command line
|
||||
controlled.
|
||||
Good: complete coverage information, nothing is lost. choice of analysis
|
||||
impacts speed, but this can be decided by user options
|
||||
Neutral: a little bit slower but no loss of coverage
|
||||
Bad: completely changes how afl uses the map and the scheduling.
|
||||
Overall another very good solution, Marc Heuse/vanHauser follows this up
|
||||
|
||||
qemu_mode:
|
||||
- persistent mode patching the return address (WinAFL style)
|
||||
- instrument only comparison with immediate values by default when using compcov
|
||||
- deferred mode with AFL_DEFERRED_QEMU=0xaddress
|
||||
(AFL_ENTRYPOINT let you to specify only a basic block address as starting
|
||||
point. This will be implemented togheter with the logic for persistent
|
||||
mode.)
|
||||
|
||||
|
5
afl-cmin
5
afl-cmin
@ -51,10 +51,13 @@ TIMEOUT=none
|
||||
unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \
|
||||
AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE
|
||||
|
||||
while getopts "+i:o:f:m:t:eQUC" opt; do
|
||||
while getopts "+i:o:f:m:t:eQUCh" opt; do
|
||||
|
||||
case "$opt" in
|
||||
|
||||
"h")
|
||||
;;
|
||||
|
||||
"i")
|
||||
IN_DIR="$OPTARG"
|
||||
;;
|
||||
|
69
afl-common.c
69
afl-common.c
@ -1,69 +0,0 @@
|
||||
/*
|
||||
gather some functions common to multiple executables
|
||||
|
||||
detect_file_args
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "alloc-inl.h"
|
||||
|
||||
/* Detect @@ in args. */
|
||||
#ifndef __glibc__
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
void detect_file_args(char** argv, u8* prog_in) {
|
||||
|
||||
u32 i = 0;
|
||||
#ifdef __GLIBC__
|
||||
u8* cwd = getcwd(NULL, 0); /* non portable glibc extension */
|
||||
#else
|
||||
u8* cwd;
|
||||
char *buf;
|
||||
long size = pathconf(".", _PC_PATH_MAX);
|
||||
if ((buf = (char *)malloc((size_t)size)) != NULL) {
|
||||
cwd = getcwd(buf, (size_t)size); /* portable version */
|
||||
} else {
|
||||
PFATAL("getcwd() failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!cwd) PFATAL("getcwd() failed");
|
||||
|
||||
while (argv[i]) {
|
||||
|
||||
u8* aa_loc = strstr(argv[i], "@@");
|
||||
|
||||
if (aa_loc) {
|
||||
|
||||
u8 *aa_subst, *n_arg;
|
||||
|
||||
if (!prog_in) FATAL("@@ syntax is not supported by this tool.");
|
||||
|
||||
/* Be sure that we're always using fully-qualified paths. */
|
||||
|
||||
if (prog_in[0] == '/') aa_subst = prog_in;
|
||||
else aa_subst = alloc_printf("%s/%s", cwd, prog_in);
|
||||
|
||||
/* Construct a replacement argv value. */
|
||||
|
||||
*aa_loc = 0;
|
||||
n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);
|
||||
argv[i] = n_arg;
|
||||
*aa_loc = '@';
|
||||
|
||||
if (prog_in[0] != '/') ck_free(aa_subst);
|
||||
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
free(cwd); /* not tracked */
|
||||
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
#ifndef __AFLCOMMON_H
|
||||
#define __AFLCOMMON_H
|
||||
|
||||
void detect_file_args(char **argv, u8 *prog_in);
|
||||
#endif
|
12659
afl-fuzz.c
12659
afl-fuzz.c
File diff suppressed because it is too large
Load Diff
4
afl-plot
4
afl-plot
@ -21,10 +21,10 @@ echo
|
||||
if [ ! "$#" = "2" ]; then
|
||||
|
||||
cat 1>&2 <<_EOF_
|
||||
This program generates gnuplot images from afl-fuzz output data. Usage:
|
||||
|
||||
$0 afl_state_dir graph_output_dir
|
||||
|
||||
This program generates gnuplot images from afl-fuzz output data. Usage:
|
||||
|
||||
The afl_state_dir parameter should point to an existing state directory for any
|
||||
active or stopped instance of afl-fuzz; while graph_output_dir should point to
|
||||
an empty directory where this tool can write the resulting plots to.
|
||||
|
@ -1,9 +1,24 @@
|
||||
#!/bin/sh
|
||||
test "$1" = "-h" && {
|
||||
echo afl-system-config by Marc Heuse
|
||||
echo
|
||||
echo $0
|
||||
echo
|
||||
echo afl-system-config has no command line options
|
||||
echo
|
||||
echo afl-system reconfigures the system to a high performance fuzzing state
|
||||
echo WARNING: this reduces the security of the system
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
PLATFORM=`uname -s`
|
||||
echo This reconfigures the system to have a better fuzzing performance
|
||||
if [ '!' "$EUID" = 0 ] && [ '!' `id -u` = 0 ] ; then
|
||||
echo Error you need to be root to run this
|
||||
exit 1
|
||||
fi
|
||||
if [ "$PLATFORM" = "Linux" ] ; then
|
||||
sysctl -w kernel.core_pattern=core
|
||||
sysctl -w kernel.randomize_va_space=0
|
||||
sysctl -w kernel.sched_child_runs_first=1
|
||||
@ -19,5 +34,19 @@ test -e /sys/devices/system/cpu/cpufreq/boost && echo 1 > /sys/devices/system/cp
|
||||
echo
|
||||
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
||||
echo '/etc/default/grub: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"'
|
||||
fi
|
||||
if [ "$PLATFORM" = "FreeBSD" ] ; then
|
||||
sysctl kern.elf32.aslr.enable=0
|
||||
sysctl kern.elf64.aslr.enable=0
|
||||
echo
|
||||
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
||||
echo 'sysctl hw.ibrs_disable=1'
|
||||
echo
|
||||
echo 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.'
|
||||
fi
|
||||
if [ "$PLATFORM" = "OpenBSD" ] ; then
|
||||
echo
|
||||
echo 'System security features cannot be disabled on OpenBSD.'
|
||||
fi
|
||||
echo
|
||||
echo Also use AFL_TMPDIR to use a tmpfs for the input file
|
||||
|
@ -19,6 +19,13 @@
|
||||
|
||||
echo "status check tool for afl-fuzz by <lcamtuf@google.com>"
|
||||
echo
|
||||
test "$1" = "-h" && {
|
||||
echo $0
|
||||
echo
|
||||
echo afl-whatsup has no command line options
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ "$1" = "-s" ]; then
|
||||
|
||||
@ -54,7 +61,7 @@ fi
|
||||
|
||||
CUR_TIME=`date +%s`
|
||||
|
||||
TMP=`mktemp -t .afl-whatsup-XXXXXXXX` || exit 1
|
||||
TMP=`mktemp -t .afl-whatsup-XXXXXXXX` || TMP=`mktemp -p /data/local/tmp .afl-whatsup-XXXXXXXX` || exit 1
|
||||
|
||||
ALIVE_CNT=0
|
||||
DEAD_CNT=0
|
||||
|
359
config.h
359
config.h
@ -1,359 +0,0 @@
|
||||
/*
|
||||
american fuzzy lop plus plus - vaguely configurable bits
|
||||
----------------------------------------------
|
||||
|
||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Copyright 2013, 2014, 2015, 2016 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _HAVE_CONFIG_H
|
||||
#define _HAVE_CONFIG_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* Version string: */
|
||||
|
||||
#define VERSION "++2.53d" // c = release, d = volatile github dev
|
||||
|
||||
/******************************************************
|
||||
* *
|
||||
* Settings that may be of interest to power users: *
|
||||
* *
|
||||
******************************************************/
|
||||
|
||||
/* Comment out to disable terminal colors (note that this makes afl-analyze
|
||||
a lot less nice): */
|
||||
|
||||
#define USE_COLOR
|
||||
|
||||
/* Comment out to disable fancy ANSI boxes and use poor man's 7-bit UI: */
|
||||
|
||||
#define FANCY_BOXES
|
||||
|
||||
/* Default timeout for fuzzed code (milliseconds). This is the upper bound,
|
||||
also used for detecting hangs; the actual value is auto-scaled: */
|
||||
|
||||
#define EXEC_TIMEOUT 1000
|
||||
|
||||
/* Timeout rounding factor when auto-scaling (milliseconds): */
|
||||
|
||||
#define EXEC_TM_ROUND 20
|
||||
|
||||
/* Default memory limit for child process (MB): */
|
||||
|
||||
#ifndef __x86_64__
|
||||
# define MEM_LIMIT 25
|
||||
#else
|
||||
# define MEM_LIMIT 50
|
||||
#endif /* ^!__x86_64__ */
|
||||
|
||||
/* Default memory limit when running in QEMU mode (MB): */
|
||||
|
||||
#define MEM_LIMIT_QEMU 200
|
||||
|
||||
/* Default memory limit when running in Unicorn mode (MB): */
|
||||
|
||||
#define MEM_LIMIT_UNICORN 200
|
||||
|
||||
/* Number of calibration cycles per every new test case (and for test
|
||||
cases that show variable behavior): */
|
||||
|
||||
#define CAL_CYCLES 8
|
||||
#define CAL_CYCLES_LONG 40
|
||||
|
||||
/* Number of subsequent timeouts before abandoning an input file: */
|
||||
|
||||
#define TMOUT_LIMIT 250
|
||||
|
||||
/* Maximum number of unique hangs or crashes to record: */
|
||||
|
||||
#define KEEP_UNIQUE_HANG 500
|
||||
#define KEEP_UNIQUE_CRASH 5000
|
||||
|
||||
/* Baseline number of random tweaks during a single 'havoc' stage: */
|
||||
|
||||
#define HAVOC_CYCLES 256
|
||||
#define HAVOC_CYCLES_INIT 1024
|
||||
|
||||
/* Maximum multiplier for the above (should be a power of two, beware
|
||||
of 32-bit int overflows): */
|
||||
|
||||
#define HAVOC_MAX_MULT 16
|
||||
#define HAVOC_MAX_MULT_MOPT 32
|
||||
|
||||
/* Absolute minimum number of havoc cycles (after all adjustments): */
|
||||
|
||||
#define HAVOC_MIN 16
|
||||
|
||||
/* Power Schedule Divisor */
|
||||
#define POWER_BETA 1
|
||||
#define MAX_FACTOR (POWER_BETA * 32)
|
||||
|
||||
/* Maximum stacking for havoc-stage tweaks. The actual value is calculated
|
||||
like this:
|
||||
|
||||
n = random between 1 and HAVOC_STACK_POW2
|
||||
stacking = 2^n
|
||||
|
||||
In other words, the default (n = 7) produces 2, 4, 8, 16, 32, 64, or
|
||||
128 stacked tweaks: */
|
||||
|
||||
#define HAVOC_STACK_POW2 7
|
||||
|
||||
/* Caps on block sizes for cloning and deletion operations. Each of these
|
||||
ranges has a 33% probability of getting picked, except for the first
|
||||
two cycles where smaller blocks are favored: */
|
||||
|
||||
#define HAVOC_BLK_SMALL 32
|
||||
#define HAVOC_BLK_MEDIUM 128
|
||||
#define HAVOC_BLK_LARGE 1500
|
||||
|
||||
/* Extra-large blocks, selected very rarely (<5% of the time): */
|
||||
|
||||
#define HAVOC_BLK_XL 32768
|
||||
|
||||
/* Probabilities of skipping non-favored entries in the queue, expressed as
|
||||
percentages: */
|
||||
|
||||
#define SKIP_TO_NEW_PROB 99 /* ...when there are new, pending favorites */
|
||||
#define SKIP_NFAV_OLD_PROB 95 /* ...no new favs, cur entry already fuzzed */
|
||||
#define SKIP_NFAV_NEW_PROB 75 /* ...no new favs, cur entry not fuzzed yet */
|
||||
|
||||
/* Splicing cycle count: */
|
||||
|
||||
#define SPLICE_CYCLES 15
|
||||
|
||||
/* Nominal per-splice havoc cycle length: */
|
||||
|
||||
#define SPLICE_HAVOC 32
|
||||
|
||||
/* Maximum offset for integer addition / subtraction stages: */
|
||||
|
||||
#define ARITH_MAX 35
|
||||
|
||||
/* Limits for the test case trimmer. The absolute minimum chunk size; and
|
||||
the starting and ending divisors for chopping up the input file: */
|
||||
|
||||
#define TRIM_MIN_BYTES 4
|
||||
#define TRIM_START_STEPS 16
|
||||
#define TRIM_END_STEPS 1024
|
||||
|
||||
/* Maximum size of input file, in bytes (keep under 100MB): */
|
||||
|
||||
#define MAX_FILE (1 * 1024 * 1024)
|
||||
|
||||
/* The same, for the test case minimizer: */
|
||||
|
||||
#define TMIN_MAX_FILE (10 * 1024 * 1024)
|
||||
|
||||
/* Block normalization steps for afl-tmin: */
|
||||
|
||||
#define TMIN_SET_MIN_SIZE 4
|
||||
#define TMIN_SET_STEPS 128
|
||||
|
||||
/* Maximum dictionary token size (-x), in bytes: */
|
||||
|
||||
#define MAX_DICT_FILE 128
|
||||
|
||||
/* Length limits for auto-detected dictionary tokens: */
|
||||
|
||||
#define MIN_AUTO_EXTRA 3
|
||||
#define MAX_AUTO_EXTRA 32
|
||||
|
||||
/* Maximum number of user-specified dictionary tokens to use in deterministic
|
||||
steps; past this point, the "extras/user" step will be still carried out,
|
||||
but with proportionally lower odds: */
|
||||
|
||||
#define MAX_DET_EXTRAS 200
|
||||
|
||||
/* 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
|
||||
higher than the former. */
|
||||
|
||||
#define USE_AUTO_EXTRAS 50
|
||||
#define MAX_AUTO_EXTRAS (USE_AUTO_EXTRAS * 10)
|
||||
|
||||
/* Scaling factor for the effector map used to skip some of the more
|
||||
expensive deterministic steps. The actual divisor is set to
|
||||
2^EFF_MAP_SCALE2 bytes: */
|
||||
|
||||
#define EFF_MAP_SCALE2 3
|
||||
|
||||
/* Minimum input file length at which the effector logic kicks in: */
|
||||
|
||||
#define EFF_MIN_LEN 128
|
||||
|
||||
/* Maximum effector density past which everything is just fuzzed
|
||||
unconditionally (%): */
|
||||
|
||||
#define EFF_MAX_PERC 90
|
||||
|
||||
/* UI refresh frequency (Hz): */
|
||||
|
||||
#define UI_TARGET_HZ 5
|
||||
|
||||
/* Fuzzer stats file and plot update intervals (sec): */
|
||||
|
||||
#define STATS_UPDATE_SEC 60
|
||||
#define PLOT_UPDATE_SEC 5
|
||||
|
||||
/* Smoothing divisor for CPU load and exec speed stats (1 - no smoothing). */
|
||||
|
||||
#define AVG_SMOOTHING 16
|
||||
|
||||
/* Sync interval (every n havoc cycles): */
|
||||
|
||||
#define SYNC_INTERVAL 5
|
||||
|
||||
/* Output directory reuse grace period (minutes): */
|
||||
|
||||
#define OUTPUT_GRACE 25
|
||||
|
||||
/* Uncomment to use simple file names (id_NNNNNN): */
|
||||
|
||||
// #define SIMPLE_FILES
|
||||
|
||||
/* List of interesting values to use in fuzzing. */
|
||||
|
||||
#define INTERESTING_8 \
|
||||
-128, /* Overflow signed 8-bit when decremented */ \
|
||||
-1, /* */ \
|
||||
0, /* */ \
|
||||
1, /* */ \
|
||||
16, /* One-off with common buffer size */ \
|
||||
32, /* One-off with common buffer size */ \
|
||||
64, /* One-off with common buffer size */ \
|
||||
100, /* One-off with common buffer size */ \
|
||||
127 /* Overflow signed 8-bit when incremented */
|
||||
|
||||
#define INTERESTING_16 \
|
||||
-32768, /* Overflow signed 16-bit when decremented */ \
|
||||
-129, /* Overflow signed 8-bit */ \
|
||||
128, /* Overflow signed 8-bit */ \
|
||||
255, /* Overflow unsig 8-bit when incremented */ \
|
||||
256, /* Overflow unsig 8-bit */ \
|
||||
512, /* One-off with common buffer size */ \
|
||||
1000, /* One-off with common buffer size */ \
|
||||
1024, /* One-off with common buffer size */ \
|
||||
4096, /* One-off with common buffer size */ \
|
||||
32767 /* Overflow signed 16-bit when incremented */
|
||||
|
||||
#define INTERESTING_32 \
|
||||
-2147483648LL, /* Overflow signed 32-bit when decremented */ \
|
||||
-100663046, /* Large negative number (endian-agnostic) */ \
|
||||
-32769, /* Overflow signed 16-bit */ \
|
||||
32768, /* Overflow signed 16-bit */ \
|
||||
65535, /* Overflow unsig 16-bit when incremented */ \
|
||||
65536, /* Overflow unsig 16 bit */ \
|
||||
100663045, /* Large positive number (endian-agnostic) */ \
|
||||
2147483647 /* Overflow signed 32-bit when incremented */
|
||||
|
||||
/***********************************************************
|
||||
* *
|
||||
* Really exotic stuff you probably don't want to touch: *
|
||||
* *
|
||||
***********************************************************/
|
||||
|
||||
/* Call count interval between reseeding the libc PRNG from /dev/urandom: */
|
||||
|
||||
#define RESEED_RNG 10000
|
||||
|
||||
/* Maximum line length passed from GCC to 'as' and used for parsing
|
||||
configuration files: */
|
||||
|
||||
#define MAX_LINE 8192
|
||||
|
||||
/* Environment variable used to pass SHM ID to the called program. */
|
||||
|
||||
#define SHM_ENV_VAR "__AFL_SHM_ID"
|
||||
|
||||
/* Other less interesting, internal-only variables. */
|
||||
|
||||
#define CLANG_ENV_VAR "__AFL_CLANG_MODE"
|
||||
#define AS_LOOP_ENV_VAR "__AFL_AS_LOOPCHECK"
|
||||
#define PERSIST_ENV_VAR "__AFL_PERSISTENT"
|
||||
#define DEFER_ENV_VAR "__AFL_DEFER_FORKSRV"
|
||||
|
||||
/* In-code signatures for deferred and persistent mode. */
|
||||
|
||||
#define PERSIST_SIG "##SIG_AFL_PERSISTENT##"
|
||||
#define DEFER_SIG "##SIG_AFL_DEFER_FORKSRV##"
|
||||
|
||||
/* Distinctive bitmap signature used to indicate failed execution: */
|
||||
|
||||
#define EXEC_FAIL_SIG 0xfee1dead
|
||||
|
||||
/* Distinctive exit code used to indicate MSAN trip condition: */
|
||||
|
||||
#define MSAN_ERROR 86
|
||||
|
||||
/* Designated file descriptors for forkserver commands (the application will
|
||||
use FORKSRV_FD and FORKSRV_FD + 1): */
|
||||
|
||||
#define FORKSRV_FD 198
|
||||
|
||||
/* Fork server init timeout multiplier: we'll wait the user-selected
|
||||
timeout plus this much for the fork server to spin up. */
|
||||
|
||||
#define FORK_WAIT_MULT 10
|
||||
|
||||
/* Calibration timeout adjustments, to be a bit more generous when resuming
|
||||
fuzzing sessions or trying to calibrate already-added internal finds.
|
||||
The first value is a percentage, the other is in milliseconds: */
|
||||
|
||||
#define CAL_TMOUT_PERC 125
|
||||
#define CAL_TMOUT_ADD 50
|
||||
|
||||
/* Number of chances to calibrate a case before giving up: */
|
||||
|
||||
#define CAL_CHANCES 3
|
||||
|
||||
/* Map size for the traced binary (2^MAP_SIZE_POW2). Must be greater than
|
||||
2; you probably want to keep it under 18 or so for performance reasons
|
||||
(adjusting AFL_INST_RATIO when compiling is probably a better way to solve
|
||||
problems with complex programs). You need to recompile the target binary
|
||||
after changing this - otherwise, SEGVs may ensue. */
|
||||
|
||||
#define MAP_SIZE_POW2 16
|
||||
#define MAP_SIZE (1 << MAP_SIZE_POW2)
|
||||
|
||||
/* Maximum allocator request size (keep well under INT_MAX): */
|
||||
|
||||
#define MAX_ALLOC 0x40000000
|
||||
|
||||
/* A made-up hashing seed: */
|
||||
|
||||
#define HASH_CONST 0xa5b35705
|
||||
|
||||
/* Constants for afl-gotcpu to control busy loop timing: */
|
||||
|
||||
#define CTEST_TARGET_MS 5000
|
||||
#define CTEST_CORE_TRG_MS 1000
|
||||
#define CTEST_BUSY_CYCLES (10 * 1000 * 1000)
|
||||
|
||||
/* Uncomment this to use inferior block-coverage-based instrumentation. Note
|
||||
that you need to recompile the target binary for this to have any effect: */
|
||||
|
||||
// #define COVERAGE_ONLY
|
||||
|
||||
/* Uncomment this to ignore hit counts and output just one bit per tuple.
|
||||
As with the previous setting, you will need to recompile the target
|
||||
binary: */
|
||||
|
||||
// #define SKIP_COUNTS
|
||||
|
||||
/* Uncomment this to use instrumentation data to record newly discovered paths,
|
||||
but do not use them as seeds for fuzzing. This is useful for conveniently
|
||||
measuring coverage that could be attained by a "dumb" fuzzing algorithm: */
|
||||
|
||||
// #define IGNORE_FINDS
|
||||
|
||||
#endif /* ! _HAVE_CONFIG_H */
|
251
debug.h
251
debug.h
@ -1,251 +0,0 @@
|
||||
/*
|
||||
american fuzzy lop - debug / error handling macros
|
||||
--------------------------------------------------
|
||||
|
||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Copyright 2013, 2014, 2015, 2016 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _HAVE_DEBUG_H
|
||||
#define _HAVE_DEBUG_H
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
/*******************
|
||||
* Terminal colors *
|
||||
*******************/
|
||||
|
||||
#ifdef USE_COLOR
|
||||
|
||||
# define cBLK "\x1b[0;30m"
|
||||
# define cRED "\x1b[0;31m"
|
||||
# define cGRN "\x1b[0;32m"
|
||||
# define cBRN "\x1b[0;33m"
|
||||
# define cBLU "\x1b[0;34m"
|
||||
# define cMGN "\x1b[0;35m"
|
||||
# define cCYA "\x1b[0;36m"
|
||||
# define cLGR "\x1b[0;37m"
|
||||
# define cGRA "\x1b[1;90m"
|
||||
# define cLRD "\x1b[1;91m"
|
||||
# define cLGN "\x1b[1;92m"
|
||||
# define cYEL "\x1b[1;93m"
|
||||
# define cLBL "\x1b[1;94m"
|
||||
# define cPIN "\x1b[1;95m"
|
||||
# define cLCY "\x1b[1;96m"
|
||||
# define cBRI "\x1b[1;97m"
|
||||
# define cRST "\x1b[0m"
|
||||
|
||||
# define bgBLK "\x1b[40m"
|
||||
# define bgRED "\x1b[41m"
|
||||
# define bgGRN "\x1b[42m"
|
||||
# define bgBRN "\x1b[43m"
|
||||
# define bgBLU "\x1b[44m"
|
||||
# define bgMGN "\x1b[45m"
|
||||
# define bgCYA "\x1b[46m"
|
||||
# define bgLGR "\x1b[47m"
|
||||
# define bgGRA "\x1b[100m"
|
||||
# define bgLRD "\x1b[101m"
|
||||
# define bgLGN "\x1b[102m"
|
||||
# define bgYEL "\x1b[103m"
|
||||
# define bgLBL "\x1b[104m"
|
||||
# define bgPIN "\x1b[105m"
|
||||
# define bgLCY "\x1b[106m"
|
||||
# define bgBRI "\x1b[107m"
|
||||
|
||||
#else
|
||||
|
||||
# define cBLK ""
|
||||
# define cRED ""
|
||||
# define cGRN ""
|
||||
# define cBRN ""
|
||||
# define cBLU ""
|
||||
# define cMGN ""
|
||||
# define cCYA ""
|
||||
# define cLGR ""
|
||||
# define cGRA ""
|
||||
# define cLRD ""
|
||||
# define cLGN ""
|
||||
# define cYEL ""
|
||||
# define cLBL ""
|
||||
# define cPIN ""
|
||||
# define cLCY ""
|
||||
# define cBRI ""
|
||||
# define cRST ""
|
||||
|
||||
# define bgBLK ""
|
||||
# define bgRED ""
|
||||
# define bgGRN ""
|
||||
# define bgBRN ""
|
||||
# define bgBLU ""
|
||||
# define bgMGN ""
|
||||
# define bgCYA ""
|
||||
# define bgLGR ""
|
||||
# define bgGRA ""
|
||||
# define bgLRD ""
|
||||
# define bgLGN ""
|
||||
# define bgYEL ""
|
||||
# define bgLBL ""
|
||||
# define bgPIN ""
|
||||
# define bgLCY ""
|
||||
# define bgBRI ""
|
||||
|
||||
#endif /* ^USE_COLOR */
|
||||
|
||||
/*************************
|
||||
* Box drawing sequences *
|
||||
*************************/
|
||||
|
||||
#ifdef FANCY_BOXES
|
||||
|
||||
# define SET_G1 "\x1b)0" /* Set G1 for box drawing */
|
||||
# define RESET_G1 "\x1b)B" /* Reset G1 to ASCII */
|
||||
# define bSTART "\x0e" /* Enter G1 drawing mode */
|
||||
# define bSTOP "\x0f" /* Leave G1 drawing mode */
|
||||
# define bH "q" /* Horizontal line */
|
||||
# define bV "x" /* Vertical line */
|
||||
# define bLT "l" /* Left top corner */
|
||||
# define bRT "k" /* Right top corner */
|
||||
# define bLB "m" /* Left bottom corner */
|
||||
# define bRB "j" /* Right bottom corner */
|
||||
# define bX "n" /* Cross */
|
||||
# define bVR "t" /* Vertical, branch right */
|
||||
# define bVL "u" /* Vertical, branch left */
|
||||
# define bHT "v" /* Horizontal, branch top */
|
||||
# define bHB "w" /* Horizontal, branch bottom */
|
||||
|
||||
#else
|
||||
|
||||
# define SET_G1 ""
|
||||
# define RESET_G1 ""
|
||||
# define bSTART ""
|
||||
# define bSTOP ""
|
||||
# define bH "-"
|
||||
# define bV "|"
|
||||
# define bLT "+"
|
||||
# define bRT "+"
|
||||
# define bLB "+"
|
||||
# define bRB "+"
|
||||
# define bX "+"
|
||||
# define bVR "+"
|
||||
# define bVL "+"
|
||||
# define bHT "+"
|
||||
# define bHB "+"
|
||||
|
||||
#endif /* ^FANCY_BOXES */
|
||||
|
||||
/***********************
|
||||
* Misc terminal codes *
|
||||
***********************/
|
||||
|
||||
#define TERM_HOME "\x1b[H"
|
||||
#define TERM_CLEAR TERM_HOME "\x1b[2J"
|
||||
#define cEOL "\x1b[0K"
|
||||
#define CURSOR_HIDE "\x1b[?25l"
|
||||
#define CURSOR_SHOW "\x1b[?25h"
|
||||
|
||||
/************************
|
||||
* Debug & error macros *
|
||||
************************/
|
||||
|
||||
/* Just print stuff to the appropriate stream. */
|
||||
|
||||
#ifdef MESSAGES_TO_STDOUT
|
||||
# define SAYF(x...) printf(x)
|
||||
#else
|
||||
# define SAYF(x...) fprintf(stderr, x)
|
||||
#endif /* ^MESSAGES_TO_STDOUT */
|
||||
|
||||
/* Show a prefixed warning. */
|
||||
|
||||
#define WARNF(x...) do { \
|
||||
SAYF(cYEL "[!] " cBRI "WARNING: " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
} while (0)
|
||||
|
||||
/* Show a prefixed "doing something" message. */
|
||||
|
||||
#define ACTF(x...) do { \
|
||||
SAYF(cLBL "[*] " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
} while (0)
|
||||
|
||||
/* Show a prefixed "success" message. */
|
||||
|
||||
#define OKF(x...) do { \
|
||||
SAYF(cLGN "[+] " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
} while (0)
|
||||
|
||||
/* Show a prefixed fatal error message (not used in afl). */
|
||||
|
||||
#define BADF(x...) do { \
|
||||
SAYF(cLRD "\n[-] " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
} while (0)
|
||||
|
||||
/* Die with a verbose non-OS fatal error message. */
|
||||
|
||||
#define FATAL(x...) do { \
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \
|
||||
cBRI x); \
|
||||
SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", \
|
||||
__FUNCTION__, __FILE__, __LINE__); \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
|
||||
/* Die by calling abort() to provide a core dump. */
|
||||
|
||||
#define ABORT(x...) do { \
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \
|
||||
cBRI x); \
|
||||
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", \
|
||||
__FUNCTION__, __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
|
||||
/* Die while also including the output of perror(). */
|
||||
|
||||
#define PFATAL(x...) do { \
|
||||
fflush(stdout); \
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] SYSTEM ERROR : " \
|
||||
cBRI x); \
|
||||
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", \
|
||||
__FUNCTION__, __FILE__, __LINE__); \
|
||||
SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
|
||||
/* Die with FAULT() or PFAULT() depending on the value of res (used to
|
||||
interpret different failure modes for read(), write(), etc). */
|
||||
|
||||
#define RPFATAL(res, x...) do { \
|
||||
if (res < 0) PFATAL(x); else FATAL(x); \
|
||||
} while (0)
|
||||
|
||||
/* Error-checking versions of read() and write() that call RPFATAL() as
|
||||
appropriate. */
|
||||
|
||||
#define ck_write(fd, buf, len, fn) do { \
|
||||
u32 _len = (len); \
|
||||
s32 _res = write(fd, buf, _len); \
|
||||
if (_res != _len) RPFATAL(_res, "Short write to %s", fn); \
|
||||
} while (0)
|
||||
|
||||
#define ck_read(fd, buf, len, fn) do { \
|
||||
u32 _len = (len); \
|
||||
s32 _res = read(fd, buf, _len); \
|
||||
if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \
|
||||
} while (0)
|
||||
|
||||
#endif /* ! _HAVE_DEBUG_H */
|
@ -17,14 +17,30 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
Version ++2.53d (dev):
|
||||
----------------------
|
||||
|
||||
- big code refactoring:
|
||||
* all includes are now in include/
|
||||
* all afl sources are now in src/ - see src/README.src
|
||||
* afl-fuzz was splitted up in various individual files for including
|
||||
functionality in other programs (e.g. forkserver, memory map, etc.)
|
||||
for better readability.
|
||||
* new code indention everywhere
|
||||
- auto-generating man pages for all (main) tools
|
||||
- added AFL_FORCE_UI to show the UI even if the terminal is not detected
|
||||
- llvm 9 is now supported (still needs testing)
|
||||
- Android is now supported (thank to JoeyJiao!) - still need to modify the Makefile though
|
||||
- fix building qemu on some Ubuntus (thanks to floyd!)
|
||||
- custom mutator by a loaded library is now supported (thanks to kyakdan!)
|
||||
- added PR that includes peak_rss_mb and slowest_exec_ms in the fuzzer_stats report
|
||||
- more support for *BSD (thanks to devnexen!)
|
||||
- fix building on *BSD (thanks to tobias.kortkamp for the patch)
|
||||
- fix for a few features to support different map sized than 2^16
|
||||
- afl-showmap: new option -r now shows the real values in the buckets (stock
|
||||
afl never did), plus shows tuple content summary information now
|
||||
- fix building on *BSD (thanks to tobias.kortkamp for the patch)
|
||||
- small docu updates
|
||||
- ... your patch? :)
|
||||
|
||||
- NeverZero counters for QEMU
|
||||
- NeverZero counters for Unicorn
|
||||
- CompareCoverage Unicorn
|
||||
- immediates-only instrumentation for CompareCoverage
|
||||
|
||||
|
||||
--------------------------
|
||||
|
@ -223,6 +223,9 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
some basic stats. This behavior is also automatically triggered when the
|
||||
output from afl-fuzz is redirected to a file or to a pipe.
|
||||
|
||||
- Setting AFL_FORCE_UI will force painting the UI on the screen even if
|
||||
no valid terminal was detected (for virtual consoles)
|
||||
|
||||
- If you are Jakub, you may need AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES.
|
||||
Others need not apply.
|
||||
|
||||
@ -245,9 +248,19 @@ The QEMU wrapper used to instrument binary-only code supports several settings:
|
||||
- Setting AFL_INST_LIBS causes the translator to also instrument the code
|
||||
inside any dynamically linked libraries (notably including glibc).
|
||||
|
||||
- Setting AFL_COMPCOV_LEVEL enables the CompareCoverage tracing of all cmp
|
||||
and sub in x86 and x86_64 and memory comparions functions (e.g. strcmp,
|
||||
memcmp, ...) when libcompcov is preloaded using AFL_PRELOAD.
|
||||
More info at qemu_mode/libcompcov/README.compcov.
|
||||
There are two levels at the moment, AFL_COMPCOV_LEVEL=1 that instruments
|
||||
only comparisons with immediate values / read-only memory and
|
||||
AFL_COMPCOV_LEVEL=2 that instruments all the comparions. Level 2 is more
|
||||
accurate but may need a larger shared memory.
|
||||
|
||||
- Setting AFL_QEMU_COMPCOV enables the CompareCoverage tracing of all
|
||||
cmp and sub in x86 and x86_64. Support for other architectures and
|
||||
comparison functions (mem/strcmp et al.) is planned.
|
||||
cmp and sub in x86 and x86_64.
|
||||
This is an alias of AFL_COMPCOV_LEVEL=1 when AFL_COMPCOV_LEVEL is
|
||||
not specified.
|
||||
|
||||
- The underlying QEMU binary will recognize any standard "user space
|
||||
emulation" variables (e.g., QEMU_STACK_SIZE), but there should be no
|
||||
@ -257,9 +270,10 @@ The QEMU wrapper used to instrument binary-only code supports several settings:
|
||||
Use this if you are unsure if the entrypoint might be wrong - but
|
||||
use it directly, e.g. afl-qemu-trace ./program
|
||||
|
||||
- If you want to specify a specific entrypoint into the binary (this can
|
||||
be very good for the performance!), use AFL_ENTRYPOINT for this.
|
||||
- AFL_ENTRYPOINT allows you to specify a specific entrypoint into the
|
||||
binary (this can be very good for the performance!).
|
||||
The entrypoint is specified as hex address, e.g. 0x4004110
|
||||
Note that the address must be the address of a basic block.
|
||||
|
||||
5) Settings for afl-cmin
|
||||
------------------------
|
||||
|
@ -407,6 +407,9 @@ directory. This includes:
|
||||
- variable_paths - number of test cases showing variable behavior
|
||||
- unique_crashes - number of unique crashes recorded
|
||||
- unique_hangs - number of unique hangs encountered
|
||||
- command_line - full command line used for the fuzzing session
|
||||
- slowest_exec_ms- real time of the slowest execution in seconds
|
||||
- peak_rss_mb - max rss usage reached during fuzzing in MB
|
||||
|
||||
Most of these map directly to the UI elements discussed earlier on.
|
||||
|
||||
|
@ -1,109 +0,0 @@
|
||||
=========================================================
|
||||
Unicorn-based binary-only instrumentation for afl-fuzz
|
||||
=========================================================
|
||||
|
||||
1) Introduction
|
||||
---------------
|
||||
|
||||
The code in ./unicorn_mode allows you to build a standalone feature that
|
||||
leverages the Unicorn Engine and allows callers to obtain instrumentation
|
||||
output for black-box, closed-source binary code snippets. This mechanism
|
||||
can be then used by afl-fuzz to stress-test targets that couldn't be built
|
||||
with afl-gcc or used in QEMU mode, or with other extensions such as
|
||||
TriforceAFL.
|
||||
|
||||
There is a significant performance penalty compared to native AFL,
|
||||
but at least we're able to use AFL on these binaries, right?
|
||||
|
||||
The idea and much of the implementation comes from Nathan Voss <njvoss299@gmail.com>.
|
||||
|
||||
2) How to use
|
||||
-------------
|
||||
|
||||
Requirements: you need an installed python2 environment.
|
||||
|
||||
*** Building AFL's Unicorn Mode ***
|
||||
|
||||
First, make afl as usual.
|
||||
Once that completes successfully you need to build and add in the Unicorn Mode
|
||||
features:
|
||||
|
||||
$ cd unicorn_mode
|
||||
$ ./build_unicorn_support.sh
|
||||
|
||||
NOTE: This script downloads a recent Unicorn Engine commit that has been tested
|
||||
and is stable-ish from the Unicorn github page. If you are offline, you'll need
|
||||
to hack up this script a little bit and supply your own copy of Unicorn's latest
|
||||
stable release. It's not very hard, just check out the beginning of the
|
||||
build_unicorn_support.sh script and adjust as necessary.
|
||||
|
||||
Building Unicorn will take a little bit (~5-10 minutes). Once it completes
|
||||
it automatically compiles a sample application and verify that it works.
|
||||
|
||||
*** Fuzzing with Unicorn Mode ***
|
||||
|
||||
To really use unicorn-mode effectively you need to prepare the following:
|
||||
|
||||
* Relevant binary code to be fuzzed
|
||||
* Knowledge of the memory map and good starting state
|
||||
* Folder containing sample inputs to start fuzzing with
|
||||
- Same ideas as any other AFL inputs
|
||||
- Quality/speed of results will depend greatly on quality of starting
|
||||
samples
|
||||
- See AFL's guidance on how to create a sample corpus
|
||||
* Unicorn-based test harness which:
|
||||
- Adds memory map regions
|
||||
- Loads binary code into memory
|
||||
- Emulates at least one instruction*
|
||||
- Yeah, this is lame. See 'Gotchas' section below for more info
|
||||
- Loads and verifies data to fuzz from a command-line specified file
|
||||
- AFL will provide mutated inputs by changing the file passed to
|
||||
the test harness
|
||||
- Presumably the data to be fuzzed is at a fixed buffer address
|
||||
- If input constraints (size, invalid bytes, etc.) are known they
|
||||
should be checked after the file is loaded. If a constraint
|
||||
fails, just exit the test harness. AFL will treat the input as
|
||||
'uninteresting' and move on.
|
||||
- Sets up registers and memory state for beginning of test
|
||||
- Emulates the interested code from beginning to end
|
||||
- If a crash is detected, the test harness must 'crash' by
|
||||
throwing a signal (SIGSEGV, SIGKILL, SIGABORT, etc.)
|
||||
|
||||
Once you have all those things ready to go you just need to run afl-fuzz in
|
||||
'unicorn-mode' by passing in the '-U' flag:
|
||||
|
||||
$ afl-fuzz -U -m none -i /path/to/inputs -o /path/to/results -- ./test_harness @@
|
||||
|
||||
The normal afl-fuzz command line format applies to everything here. Refer to
|
||||
AFL's main documentation for more info about how to use afl-fuzz effectively.
|
||||
|
||||
For a much clearer vision of what all of this looks like, please refer to the
|
||||
sample provided in the 'unicorn_mode/samples' directory. There is also a blog
|
||||
post that goes over the basics at:
|
||||
|
||||
https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf
|
||||
|
||||
The 'helper_scripts' directory also contains several helper scripts that allow you
|
||||
to dump context from a running process, load it, and hook heap allocations. For details
|
||||
on how to use this check out the follow-up blog post to the one linked above.
|
||||
|
||||
A example use of AFL-Unicorn mode is discussed in the Paper Unicorefuzz:
|
||||
https://www.usenix.org/conference/woot19/presentation/maier
|
||||
|
||||
3) Gotchas, feedback, bugs
|
||||
--------------------------
|
||||
|
||||
To make sure that AFL's fork server starts up correctly the Unicorn test
|
||||
harness script must emulate at least one instruction before loading the
|
||||
data that will be fuzzed from the input file. It doesn't matter what the
|
||||
instruction is, nor if it is valid. This is an artifact of how the fork-server
|
||||
is started and could likely be fixed with some clever re-arranging of the
|
||||
patches applied to Unicorn.
|
||||
|
||||
Running the build script builds Unicorn and its python bindings and installs
|
||||
them on your system. This installation will supersede any existing Unicorn
|
||||
installation with the patched afl-unicorn version.
|
||||
|
||||
Refer to the unicorn_mode/samples/arm_example/arm_tester.c for an example
|
||||
of how to do this properly! If you don't get this right, AFL will not
|
||||
load any mutated inputs and your fuzzing will be useless!
|
@ -1,12 +1,15 @@
|
||||
/*
|
||||
american fuzzy lop - injectable parts
|
||||
-------------------------------------
|
||||
american fuzzy lop++ - injectable parts
|
||||
---------------------------------------
|
||||
|
||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2013, 2014, 2015 Google Inc. All rights reserved.
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -37,7 +40,7 @@
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
|
||||
/*
|
||||
/*
|
||||
------------------
|
||||
Performances notes
|
||||
------------------
|
||||
@ -106,47 +109,47 @@
|
||||
|
||||
static const u8* trampoline_fmt_32 =
|
||||
|
||||
"\n"
|
||||
"/* --- AFL TRAMPOLINE (32-BIT) --- */\n"
|
||||
"\n"
|
||||
".align 4\n"
|
||||
"\n"
|
||||
"leal -16(%%esp), %%esp\n"
|
||||
"movl %%edi, 0(%%esp)\n"
|
||||
"movl %%edx, 4(%%esp)\n"
|
||||
"movl %%ecx, 8(%%esp)\n"
|
||||
"movl %%eax, 12(%%esp)\n"
|
||||
"movl $0x%08x, %%ecx\n"
|
||||
"call __afl_maybe_log\n"
|
||||
"movl 12(%%esp), %%eax\n"
|
||||
"movl 8(%%esp), %%ecx\n"
|
||||
"movl 4(%%esp), %%edx\n"
|
||||
"movl 0(%%esp), %%edi\n"
|
||||
"leal 16(%%esp), %%esp\n"
|
||||
"\n"
|
||||
"/* --- END --- */\n"
|
||||
"\n";
|
||||
"\n"
|
||||
"/* --- AFL TRAMPOLINE (32-BIT) --- */\n"
|
||||
"\n"
|
||||
".align 4\n"
|
||||
"\n"
|
||||
"leal -16(%%esp), %%esp\n"
|
||||
"movl %%edi, 0(%%esp)\n"
|
||||
"movl %%edx, 4(%%esp)\n"
|
||||
"movl %%ecx, 8(%%esp)\n"
|
||||
"movl %%eax, 12(%%esp)\n"
|
||||
"movl $0x%08x, %%ecx\n"
|
||||
"call __afl_maybe_log\n"
|
||||
"movl 12(%%esp), %%eax\n"
|
||||
"movl 8(%%esp), %%ecx\n"
|
||||
"movl 4(%%esp), %%edx\n"
|
||||
"movl 0(%%esp), %%edi\n"
|
||||
"leal 16(%%esp), %%esp\n"
|
||||
"\n"
|
||||
"/* --- END --- */\n"
|
||||
"\n";
|
||||
|
||||
static const u8* trampoline_fmt_64 =
|
||||
|
||||
"\n"
|
||||
"/* --- AFL TRAMPOLINE (64-BIT) --- */\n"
|
||||
"\n"
|
||||
".align 4\n"
|
||||
"\n"
|
||||
"leaq -(128+24)(%%rsp), %%rsp\n"
|
||||
"movq %%rdx, 0(%%rsp)\n"
|
||||
"movq %%rcx, 8(%%rsp)\n"
|
||||
"movq %%rax, 16(%%rsp)\n"
|
||||
"movq $0x%08x, %%rcx\n"
|
||||
"call __afl_maybe_log\n"
|
||||
"movq 16(%%rsp), %%rax\n"
|
||||
"movq 8(%%rsp), %%rcx\n"
|
||||
"movq 0(%%rsp), %%rdx\n"
|
||||
"leaq (128+24)(%%rsp), %%rsp\n"
|
||||
"\n"
|
||||
"/* --- END --- */\n"
|
||||
"\n";
|
||||
"\n"
|
||||
"/* --- AFL TRAMPOLINE (64-BIT) --- */\n"
|
||||
"\n"
|
||||
".align 4\n"
|
||||
"\n"
|
||||
"leaq -(128+24)(%%rsp), %%rsp\n"
|
||||
"movq %%rdx, 0(%%rsp)\n"
|
||||
"movq %%rcx, 8(%%rsp)\n"
|
||||
"movq %%rax, 16(%%rsp)\n"
|
||||
"movq $0x%08x, %%rcx\n"
|
||||
"call __afl_maybe_log\n"
|
||||
"movq 16(%%rsp), %%rax\n"
|
||||
"movq 8(%%rsp), %%rcx\n"
|
||||
"movq 0(%%rsp), %%rdx\n"
|
||||
"leaq (128+24)(%%rsp), %%rsp\n"
|
||||
"\n"
|
||||
"/* --- END --- */\n"
|
||||
"\n";
|
||||
|
||||
static const u8* main_payload_32 =
|
||||
|
||||
@ -183,14 +186,14 @@ static const u8* main_payload_32 =
|
||||
" movl %ecx, __afl_prev_loc\n"
|
||||
#else
|
||||
" movl %ecx, %edi\n"
|
||||
#endif /* ^!COVERAGE_ONLY */
|
||||
#endif /* ^!COVERAGE_ONLY */
|
||||
"\n"
|
||||
#ifdef SKIP_COUNTS
|
||||
" orb $1, (%edx, %edi, 1)\n"
|
||||
#else
|
||||
" incb (%edx, %edi, 1)\n"
|
||||
" adcb $0, (%edx, %edi, 1)\n" // never zero counter implementation. slightly better path discovery and little performance impact
|
||||
#endif /* ^SKIP_COUNTS */
|
||||
#endif /* ^SKIP_COUNTS */
|
||||
"\n"
|
||||
"__afl_return:\n"
|
||||
"\n"
|
||||
@ -379,7 +382,7 @@ static const u8* main_payload_32 =
|
||||
" .comm __afl_setup_failure, 1, 32\n"
|
||||
#ifndef COVERAGE_ONLY
|
||||
" .comm __afl_prev_loc, 4, 32\n"
|
||||
#endif /* !COVERAGE_ONLY */
|
||||
#endif /* !COVERAGE_ONLY */
|
||||
" .comm __afl_fork_pid, 4, 32\n"
|
||||
" .comm __afl_temp, 4, 32\n"
|
||||
"\n"
|
||||
@ -398,10 +401,10 @@ static const u8* main_payload_32 =
|
||||
recognize .string. */
|
||||
|
||||
#ifdef __APPLE__
|
||||
# define CALL_L64(str) "call _" str "\n"
|
||||
#define CALL_L64(str) "call _" str "\n"
|
||||
#else
|
||||
# define CALL_L64(str) "call " str "@PLT\n"
|
||||
#endif /* ^__APPLE__ */
|
||||
#define CALL_L64(str) "call " str "@PLT\n"
|
||||
#endif /* ^__APPLE__ */
|
||||
|
||||
static const u8* main_payload_64 =
|
||||
|
||||
@ -415,11 +418,11 @@ static const u8* main_payload_64 =
|
||||
"\n"
|
||||
"__afl_maybe_log:\n"
|
||||
"\n"
|
||||
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9))
|
||||
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9))
|
||||
" .byte 0x9f /* lahf */\n"
|
||||
#else
|
||||
" lahf\n"
|
||||
#endif /* ^__OpenBSD__, etc */
|
||||
#endif /* ^__OpenBSD__, etc */
|
||||
" seto %al\n"
|
||||
"\n"
|
||||
" /* Check if SHM region is already mapped. */\n"
|
||||
@ -436,23 +439,23 @@ static const u8* main_payload_64 =
|
||||
" xorq __afl_prev_loc(%rip), %rcx\n"
|
||||
" xorq %rcx, __afl_prev_loc(%rip)\n"
|
||||
" shrq $1, __afl_prev_loc(%rip)\n"
|
||||
#endif /* ^!COVERAGE_ONLY */
|
||||
#endif /* ^!COVERAGE_ONLY */
|
||||
"\n"
|
||||
#ifdef SKIP_COUNTS
|
||||
" orb $1, (%rdx, %rcx, 1)\n"
|
||||
#else
|
||||
" incb (%rdx, %rcx, 1)\n"
|
||||
" adcb $0, (%rdx, %rcx, 1)\n" // never zero counter implementation. slightly better path discovery and little performance impact
|
||||
#endif /* ^SKIP_COUNTS */
|
||||
#endif /* ^SKIP_COUNTS */
|
||||
"\n"
|
||||
"__afl_return:\n"
|
||||
"\n"
|
||||
" addb $127, %al\n"
|
||||
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9))
|
||||
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9))
|
||||
" .byte 0x9e /* sahf */\n"
|
||||
#else
|
||||
" sahf\n"
|
||||
#endif /* ^__OpenBSD__, etc */
|
||||
#endif /* ^__OpenBSD__, etc */
|
||||
" ret\n"
|
||||
"\n"
|
||||
".align 8\n"
|
||||
@ -471,7 +474,7 @@ static const u8* main_payload_64 =
|
||||
" movq (%rdx), %rdx\n"
|
||||
#else
|
||||
" movq __afl_global_area_ptr(%rip), %rdx\n"
|
||||
#endif /* !^__APPLE__ */
|
||||
#endif /* !^__APPLE__ */
|
||||
" testq %rdx, %rdx\n"
|
||||
" je __afl_setup_first\n"
|
||||
"\n"
|
||||
@ -570,7 +573,7 @@ static const u8* main_payload_64 =
|
||||
#else
|
||||
" movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n"
|
||||
" movq %rax, (%rdx)\n"
|
||||
#endif /* ^__APPLE__ */
|
||||
#endif /* ^__APPLE__ */
|
||||
" movq %rax, %rdx\n"
|
||||
"\n"
|
||||
"__afl_forkserver:\n"
|
||||
@ -739,7 +742,7 @@ static const u8* main_payload_64 =
|
||||
" .comm __afl_area_ptr, 8\n"
|
||||
#ifndef COVERAGE_ONLY
|
||||
" .comm __afl_prev_loc, 8\n"
|
||||
#endif /* !COVERAGE_ONLY */
|
||||
#endif /* !COVERAGE_ONLY */
|
||||
" .comm __afl_fork_pid, 4\n"
|
||||
" .comm __afl_temp, 4\n"
|
||||
" .comm __afl_setup_failure, 1\n"
|
||||
@ -749,12 +752,12 @@ static const u8* main_payload_64 =
|
||||
" .lcomm __afl_area_ptr, 8\n"
|
||||
#ifndef COVERAGE_ONLY
|
||||
" .lcomm __afl_prev_loc, 8\n"
|
||||
#endif /* !COVERAGE_ONLY */
|
||||
#endif /* !COVERAGE_ONLY */
|
||||
" .lcomm __afl_fork_pid, 4\n"
|
||||
" .lcomm __afl_temp, 4\n"
|
||||
" .lcomm __afl_setup_failure, 1\n"
|
||||
|
||||
#endif /* ^__APPLE__ */
|
||||
#endif /* ^__APPLE__ */
|
||||
|
||||
" .comm __afl_global_area_ptr, 8, 8\n"
|
||||
"\n"
|
||||
@ -764,4 +767,5 @@ static const u8* main_payload_64 =
|
||||
"/* --- END --- */\n"
|
||||
"\n";
|
||||
|
||||
#endif /* !_HAVE_AFL_AS_H */
|
||||
#endif /* !_HAVE_AFL_AS_H */
|
||||
|
642
include/afl-fuzz.h
Normal file
642
include/afl-fuzz.h
Normal file
@ -0,0 +1,642 @@
|
||||
/*
|
||||
american fuzzy lop++ - fuzzer header
|
||||
------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This is the real deal: the program takes an instrumented binary and
|
||||
attempts a variety of basic fuzzing tricks, paying close attention to
|
||||
how they affect the execution path.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _AFL_FUZZ_H
|
||||
#define _AFL_FUZZ_H
|
||||
|
||||
#define AFL_MAIN
|
||||
#define MESSAGES_TO_STDOUT
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "alloc-inl.h"
|
||||
#include "hash.h"
|
||||
#include "sharedmem.h"
|
||||
#include "forkserver.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#define HAVE_ARC4RANDOM 1
|
||||
#endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */
|
||||
|
||||
/* For systems that have sched_setaffinity; right now just Linux, but one
|
||||
can hope... */
|
||||
|
||||
#ifdef __linux__
|
||||
#define HAVE_AFFINITY 1
|
||||
#endif /* __linux__ */
|
||||
|
||||
#ifndef SIMPLE_FILES
|
||||
#define CASE_PREFIX "id:"
|
||||
#else
|
||||
#define CASE_PREFIX "id_"
|
||||
#endif /* ^!SIMPLE_FILES */
|
||||
|
||||
struct queue_entry {
|
||||
|
||||
u8* fname; /* File name for the test case */
|
||||
u32 len; /* Input length */
|
||||
|
||||
u8 cal_failed, /* Calibration failed? */
|
||||
trim_done, /* Trimmed? */
|
||||
was_fuzzed, /* historical, but needed for MOpt */
|
||||
passed_det, /* Deterministic stages passed? */
|
||||
has_new_cov, /* Triggers new coverage? */
|
||||
var_behavior, /* Variable behavior? */
|
||||
favored, /* Currently favored? */
|
||||
fs_redundant; /* Marked as redundant in the fs? */
|
||||
|
||||
u32 bitmap_size, /* Number of bits set in bitmap */
|
||||
fuzz_level, /* Number of fuzzing iterations */
|
||||
exec_cksum; /* Checksum of the execution trace */
|
||||
|
||||
u64 exec_us, /* Execution time (us) */
|
||||
handicap, /* Number of queue cycles behind */
|
||||
n_fuzz, /* Number of fuzz, does not overflow */
|
||||
depth; /* Path depth */
|
||||
|
||||
u8* trace_mini; /* Trace bytes, if kept */
|
||||
u32 tc_ref; /* Trace bytes ref count */
|
||||
|
||||
struct queue_entry *next, /* Next element, if any */
|
||||
*next_100; /* 100 elements ahead */
|
||||
|
||||
};
|
||||
|
||||
struct extra_data {
|
||||
|
||||
u8* data; /* Dictionary token data */
|
||||
u32 len; /* Dictionary token length */
|
||||
u32 hit_cnt; /* Use count in the corpus */
|
||||
|
||||
};
|
||||
|
||||
/* Fuzzing stages */
|
||||
|
||||
enum {
|
||||
|
||||
/* 00 */ STAGE_FLIP1,
|
||||
/* 01 */ STAGE_FLIP2,
|
||||
/* 02 */ STAGE_FLIP4,
|
||||
/* 03 */ STAGE_FLIP8,
|
||||
/* 04 */ STAGE_FLIP16,
|
||||
/* 05 */ STAGE_FLIP32,
|
||||
/* 06 */ STAGE_ARITH8,
|
||||
/* 07 */ STAGE_ARITH16,
|
||||
/* 08 */ STAGE_ARITH32,
|
||||
/* 09 */ STAGE_INTEREST8,
|
||||
/* 10 */ STAGE_INTEREST16,
|
||||
/* 11 */ STAGE_INTEREST32,
|
||||
/* 12 */ STAGE_EXTRAS_UO,
|
||||
/* 13 */ STAGE_EXTRAS_UI,
|
||||
/* 14 */ STAGE_EXTRAS_AO,
|
||||
/* 15 */ STAGE_HAVOC,
|
||||
/* 16 */ STAGE_SPLICE,
|
||||
/* 17 */ STAGE_PYTHON,
|
||||
/* 18 */ STAGE_CUSTOM_MUTATOR
|
||||
|
||||
};
|
||||
|
||||
/* Stage value types */
|
||||
|
||||
enum {
|
||||
|
||||
/* 00 */ STAGE_VAL_NONE,
|
||||
/* 01 */ STAGE_VAL_LE,
|
||||
/* 02 */ STAGE_VAL_BE
|
||||
|
||||
};
|
||||
|
||||
/* Execution status fault codes */
|
||||
|
||||
enum {
|
||||
|
||||
/* 00 */ FAULT_NONE,
|
||||
/* 01 */ FAULT_TMOUT,
|
||||
/* 02 */ FAULT_CRASH,
|
||||
/* 03 */ FAULT_ERROR,
|
||||
/* 04 */ FAULT_NOINST,
|
||||
/* 05 */ FAULT_NOBITS
|
||||
|
||||
};
|
||||
|
||||
/* MOpt:
|
||||
Lots of globals, but mostly for the status UI and other things where it
|
||||
really makes no sense to haul them around as function parameters. */
|
||||
extern u64 limit_time_puppet, orig_hit_cnt_puppet, last_limit_time_start,
|
||||
tmp_pilot_time, total_pacemaker_time, total_puppet_find, temp_puppet_find,
|
||||
most_time_key, most_time, most_execs_key, most_execs, old_hit_count;
|
||||
|
||||
extern s32 SPLICE_CYCLES_puppet, limit_time_sig, key_puppet, key_module;
|
||||
|
||||
extern double w_init, w_end, w_now;
|
||||
|
||||
extern s32 g_now;
|
||||
extern s32 g_max;
|
||||
|
||||
#define operator_num 16
|
||||
#define swarm_num 5
|
||||
#define period_core 500000
|
||||
|
||||
extern u64 tmp_core_time;
|
||||
extern s32 swarm_now;
|
||||
|
||||
extern double x_now[swarm_num][operator_num], L_best[swarm_num][operator_num],
|
||||
eff_best[swarm_num][operator_num], G_best[operator_num],
|
||||
v_now[swarm_num][operator_num], probability_now[swarm_num][operator_num],
|
||||
swarm_fitness[swarm_num];
|
||||
|
||||
extern u64 stage_finds_puppet[swarm_num][operator_num], /* Patterns found per
|
||||
fuzz stage */
|
||||
stage_finds_puppet_v2[swarm_num][operator_num],
|
||||
stage_cycles_puppet_v2[swarm_num][operator_num],
|
||||
stage_cycles_puppet_v3[swarm_num][operator_num],
|
||||
stage_cycles_puppet[swarm_num][operator_num],
|
||||
operator_finds_puppet[operator_num],
|
||||
core_operator_finds_puppet[operator_num],
|
||||
core_operator_finds_puppet_v2[operator_num],
|
||||
core_operator_cycles_puppet[operator_num],
|
||||
core_operator_cycles_puppet_v2[operator_num],
|
||||
core_operator_cycles_puppet_v3[operator_num]; /* Execs per fuzz stage */
|
||||
|
||||
#define RAND_C (rand() % 1000 * 0.001)
|
||||
#define v_max 1
|
||||
#define v_min 0.05
|
||||
#define limit_time_bound 1.1
|
||||
#define SPLICE_CYCLES_puppet_up 25
|
||||
#define SPLICE_CYCLES_puppet_low 5
|
||||
#define STAGE_RANDOMBYTE 12
|
||||
#define STAGE_DELETEBYTE 13
|
||||
#define STAGE_Clone75 14
|
||||
#define STAGE_OverWrite75 15
|
||||
#define period_pilot 50000
|
||||
|
||||
extern double period_pilot_tmp;
|
||||
extern s32 key_lv;
|
||||
|
||||
extern u8 *in_dir, /* Input directory with test cases */
|
||||
*out_dir, /* Working & output directory */
|
||||
*tmp_dir, /* Temporary directory for input */
|
||||
*sync_dir, /* Synchronization directory */
|
||||
*sync_id, /* Fuzzer ID */
|
||||
*power_name, /* Power schedule name */
|
||||
*use_banner, /* Display banner */
|
||||
*in_bitmap, /* Input bitmap */
|
||||
*file_extension, /* File extension */
|
||||
*orig_cmdline, /* Original command line */
|
||||
*doc_path, /* Path to documentation dir */
|
||||
*target_path, /* Path to target binary */
|
||||
*out_file; /* File to fuzz, if any */
|
||||
|
||||
extern u32 exec_tmout; /* Configurable exec timeout (ms) */
|
||||
extern u32 hang_tmout; /* Timeout used for hang det (ms) */
|
||||
|
||||
extern u64 mem_limit; /* Memory cap for child (MB) */
|
||||
|
||||
extern u8 cal_cycles, /* Calibration cycles defaults */
|
||||
cal_cycles_long, debug, /* Debug mode */
|
||||
python_only; /* Python-only mode */
|
||||
|
||||
extern u32 stats_update_freq; /* Stats update frequency (execs) */
|
||||
|
||||
enum {
|
||||
|
||||
/* 00 */ EXPLORE, /* AFL default, Exploration-based constant schedule */
|
||||
/* 01 */ FAST, /* Exponential schedule */
|
||||
/* 02 */ COE, /* Cut-Off Exponential schedule */
|
||||
/* 03 */ LIN, /* Linear schedule */
|
||||
/* 04 */ QUAD, /* Quadratic schedule */
|
||||
/* 05 */ EXPLOIT, /* AFL's exploitation-based const. */
|
||||
|
||||
POWER_SCHEDULES_NUM
|
||||
|
||||
};
|
||||
|
||||
extern char* power_names[POWER_SCHEDULES_NUM];
|
||||
|
||||
extern u8 schedule; /* Power schedule (default: EXPLORE)*/
|
||||
extern u8 havoc_max_mult;
|
||||
|
||||
extern u8 skip_deterministic, /* Skip deterministic stages? */
|
||||
force_deterministic, /* Force deterministic stages? */
|
||||
use_splicing, /* Recombine input files? */
|
||||
dumb_mode, /* Run in non-instrumented mode? */
|
||||
score_changed, /* Scoring for favorites changed? */
|
||||
kill_signal, /* Signal that killed the child */
|
||||
resuming_fuzz, /* Resuming an older fuzzing job? */
|
||||
timeout_given, /* Specific timeout given? */
|
||||
not_on_tty, /* stdout is not a tty */
|
||||
term_too_small, /* terminal dimensions too small */
|
||||
no_forkserver, /* Disable forkserver? */
|
||||
crash_mode, /* Crash mode! Yeah! */
|
||||
in_place_resume, /* Attempt in-place resume? */
|
||||
auto_changed, /* Auto-generated tokens changed? */
|
||||
no_cpu_meter_red, /* Feng shui on the status screen */
|
||||
no_arith, /* Skip most arithmetic ops */
|
||||
shuffle_queue, /* Shuffle input queue? */
|
||||
bitmap_changed, /* Time to update bitmap? */
|
||||
qemu_mode, /* Running in QEMU mode? */
|
||||
unicorn_mode, /* Running in Unicorn mode? */
|
||||
skip_requested, /* Skip request, via SIGUSR1 */
|
||||
run_over10m, /* Run time over 10 minutes? */
|
||||
persistent_mode, /* Running in persistent mode? */
|
||||
deferred_mode, /* Deferred forkserver mode? */
|
||||
fixed_seed, /* do not reseed */
|
||||
fast_cal, /* Try to calibrate faster? */
|
||||
uses_asan; /* Target uses ASAN? */
|
||||
|
||||
extern s32 out_fd, /* Persistent fd for out_file */
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
dev_urandom_fd, /* Persistent fd for /dev/urandom */
|
||||
#endif
|
||||
dev_null_fd, /* Persistent fd for /dev/null */
|
||||
fsrv_ctl_fd, /* Fork server control pipe (write) */
|
||||
fsrv_st_fd; /* Fork server status pipe (read) */
|
||||
|
||||
extern s32 forksrv_pid, /* PID of the fork server */
|
||||
child_pid, /* PID of the fuzzed program */
|
||||
out_dir_fd; /* FD of the lock file */
|
||||
|
||||
extern u8* trace_bits; /* SHM with instrumentation bitmap */
|
||||
|
||||
extern u8 virgin_bits[MAP_SIZE], /* Regions yet untouched by fuzzing */
|
||||
virgin_tmout[MAP_SIZE], /* Bits we haven't seen in tmouts */
|
||||
virgin_crash[MAP_SIZE]; /* Bits we haven't seen in crashes */
|
||||
|
||||
extern u8 var_bytes[MAP_SIZE]; /* Bytes that appear to be variable */
|
||||
|
||||
extern volatile u8 stop_soon, /* Ctrl-C pressed? */
|
||||
clear_screen, /* Window resized? */
|
||||
child_timed_out; /* Traced process timed out? */
|
||||
|
||||
extern u32 queued_paths, /* Total number of queued testcases */
|
||||
queued_variable, /* Testcases with variable behavior */
|
||||
queued_at_start, /* Total number of initial inputs */
|
||||
queued_discovered, /* Items discovered during this run */
|
||||
queued_imported, /* Items imported via -S */
|
||||
queued_favored, /* Paths deemed favorable */
|
||||
queued_with_cov, /* Paths with new coverage bytes */
|
||||
pending_not_fuzzed, /* Queued but not done yet */
|
||||
pending_favored, /* Pending favored paths */
|
||||
cur_skipped_paths, /* Abandoned inputs in cur cycle */
|
||||
cur_depth, /* Current path depth */
|
||||
max_depth, /* Max path depth */
|
||||
useless_at_start, /* Number of useless starting paths */
|
||||
var_byte_count, /* Bitmap bytes with var behavior */
|
||||
current_entry, /* Current queue entry ID */
|
||||
havoc_div; /* Cycle count divisor for havoc */
|
||||
|
||||
extern u64 total_crashes, /* Total number of crashes */
|
||||
unique_crashes, /* Crashes with unique signatures */
|
||||
total_tmouts, /* Total number of timeouts */
|
||||
unique_tmouts, /* Timeouts with unique signatures */
|
||||
unique_hangs, /* Hangs with unique signatures */
|
||||
total_execs, /* Total execve() calls */
|
||||
slowest_exec_ms, /* Slowest testcase non hang in ms */
|
||||
start_time, /* Unix start time (ms) */
|
||||
last_path_time, /* Time for most recent path (ms) */
|
||||
last_crash_time, /* Time for most recent crash (ms) */
|
||||
last_hang_time, /* Time for most recent hang (ms) */
|
||||
last_crash_execs, /* Exec counter at last crash */
|
||||
queue_cycle, /* Queue round counter */
|
||||
cycles_wo_finds, /* Cycles without any new paths */
|
||||
trim_execs, /* Execs done to trim input files */
|
||||
bytes_trim_in, /* Bytes coming into the trimmer */
|
||||
bytes_trim_out, /* Bytes coming outa the trimmer */
|
||||
blocks_eff_total, /* Blocks subject to effector maps */
|
||||
blocks_eff_select; /* Blocks selected as fuzzable */
|
||||
|
||||
extern u32 subseq_tmouts; /* Number of timeouts in a row */
|
||||
|
||||
extern u8 *stage_name, /* Name of the current fuzz stage */
|
||||
*stage_short, /* Short stage name */
|
||||
*syncing_party; /* Currently syncing with... */
|
||||
|
||||
extern s32 stage_cur, stage_max; /* Stage progression */
|
||||
extern s32 splicing_with; /* Splicing with which test case? */
|
||||
|
||||
extern u32 master_id, master_max; /* Master instance job splitting */
|
||||
|
||||
extern u32 syncing_case; /* Syncing with case #... */
|
||||
|
||||
extern s32 stage_cur_byte, /* Byte offset of current stage op */
|
||||
stage_cur_val; /* Value used for stage op */
|
||||
|
||||
extern u8 stage_val_type; /* Value type (STAGE_VAL_*) */
|
||||
|
||||
extern u64 stage_finds[32], /* Patterns found per fuzz stage */
|
||||
stage_cycles[32]; /* Execs per fuzz stage */
|
||||
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
extern u32 rand_cnt; /* Random number counter */
|
||||
#endif
|
||||
|
||||
extern u64 total_cal_us, /* Total calibration time (us) */
|
||||
total_cal_cycles; /* Total calibration cycles */
|
||||
|
||||
extern u64 total_bitmap_size, /* Total bit count for all bitmaps */
|
||||
total_bitmap_entries; /* Number of bitmaps counted */
|
||||
|
||||
extern s32 cpu_core_count; /* CPU core count */
|
||||
|
||||
#ifdef HAVE_AFFINITY
|
||||
|
||||
extern s32 cpu_aff; /* Selected CPU core */
|
||||
|
||||
#endif /* HAVE_AFFINITY */
|
||||
|
||||
extern FILE* plot_file; /* Gnuplot output file */
|
||||
|
||||
extern struct queue_entry *queue, /* Fuzzing queue (linked list) */
|
||||
*queue_cur, /* Current offset within the queue */
|
||||
*queue_top, /* Top of the list */
|
||||
*q_prev100; /* Previous 100 marker */
|
||||
|
||||
extern struct queue_entry*
|
||||
top_rated[MAP_SIZE]; /* Top entries for bitmap bytes */
|
||||
|
||||
extern struct extra_data* extras; /* Extra tokens to fuzz with */
|
||||
extern u32 extras_cnt; /* Total number of tokens read */
|
||||
|
||||
extern struct extra_data* a_extras; /* Automatically selected extras */
|
||||
extern u32 a_extras_cnt; /* Total number of tokens available */
|
||||
|
||||
u8* (*post_handler)(u8* buf, u32* len);
|
||||
|
||||
/* hooks for the custom mutator function */
|
||||
size_t (*custom_mutator)(u8* data, size_t size, u8* mutated_out,
|
||||
size_t max_size, unsigned int seed);
|
||||
size_t (*pre_save_handler)(u8* data, size_t size, u8** new_data);
|
||||
|
||||
/* Interesting values, as per config.h */
|
||||
|
||||
extern s8 interesting_8[INTERESTING_8_LEN];
|
||||
extern s16 interesting_16[INTERESTING_8_LEN + INTERESTING_16_LEN];
|
||||
extern s32
|
||||
interesting_32[INTERESTING_8_LEN + INTERESTING_16_LEN + INTERESTING_32_LEN];
|
||||
|
||||
/* Python stuff */
|
||||
#ifdef USE_PYTHON
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
extern PyObject* py_module;
|
||||
|
||||
enum {
|
||||
|
||||
/* 00 */ PY_FUNC_INIT,
|
||||
/* 01 */ PY_FUNC_FUZZ,
|
||||
/* 02 */ PY_FUNC_INIT_TRIM,
|
||||
/* 03 */ PY_FUNC_POST_TRIM,
|
||||
/* 04 */ PY_FUNC_TRIM,
|
||||
PY_FUNC_COUNT
|
||||
|
||||
};
|
||||
|
||||
extern PyObject* py_functions[PY_FUNC_COUNT];
|
||||
|
||||
#endif
|
||||
|
||||
/**** Prototypes ****/
|
||||
|
||||
/* Python */
|
||||
#ifdef USE_PYTHON
|
||||
int init_py();
|
||||
void finalize_py();
|
||||
void fuzz_py(char*, size_t, char*, size_t, char**, size_t*);
|
||||
u32 init_trim_py(char*, size_t);
|
||||
u32 post_trim_py(char);
|
||||
void trim_py(char**, size_t*);
|
||||
u8 trim_case_python(char**, struct queue_entry*, u8*);
|
||||
#endif
|
||||
|
||||
/* Queue */
|
||||
|
||||
void mark_as_det_done(struct queue_entry*);
|
||||
void mark_as_variable(struct queue_entry*);
|
||||
void mark_as_redundant(struct queue_entry*, u8);
|
||||
void add_to_queue(u8*, u32, u8);
|
||||
void destroy_queue(void);
|
||||
void update_bitmap_score(struct queue_entry*);
|
||||
void cull_queue(void);
|
||||
u32 calculate_score(struct queue_entry*);
|
||||
|
||||
/* Bitmap */
|
||||
|
||||
void write_bitmap(void);
|
||||
void read_bitmap(u8*);
|
||||
u8 has_new_bits(u8*);
|
||||
u32 count_bits(u8*);
|
||||
u32 count_bytes(u8*);
|
||||
u32 count_non_255_bytes(u8*);
|
||||
#ifdef __x86_64__
|
||||
void simplify_trace(u64*);
|
||||
void classify_counts(u64*);
|
||||
#else
|
||||
void simplify_trace(u32*);
|
||||
void classify_counts(u32*);
|
||||
#endif
|
||||
void init_count_class16(void);
|
||||
void minimize_bits(u8*, u8*);
|
||||
#ifndef SIMPLE_FILES
|
||||
u8* describe_op(u8);
|
||||
#endif
|
||||
u8 save_if_interesting(char**, void*, u32, u8);
|
||||
|
||||
/* Misc */
|
||||
|
||||
u8* DI(u64);
|
||||
u8* DF(double);
|
||||
u8* DMS(u64);
|
||||
u8* DTD(u64, u64);
|
||||
|
||||
/* Extras */
|
||||
|
||||
void load_extras_file(u8*, u32*, u32*, u32);
|
||||
void load_extras(u8*);
|
||||
void maybe_add_auto(u8*, u32);
|
||||
void save_auto(void);
|
||||
void load_auto(void);
|
||||
void destroy_extras(void);
|
||||
|
||||
/* Stats */
|
||||
|
||||
void write_stats_file(double, double, double);
|
||||
void maybe_update_plot_file(double, double);
|
||||
void show_stats(void);
|
||||
void show_init_stats(void);
|
||||
|
||||
/* Run */
|
||||
|
||||
u8 run_target(char**, u32);
|
||||
void write_to_testcase(void*, u32);
|
||||
void write_with_gap(void*, u32, u32, u32);
|
||||
u8 calibrate_case(char**, struct queue_entry*, u8*, u32, u8);
|
||||
void sync_fuzzers(char**);
|
||||
u8 trim_case(char**, struct queue_entry*, u8*);
|
||||
u8 common_fuzz_stuff(char**, u8*, u32);
|
||||
|
||||
/* Fuzz one */
|
||||
|
||||
u8 fuzz_one_original(char**);
|
||||
u8 pilot_fuzzing(char**);
|
||||
u8 core_fuzzing(char**);
|
||||
void pso_updating(void);
|
||||
u8 fuzz_one(char**);
|
||||
|
||||
/* Init */
|
||||
|
||||
#ifdef HAVE_AFFINITY
|
||||
void bind_to_free_cpu(void);
|
||||
#endif
|
||||
void setup_post(void);
|
||||
void setup_custom_mutator(void);
|
||||
void read_testcases(void);
|
||||
void perform_dry_run(char**);
|
||||
void pivot_inputs(void);
|
||||
u32 find_start_position(void);
|
||||
void find_timeout(void);
|
||||
double get_runnable_processes(void);
|
||||
void nuke_resume_dir(void);
|
||||
void maybe_delete_out_dir(void);
|
||||
void setup_dirs_fds(void);
|
||||
void setup_cmdline_file(char**);
|
||||
void setup_stdio_file(void);
|
||||
void check_crash_handling(void);
|
||||
void check_cpu_governor(void);
|
||||
void get_core_count(void);
|
||||
void fix_up_sync(void);
|
||||
void check_asan_opts(void);
|
||||
void check_binary(u8*);
|
||||
void fix_up_banner(u8*);
|
||||
void check_if_tty(void);
|
||||
void setup_signal_handlers(void);
|
||||
char** get_qemu_argv(u8*, char**, int);
|
||||
void save_cmdline(u32, char**);
|
||||
|
||||
/**** Inline routines ****/
|
||||
|
||||
/* Generate a random number (from 0 to limit - 1). This may
|
||||
have slight bias. */
|
||||
|
||||
static inline u32 UR(u32 limit) {
|
||||
|
||||
#ifdef HAVE_ARC4RANDOM
|
||||
if (fixed_seed) { return random() % limit; }
|
||||
|
||||
/* The boundary not being necessarily a power of 2,
|
||||
we need to ensure the result uniformity. */
|
||||
return arc4random_uniform(limit);
|
||||
#else
|
||||
if (!fixed_seed && unlikely(!rand_cnt--)) {
|
||||
|
||||
u32 seed[2];
|
||||
|
||||
ck_read(dev_urandom_fd, &seed, sizeof(seed), "/dev/urandom");
|
||||
srandom(seed[0]);
|
||||
rand_cnt = (RESEED_RNG / 2) + (seed[1] % RESEED_RNG);
|
||||
|
||||
}
|
||||
|
||||
return random() % limit;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* Find first power of two greater or equal to val (assuming val under
|
||||
2^63). */
|
||||
|
||||
static u64 next_p2(u64 val) {
|
||||
|
||||
u64 ret = 1;
|
||||
while (val > ret)
|
||||
ret <<= 1;
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* Get unix time in milliseconds */
|
||||
|
||||
static u64 get_cur_time(void) {
|
||||
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
gettimeofday(&tv, &tz);
|
||||
|
||||
return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000);
|
||||
|
||||
}
|
||||
|
||||
/* Get unix time in microseconds */
|
||||
|
||||
static u64 get_cur_time_us(void) {
|
||||
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
gettimeofday(&tv, &tz);
|
||||
|
||||
return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,10 +1,15 @@
|
||||
/*
|
||||
american fuzzy lop - error-checking, memory-zeroing alloc routines
|
||||
------------------------------------------------------------------
|
||||
american fuzzy lop++ - error-checking, memory-zeroing alloc routines
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Copyright 2013, 2014, 2015 Google Inc. All rights reserved.
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -31,88 +36,128 @@
|
||||
|
||||
/* User-facing macro to sprintf() to a dynamically allocated buffer. */
|
||||
|
||||
#define alloc_printf(_str...) ({ \
|
||||
u8* _tmp; \
|
||||
s32 _len = snprintf(NULL, 0, _str); \
|
||||
#define alloc_printf(_str...) \
|
||||
({ \
|
||||
\
|
||||
u8* _tmp; \
|
||||
s32 _len = snprintf(NULL, 0, _str); \
|
||||
if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \
|
||||
_tmp = ck_alloc(_len + 1); \
|
||||
snprintf((char*)_tmp, _len + 1, _str); \
|
||||
_tmp; \
|
||||
_tmp = ck_alloc(_len + 1); \
|
||||
snprintf((char*)_tmp, _len + 1, _str); \
|
||||
_tmp; \
|
||||
\
|
||||
})
|
||||
|
||||
/* Macro to enforce allocation limits as a last-resort defense against
|
||||
integer overflows. */
|
||||
|
||||
#define ALLOC_CHECK_SIZE(_s) do { \
|
||||
if ((_s) > MAX_ALLOC) \
|
||||
ABORT("Bad alloc request: %u bytes", (_s)); \
|
||||
#define ALLOC_CHECK_SIZE(_s) \
|
||||
do { \
|
||||
\
|
||||
if ((_s) > MAX_ALLOC) ABORT("Bad alloc request: %u bytes", (_s)); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Macro to check malloc() failures and the like. */
|
||||
|
||||
#define ALLOC_CHECK_RESULT(_r, _s) do { \
|
||||
if (!(_r)) \
|
||||
ABORT("Out of memory: can't allocate %u bytes", (_s)); \
|
||||
#define ALLOC_CHECK_RESULT(_r, _s) \
|
||||
do { \
|
||||
\
|
||||
if (!(_r)) ABORT("Out of memory: can't allocate %u bytes", (_s)); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Magic tokens used to mark used / freed chunks. */
|
||||
|
||||
#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */
|
||||
#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */
|
||||
#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */
|
||||
#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */
|
||||
#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */
|
||||
#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */
|
||||
|
||||
/* Positions of guard tokens in relation to the user-visible pointer. */
|
||||
|
||||
#define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2])
|
||||
#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1])
|
||||
#define ALLOC_C2(_ptr) (((u8*)(_ptr))[ALLOC_S(_ptr)])
|
||||
#define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2])
|
||||
#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1])
|
||||
#define ALLOC_C2(_ptr) (((u8*)(_ptr))[ALLOC_S(_ptr)])
|
||||
|
||||
#define ALLOC_OFF_HEAD 8
|
||||
#define ALLOC_OFF_HEAD 8
|
||||
#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1)
|
||||
|
||||
/* Allocator increments for ck_realloc_block(). */
|
||||
|
||||
#define ALLOC_BLK_INC 256
|
||||
#define ALLOC_BLK_INC 256
|
||||
|
||||
/* Sanity-checking macros for pointers. */
|
||||
|
||||
#define CHECK_PTR(_p) do { \
|
||||
if (_p) { \
|
||||
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\
|
||||
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
|
||||
ABORT("Use after free."); \
|
||||
else ABORT("Corrupted head alloc canary."); \
|
||||
} \
|
||||
} \
|
||||
#define CHECK_PTR(_p) \
|
||||
do { \
|
||||
\
|
||||
if (_p) { \
|
||||
\
|
||||
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) { \
|
||||
\
|
||||
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
|
||||
ABORT("Use after free."); \
|
||||
else \
|
||||
ABORT("Corrupted head alloc canary."); \
|
||||
\
|
||||
} \
|
||||
\
|
||||
} \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
#define CHECK_PTR(_p) do { \
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
if (_p) { \
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
|
||||
ABORT("Use after free."); \
|
||||
else ABORT("Corrupted head alloc canary."); \
|
||||
\
|
||||
} \
|
||||
\
|
||||
\
|
||||
\
|
||||
if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \
|
||||
ABORT("Corrupted tail alloc canary."); \
|
||||
\
|
||||
} \
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
} while (0)
|
||||
|
||||
*/
|
||||
|
||||
#define CHECK_PTR_EXPR(_p) ({ \
|
||||
typeof (_p) _tmp = (_p); \
|
||||
CHECK_PTR(_tmp); \
|
||||
_tmp; \
|
||||
#define CHECK_PTR_EXPR(_p) \
|
||||
({ \
|
||||
\
|
||||
typeof(_p) _tmp = (_p); \
|
||||
CHECK_PTR(_tmp); \
|
||||
_tmp; \
|
||||
\
|
||||
})
|
||||
|
||||
|
||||
/* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized
|
||||
requests. */
|
||||
|
||||
static inline void* DFL_ck_alloc_nozero(u32 size) {
|
||||
|
||||
void* ret;
|
||||
u8* ret;
|
||||
|
||||
if (!size) return NULL;
|
||||
|
||||
@ -123,14 +168,13 @@ static inline void* DFL_ck_alloc_nozero(u32 size) {
|
||||
ret += ALLOC_OFF_HEAD;
|
||||
|
||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||
|
||||
return ret;
|
||||
return (void*)ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Allocate a buffer, returning zeroed memory. */
|
||||
|
||||
static inline void* DFL_ck_alloc(u32 size) {
|
||||
@ -144,7 +188,6 @@ static inline void* DFL_ck_alloc(u32 size) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Free memory, checking for double free and corrupted heap. When DEBUG_BUILD
|
||||
is set, the old memory will be also clobbered with 0xFF. */
|
||||
|
||||
@ -159,23 +202,23 @@ static inline void DFL_ck_free(void* mem) {
|
||||
/* Catch pointer issues sooner. */
|
||||
memset(mem, 0xFF, ALLOC_S(mem));
|
||||
|
||||
#endif /* DEBUG_BUILD */
|
||||
#endif /* DEBUG_BUILD */
|
||||
|
||||
ALLOC_C1(mem) = ALLOC_MAGIC_F;
|
||||
|
||||
free(mem - ALLOC_OFF_HEAD);
|
||||
u8* realStart = mem;
|
||||
free(realStart - ALLOC_OFF_HEAD);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Re-allocate a buffer, checking for issues and zeroing any newly-added tail.
|
||||
With DEBUG_BUILD, the buffer is always reallocated to a new addresses and the
|
||||
old memory is clobbered with 0xFF. */
|
||||
|
||||
static inline void* DFL_ck_realloc(void* orig, u32 size) {
|
||||
|
||||
void* ret;
|
||||
u32 old_size = 0;
|
||||
u8* ret;
|
||||
u32 old_size = 0;
|
||||
|
||||
if (!size) {
|
||||
|
||||
@ -190,10 +233,12 @@ static inline void* DFL_ck_realloc(void* orig, u32 size) {
|
||||
|
||||
#ifndef DEBUG_BUILD
|
||||
ALLOC_C1(orig) = ALLOC_MAGIC_F;
|
||||
#endif /* !DEBUG_BUILD */
|
||||
#endif /* !DEBUG_BUILD */
|
||||
|
||||
old_size = ALLOC_S(orig);
|
||||
orig -= ALLOC_OFF_HEAD;
|
||||
old_size = ALLOC_S(orig);
|
||||
u8* origu8 = orig;
|
||||
origu8 -= ALLOC_OFF_HEAD;
|
||||
orig = origu8;
|
||||
|
||||
ALLOC_CHECK_SIZE(old_size);
|
||||
|
||||
@ -216,31 +261,30 @@ static inline void* DFL_ck_realloc(void* orig, u32 size) {
|
||||
|
||||
if (orig) {
|
||||
|
||||
memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size));
|
||||
memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size);
|
||||
u8* origu8 = orig;
|
||||
memcpy(ret + ALLOC_OFF_HEAD, origu8 + ALLOC_OFF_HEAD, MIN(size, old_size));
|
||||
memset(origu8 + ALLOC_OFF_HEAD, 0xFF, old_size);
|
||||
|
||||
ALLOC_C1(orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F;
|
||||
ALLOC_C1(origu8 + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F;
|
||||
|
||||
free(orig);
|
||||
|
||||
}
|
||||
|
||||
#endif /* ^!DEBUG_BUILD */
|
||||
#endif /* ^!DEBUG_BUILD */
|
||||
|
||||
ret += ALLOC_OFF_HEAD;
|
||||
|
||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||
|
||||
if (size > old_size)
|
||||
memset(ret + old_size, 0, size - old_size);
|
||||
if (size > old_size) memset(ret + old_size, 0, size - old_size);
|
||||
|
||||
return ret;
|
||||
return (void*)ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up
|
||||
repeated small reallocs without complicating the user code). */
|
||||
|
||||
@ -258,19 +302,18 @@ static inline void* DFL_ck_realloc_block(void* orig, u32 size) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* !DEBUG_BUILD */
|
||||
#endif /* !DEBUG_BUILD */
|
||||
|
||||
return DFL_ck_realloc(orig, size);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */
|
||||
|
||||
static inline u8* DFL_ck_strdup(u8* str) {
|
||||
|
||||
void* ret;
|
||||
u32 size;
|
||||
u8* ret;
|
||||
u32 size;
|
||||
|
||||
if (!str) return NULL;
|
||||
|
||||
@ -283,38 +326,36 @@ static inline u8* DFL_ck_strdup(u8* str) {
|
||||
ret += ALLOC_OFF_HEAD;
|
||||
|
||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||
|
||||
return memcpy(ret, str, size);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized
|
||||
or NULL inputs. */
|
||||
|
||||
static inline void* DFL_ck_memdup(void* mem, u32 size) {
|
||||
|
||||
void* ret;
|
||||
u8* ret;
|
||||
|
||||
if (!mem || !size) return NULL;
|
||||
|
||||
ALLOC_CHECK_SIZE(size);
|
||||
ret = malloc(size + ALLOC_OFF_TOTAL);
|
||||
ALLOC_CHECK_RESULT(ret, size);
|
||||
|
||||
|
||||
ret += ALLOC_OFF_HEAD;
|
||||
|
||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||
|
||||
return memcpy(ret, mem, size);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Create a buffer with a block of text, appending a NUL terminator at the end.
|
||||
Returns NULL for zero-sized or NULL inputs. */
|
||||
|
||||
@ -327,11 +368,11 @@ static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) {
|
||||
ALLOC_CHECK_SIZE(size);
|
||||
ret = malloc(size + ALLOC_OFF_TOTAL + 1);
|
||||
ALLOC_CHECK_RESULT(ret, size);
|
||||
|
||||
|
||||
ret += ALLOC_OFF_HEAD;
|
||||
|
||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_S(ret) = size;
|
||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||
|
||||
memcpy(ret, mem, size);
|
||||
@ -341,20 +382,19 @@ static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifndef DEBUG_BUILD
|
||||
|
||||
/* In non-debug mode, we just do straightforward aliasing of the above functions
|
||||
to user-visible names such as ck_alloc(). */
|
||||
|
||||
#define ck_alloc DFL_ck_alloc
|
||||
#define ck_alloc_nozero DFL_ck_alloc_nozero
|
||||
#define ck_realloc DFL_ck_realloc
|
||||
#define ck_realloc_block DFL_ck_realloc_block
|
||||
#define ck_strdup DFL_ck_strdup
|
||||
#define ck_memdup DFL_ck_memdup
|
||||
#define ck_memdup_str DFL_ck_memdup_str
|
||||
#define ck_free DFL_ck_free
|
||||
#define ck_alloc DFL_ck_alloc
|
||||
#define ck_alloc_nozero DFL_ck_alloc_nozero
|
||||
#define ck_realloc DFL_ck_realloc
|
||||
#define ck_realloc_block DFL_ck_realloc_block
|
||||
#define ck_strdup DFL_ck_strdup
|
||||
#define ck_memdup DFL_ck_memdup
|
||||
#define ck_memdup_str DFL_ck_memdup_str
|
||||
#define ck_free DFL_ck_free
|
||||
|
||||
#define alloc_report()
|
||||
|
||||
@ -365,12 +405,14 @@ static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) {
|
||||
|
||||
/* Alloc tracking data structures: */
|
||||
|
||||
#define ALLOC_BUCKETS 4096
|
||||
#define ALLOC_BUCKETS 4096
|
||||
|
||||
struct TRK_obj {
|
||||
void *ptr;
|
||||
|
||||
void* ptr;
|
||||
char *file, *func;
|
||||
u32 line;
|
||||
u32 line;
|
||||
|
||||
};
|
||||
|
||||
#ifdef AFL_MAIN
|
||||
@ -378,22 +420,21 @@ struct TRK_obj {
|
||||
struct TRK_obj* TRK[ALLOC_BUCKETS];
|
||||
u32 TRK_cnt[ALLOC_BUCKETS];
|
||||
|
||||
# define alloc_report() TRK_report()
|
||||
#define alloc_report() TRK_report()
|
||||
|
||||
#else
|
||||
|
||||
extern struct TRK_obj* TRK[ALLOC_BUCKETS];
|
||||
extern u32 TRK_cnt[ALLOC_BUCKETS];
|
||||
extern u32 TRK_cnt[ALLOC_BUCKETS];
|
||||
|
||||
# define alloc_report()
|
||||
#define alloc_report()
|
||||
|
||||
#endif /* ^AFL_MAIN */
|
||||
#endif /* ^AFL_MAIN */
|
||||
|
||||
/* Bucket-assigning function for a given pointer: */
|
||||
|
||||
#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS)
|
||||
|
||||
|
||||
/* Add a new entry to the list of allocated objects. */
|
||||
|
||||
static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func,
|
||||
@ -411,7 +452,7 @@ static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func,
|
||||
|
||||
if (!TRK[bucket][i].ptr) {
|
||||
|
||||
TRK[bucket][i].ptr = ptr;
|
||||
TRK[bucket][i].ptr = ptr;
|
||||
TRK[bucket][i].file = (char*)file;
|
||||
TRK[bucket][i].func = (char*)func;
|
||||
TRK[bucket][i].line = line;
|
||||
@ -421,10 +462,10 @@ static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func,
|
||||
|
||||
/* No space available - allocate more. */
|
||||
|
||||
TRK[bucket] = DFL_ck_realloc_block(TRK[bucket],
|
||||
(TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
|
||||
TRK[bucket] = DFL_ck_realloc_block(
|
||||
TRK[bucket], (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
|
||||
|
||||
TRK[bucket][i].ptr = ptr;
|
||||
TRK[bucket][i].ptr = ptr;
|
||||
TRK[bucket][i].file = (char*)file;
|
||||
TRK[bucket][i].func = (char*)func;
|
||||
TRK[bucket][i].line = line;
|
||||
@ -433,7 +474,6 @@ static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func,
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Remove entry from the list of allocated objects. */
|
||||
|
||||
static inline void TRK_free_buf(void* ptr, const char* file, const char* func,
|
||||
@ -456,12 +496,11 @@ static inline void TRK_free_buf(void* ptr, const char* file, const char* func,
|
||||
|
||||
}
|
||||
|
||||
WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)",
|
||||
func, file, line);
|
||||
WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)", func, file,
|
||||
line);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Do a final report on all non-deallocated objects. */
|
||||
|
||||
static inline void TRK_report(void) {
|
||||
@ -478,7 +517,6 @@ static inline void TRK_report(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Simple wrappers for non-debugging functions: */
|
||||
|
||||
static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func,
|
||||
@ -490,7 +528,6 @@ static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func,
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void* TRK_ck_realloc(void* orig, u32 size, const char* file,
|
||||
const char* func, u32 line) {
|
||||
|
||||
@ -501,7 +538,6 @@ static inline void* TRK_ck_realloc(void* orig, u32 size, const char* file,
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void* TRK_ck_realloc_block(void* orig, u32 size, const char* file,
|
||||
const char* func, u32 line) {
|
||||
|
||||
@ -512,7 +548,6 @@ static inline void* TRK_ck_realloc_block(void* orig, u32 size, const char* file,
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void* TRK_ck_strdup(u8* str, const char* file, const char* func,
|
||||
u32 line) {
|
||||
|
||||
@ -522,7 +557,6 @@ static inline void* TRK_ck_strdup(u8* str, const char* file, const char* func,
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void* TRK_ck_memdup(void* mem, u32 size, const char* file,
|
||||
const char* func, u32 line) {
|
||||
|
||||
@ -532,7 +566,6 @@ static inline void* TRK_ck_memdup(void* mem, u32 size, const char* file,
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void* TRK_ck_memdup_str(void* mem, u32 size, const char* file,
|
||||
const char* func, u32 line) {
|
||||
|
||||
@ -542,9 +575,8 @@ static inline void* TRK_ck_memdup_str(void* mem, u32 size, const char* file,
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline void TRK_ck_free(void* ptr, const char* file,
|
||||
const char* func, u32 line) {
|
||||
static inline void TRK_ck_free(void* ptr, const char* file, const char* func,
|
||||
u32 line) {
|
||||
|
||||
TRK_free_buf(ptr, file, func, line);
|
||||
DFL_ck_free(ptr);
|
||||
@ -553,11 +585,9 @@ static inline void TRK_ck_free(void* ptr, const char* file,
|
||||
|
||||
/* Aliasing user-facing names to tracking functions: */
|
||||
|
||||
#define ck_alloc(_p1) \
|
||||
TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_alloc(_p1) TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_alloc_nozero(_p1) \
|
||||
TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_alloc_nozero(_p1) TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_realloc(_p1, _p2) \
|
||||
TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
@ -565,8 +595,7 @@ static inline void TRK_ck_free(void* ptr, const char* file,
|
||||
#define ck_realloc_block(_p1, _p2) \
|
||||
TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_strdup(_p1) \
|
||||
TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_strdup(_p1) TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_memdup(_p1, _p2) \
|
||||
TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
@ -574,9 +603,9 @@ static inline void TRK_ck_free(void* ptr, const char* file,
|
||||
#define ck_memdup_str(_p1, _p2) \
|
||||
TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#define ck_free(_p1) \
|
||||
TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
#define ck_free(_p1) TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||
|
||||
#endif /* ^!DEBUG_BUILD */
|
||||
#endif /* ^!DEBUG_BUILD */
|
||||
|
||||
#endif /* ! _HAVE_ALLOC_INL_H */
|
||||
|
||||
#endif /* ! _HAVE_ALLOC_INL_H */
|
104
include/android-ashmem.h
Normal file
104
include/android-ashmem.h
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
american fuzzy lop++ - android shared memory compatibility layer
|
||||
----------------------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This header re-defines the shared memory routines used by AFL++
|
||||
using the Andoid API.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _ANDROID_ASHMEM_H
|
||||
#define _ANDROID_ASHMEM_H
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/shm.h>
|
||||
#include <linux/ashmem.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#if __ANDROID_API__ >= 26
|
||||
#define shmat bionic_shmat
|
||||
#define shmctl bionic_shmctl
|
||||
#define shmdt bionic_shmdt
|
||||
#define shmget bionic_shmget
|
||||
#endif
|
||||
#include <sys/shm.h>
|
||||
#undef shmat
|
||||
#undef shmctl
|
||||
#undef shmdt
|
||||
#undef shmget
|
||||
#include <stdio.h>
|
||||
|
||||
#define ASHMEM_DEVICE "/dev/ashmem"
|
||||
|
||||
static inline int shmctl(int __shmid, int __cmd, struct shmid_ds *__buf) {
|
||||
|
||||
int ret = 0;
|
||||
if (__cmd == IPC_RMID) {
|
||||
|
||||
int length = ioctl(__shmid, ASHMEM_GET_SIZE, NULL);
|
||||
struct ashmem_pin pin = {0, length};
|
||||
ret = ioctl(__shmid, ASHMEM_UNPIN, &pin);
|
||||
close(__shmid);
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static inline int shmget(key_t __key, size_t __size, int __shmflg) {
|
||||
|
||||
int fd, ret;
|
||||
char ourkey[11];
|
||||
|
||||
fd = open(ASHMEM_DEVICE, O_RDWR);
|
||||
if (fd < 0) return fd;
|
||||
|
||||
sprintf(ourkey, "%d", __key);
|
||||
ret = ioctl(fd, ASHMEM_SET_NAME, ourkey);
|
||||
if (ret < 0) goto error;
|
||||
|
||||
ret = ioctl(fd, ASHMEM_SET_SIZE, __size);
|
||||
if (ret < 0) goto error;
|
||||
|
||||
return fd;
|
||||
|
||||
error:
|
||||
close(fd);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static inline void *shmat(int __shmid, const void *__shmaddr, int __shmflg) {
|
||||
|
||||
int size;
|
||||
void *ptr;
|
||||
|
||||
size = ioctl(__shmid, ASHMEM_GET_SIZE, NULL);
|
||||
if (size < 0) { return NULL; }
|
||||
|
||||
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, __shmid, 0);
|
||||
if (ptr == MAP_FAILED) { return NULL; }
|
||||
|
||||
return ptr;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
32
include/common.h
Normal file
32
include/common.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
american fuzzy lop++ - common routines header
|
||||
---------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
Gather some functions common to multiple executables
|
||||
|
||||
- detect_file_args
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __AFLCOMMON_H
|
||||
#define __AFLCOMMON_H
|
||||
#include "types.h"
|
||||
|
||||
void detect_file_args(char **argv, u8 *prog_in);
|
||||
#endif
|
||||
|
375
include/config.h
Normal file
375
include/config.h
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
american fuzzy lop++ - vaguely configurable bits
|
||||
------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _HAVE_CONFIG_H
|
||||
#define _HAVE_CONFIG_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/* Version string: */
|
||||
|
||||
#define VERSION "++2.53d" // c = release, d = volatile github dev
|
||||
|
||||
/******************************************************
|
||||
* *
|
||||
* Settings that may be of interest to power users: *
|
||||
* *
|
||||
******************************************************/
|
||||
|
||||
/* Comment out to disable terminal colors (note that this makes afl-analyze
|
||||
a lot less nice): */
|
||||
|
||||
#define USE_COLOR
|
||||
|
||||
/* Comment out to disable fancy ANSI boxes and use poor man's 7-bit UI: */
|
||||
|
||||
#define FANCY_BOXES
|
||||
|
||||
/* Default timeout for fuzzed code (milliseconds). This is the upper bound,
|
||||
also used for detecting hangs; the actual value is auto-scaled: */
|
||||
|
||||
#define EXEC_TIMEOUT 1000
|
||||
|
||||
/* Timeout rounding factor when auto-scaling (milliseconds): */
|
||||
|
||||
#define EXEC_TM_ROUND 20
|
||||
|
||||
/* Default memory limit for child process (MB): */
|
||||
|
||||
#ifndef __x86_64__
|
||||
#define MEM_LIMIT 25
|
||||
#else
|
||||
#define MEM_LIMIT 50
|
||||
#endif /* ^!__x86_64__ */
|
||||
|
||||
/* Default memory limit when running in QEMU mode (MB): */
|
||||
|
||||
#define MEM_LIMIT_QEMU 200
|
||||
|
||||
/* Default memory limit when running in Unicorn mode (MB): */
|
||||
|
||||
#define MEM_LIMIT_UNICORN 200
|
||||
|
||||
/* Number of calibration cycles per every new test case (and for test
|
||||
cases that show variable behavior): */
|
||||
|
||||
#define CAL_CYCLES 8
|
||||
#define CAL_CYCLES_LONG 40
|
||||
|
||||
/* Number of subsequent timeouts before abandoning an input file: */
|
||||
|
||||
#define TMOUT_LIMIT 250
|
||||
|
||||
/* Maximum number of unique hangs or crashes to record: */
|
||||
|
||||
#define KEEP_UNIQUE_HANG 500
|
||||
#define KEEP_UNIQUE_CRASH 5000
|
||||
|
||||
/* Baseline number of random tweaks during a single 'havoc' stage: */
|
||||
|
||||
#define HAVOC_CYCLES 256
|
||||
#define HAVOC_CYCLES_INIT 1024
|
||||
|
||||
/* Maximum multiplier for the above (should be a power of two, beware
|
||||
of 32-bit int overflows): */
|
||||
|
||||
#define HAVOC_MAX_MULT 16
|
||||
#define HAVOC_MAX_MULT_MOPT 32
|
||||
|
||||
/* Absolute minimum number of havoc cycles (after all adjustments): */
|
||||
|
||||
#define HAVOC_MIN 16
|
||||
|
||||
/* Power Schedule Divisor */
|
||||
#define POWER_BETA 1
|
||||
#define MAX_FACTOR (POWER_BETA * 32)
|
||||
|
||||
/* Maximum stacking for havoc-stage tweaks. The actual value is calculated
|
||||
like this:
|
||||
|
||||
n = random between 1 and HAVOC_STACK_POW2
|
||||
stacking = 2^n
|
||||
|
||||
In other words, the default (n = 7) produces 2, 4, 8, 16, 32, 64, or
|
||||
128 stacked tweaks: */
|
||||
|
||||
#define HAVOC_STACK_POW2 7
|
||||
|
||||
/* Caps on block sizes for cloning and deletion operations. Each of these
|
||||
ranges has a 33% probability of getting picked, except for the first
|
||||
two cycles where smaller blocks are favored: */
|
||||
|
||||
#define HAVOC_BLK_SMALL 32
|
||||
#define HAVOC_BLK_MEDIUM 128
|
||||
#define HAVOC_BLK_LARGE 1500
|
||||
|
||||
/* Extra-large blocks, selected very rarely (<5% of the time): */
|
||||
|
||||
#define HAVOC_BLK_XL 32768
|
||||
|
||||
/* Probabilities of skipping non-favored entries in the queue, expressed as
|
||||
percentages: */
|
||||
|
||||
#define SKIP_TO_NEW_PROB 99 /* ...when there are new, pending favorites */
|
||||
#define SKIP_NFAV_OLD_PROB 95 /* ...no new favs, cur entry already fuzzed */
|
||||
#define SKIP_NFAV_NEW_PROB 75 /* ...no new favs, cur entry not fuzzed yet */
|
||||
|
||||
/* Splicing cycle count: */
|
||||
|
||||
#define SPLICE_CYCLES 15
|
||||
|
||||
/* Nominal per-splice havoc cycle length: */
|
||||
|
||||
#define SPLICE_HAVOC 32
|
||||
|
||||
/* Maximum offset for integer addition / subtraction stages: */
|
||||
|
||||
#define ARITH_MAX 35
|
||||
|
||||
/* Limits for the test case trimmer. The absolute minimum chunk size; and
|
||||
the starting and ending divisors for chopping up the input file: */
|
||||
|
||||
#define TRIM_MIN_BYTES 4
|
||||
#define TRIM_START_STEPS 16
|
||||
#define TRIM_END_STEPS 1024
|
||||
|
||||
/* Maximum size of input file, in bytes (keep under 100MB): */
|
||||
|
||||
#define MAX_FILE (1 * 1024 * 1024)
|
||||
|
||||
/* The same, for the test case minimizer: */
|
||||
|
||||
#define TMIN_MAX_FILE (10 * 1024 * 1024)
|
||||
|
||||
/* Block normalization steps for afl-tmin: */
|
||||
|
||||
#define TMIN_SET_MIN_SIZE 4
|
||||
#define TMIN_SET_STEPS 128
|
||||
|
||||
/* Maximum dictionary token size (-x), in bytes: */
|
||||
|
||||
#define MAX_DICT_FILE 128
|
||||
|
||||
/* Length limits for auto-detected dictionary tokens: */
|
||||
|
||||
#define MIN_AUTO_EXTRA 3
|
||||
#define MAX_AUTO_EXTRA 32
|
||||
|
||||
/* Maximum number of user-specified dictionary tokens to use in deterministic
|
||||
steps; past this point, the "extras/user" step will be still carried out,
|
||||
but with proportionally lower odds: */
|
||||
|
||||
#define MAX_DET_EXTRAS 200
|
||||
|
||||
/* 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
|
||||
higher than the former. */
|
||||
|
||||
#define USE_AUTO_EXTRAS 50
|
||||
#define MAX_AUTO_EXTRAS (USE_AUTO_EXTRAS * 10)
|
||||
|
||||
/* Scaling factor for the effector map used to skip some of the more
|
||||
expensive deterministic steps. The actual divisor is set to
|
||||
2^EFF_MAP_SCALE2 bytes: */
|
||||
|
||||
#define EFF_MAP_SCALE2 3
|
||||
|
||||
/* Minimum input file length at which the effector logic kicks in: */
|
||||
|
||||
#define EFF_MIN_LEN 128
|
||||
|
||||
/* Maximum effector density past which everything is just fuzzed
|
||||
unconditionally (%): */
|
||||
|
||||
#define EFF_MAX_PERC 90
|
||||
|
||||
/* UI refresh frequency (Hz): */
|
||||
|
||||
#define UI_TARGET_HZ 5
|
||||
|
||||
/* Fuzzer stats file and plot update intervals (sec): */
|
||||
|
||||
#define STATS_UPDATE_SEC 60
|
||||
#define PLOT_UPDATE_SEC 5
|
||||
|
||||
/* Smoothing divisor for CPU load and exec speed stats (1 - no smoothing). */
|
||||
|
||||
#define AVG_SMOOTHING 16
|
||||
|
||||
/* Sync interval (every n havoc cycles): */
|
||||
|
||||
#define SYNC_INTERVAL 5
|
||||
|
||||
/* Output directory reuse grace period (minutes): */
|
||||
|
||||
#define OUTPUT_GRACE 25
|
||||
|
||||
/* Uncomment to use simple file names (id_NNNNNN): */
|
||||
|
||||
// #define SIMPLE_FILES
|
||||
|
||||
/* List of interesting values to use in fuzzing. */
|
||||
|
||||
#define INTERESTING_8 \
|
||||
-128, /* Overflow signed 8-bit when decremented */ \
|
||||
-1, /* */ \
|
||||
0, /* */ \
|
||||
1, /* */ \
|
||||
16, /* One-off with common buffer size */ \
|
||||
32, /* One-off with common buffer size */ \
|
||||
64, /* One-off with common buffer size */ \
|
||||
100, /* One-off with common buffer size */ \
|
||||
127 /* Overflow signed 8-bit when incremented */
|
||||
|
||||
#define INTERESTING_8_LEN 9
|
||||
|
||||
#define INTERESTING_16 \
|
||||
-32768, /* Overflow signed 16-bit when decremented */ \
|
||||
-129, /* Overflow signed 8-bit */ \
|
||||
128, /* Overflow signed 8-bit */ \
|
||||
255, /* Overflow unsig 8-bit when incremented */ \
|
||||
256, /* Overflow unsig 8-bit */ \
|
||||
512, /* One-off with common buffer size */ \
|
||||
1000, /* One-off with common buffer size */ \
|
||||
1024, /* One-off with common buffer size */ \
|
||||
4096, /* One-off with common buffer size */ \
|
||||
32767 /* Overflow signed 16-bit when incremented */
|
||||
|
||||
#define INTERESTING_16_LEN 10
|
||||
|
||||
#define INTERESTING_32 \
|
||||
-2147483648LL, /* Overflow signed 32-bit when decremented */ \
|
||||
-100663046, /* Large negative number (endian-agnostic) */ \
|
||||
-32769, /* Overflow signed 16-bit */ \
|
||||
32768, /* Overflow signed 16-bit */ \
|
||||
65535, /* Overflow unsig 16-bit when incremented */ \
|
||||
65536, /* Overflow unsig 16 bit */ \
|
||||
100663045, /* Large positive number (endian-agnostic) */ \
|
||||
2147483647 /* Overflow signed 32-bit when incremented */
|
||||
|
||||
#define INTERESTING_32_LEN 8
|
||||
|
||||
/***********************************************************
|
||||
* *
|
||||
* Really exotic stuff you probably don't want to touch: *
|
||||
* *
|
||||
***********************************************************/
|
||||
|
||||
/* Call count interval between reseeding the libc PRNG from /dev/urandom: */
|
||||
|
||||
#define RESEED_RNG 10000
|
||||
|
||||
/* Maximum line length passed from GCC to 'as' and used for parsing
|
||||
configuration files: */
|
||||
|
||||
#define MAX_LINE 8192
|
||||
|
||||
/* Environment variable used to pass SHM ID to the called program. */
|
||||
|
||||
#define SHM_ENV_VAR "__AFL_SHM_ID"
|
||||
|
||||
/* Other less interesting, internal-only variables. */
|
||||
|
||||
#define CLANG_ENV_VAR "__AFL_CLANG_MODE"
|
||||
#define AS_LOOP_ENV_VAR "__AFL_AS_LOOPCHECK"
|
||||
#define PERSIST_ENV_VAR "__AFL_PERSISTENT"
|
||||
#define DEFER_ENV_VAR "__AFL_DEFER_FORKSRV"
|
||||
|
||||
/* In-code signatures for deferred and persistent mode. */
|
||||
|
||||
#define PERSIST_SIG "##SIG_AFL_PERSISTENT##"
|
||||
#define DEFER_SIG "##SIG_AFL_DEFER_FORKSRV##"
|
||||
|
||||
/* Distinctive bitmap signature used to indicate failed execution: */
|
||||
|
||||
#define EXEC_FAIL_SIG 0xfee1dead
|
||||
|
||||
/* Distinctive exit code used to indicate MSAN trip condition: */
|
||||
|
||||
#define MSAN_ERROR 86
|
||||
|
||||
/* Designated file descriptors for forkserver commands (the application will
|
||||
use FORKSRV_FD and FORKSRV_FD + 1): */
|
||||
|
||||
#define FORKSRV_FD 198
|
||||
|
||||
/* Fork server init timeout multiplier: we'll wait the user-selected
|
||||
timeout plus this much for the fork server to spin up. */
|
||||
|
||||
#define FORK_WAIT_MULT 10
|
||||
|
||||
/* Calibration timeout adjustments, to be a bit more generous when resuming
|
||||
fuzzing sessions or trying to calibrate already-added internal finds.
|
||||
The first value is a percentage, the other is in milliseconds: */
|
||||
|
||||
#define CAL_TMOUT_PERC 125
|
||||
#define CAL_TMOUT_ADD 50
|
||||
|
||||
/* Number of chances to calibrate a case before giving up: */
|
||||
|
||||
#define CAL_CHANCES 3
|
||||
|
||||
/* Map size for the traced binary (2^MAP_SIZE_POW2). Must be greater than
|
||||
2; you probably want to keep it under 18 or so for performance reasons
|
||||
(adjusting AFL_INST_RATIO when compiling is probably a better way to solve
|
||||
problems with complex programs). You need to recompile the target binary
|
||||
after changing this - otherwise, SEGVs may ensue. */
|
||||
|
||||
#define MAP_SIZE_POW2 16
|
||||
#define MAP_SIZE (1 << MAP_SIZE_POW2)
|
||||
|
||||
/* Maximum allocator request size (keep well under INT_MAX): */
|
||||
|
||||
#define MAX_ALLOC 0x40000000
|
||||
|
||||
/* A made-up hashing seed: */
|
||||
|
||||
#define HASH_CONST 0xa5b35705
|
||||
|
||||
/* Constants for afl-gotcpu to control busy loop timing: */
|
||||
|
||||
#define CTEST_TARGET_MS 5000
|
||||
#define CTEST_CORE_TRG_MS 1000
|
||||
#define CTEST_BUSY_CYCLES (10 * 1000 * 1000)
|
||||
|
||||
/* Enable NeverZero counters in QEMU mode */
|
||||
|
||||
#define AFL_QEMU_NOT_ZERO
|
||||
|
||||
/* Uncomment this to use inferior block-coverage-based instrumentation. Note
|
||||
that you need to recompile the target binary for this to have any effect: */
|
||||
|
||||
// #define COVERAGE_ONLY
|
||||
|
||||
/* Uncomment this to ignore hit counts and output just one bit per tuple.
|
||||
As with the previous setting, you will need to recompile the target
|
||||
binary: */
|
||||
|
||||
// #define SKIP_COUNTS
|
||||
|
||||
/* Uncomment this to use instrumentation data to record newly discovered paths,
|
||||
but do not use them as seeds for fuzzing. This is useful for conveniently
|
||||
measuring coverage that could be attained by a "dumb" fuzzing algorithm: */
|
||||
|
||||
// #define IGNORE_FINDS
|
||||
|
||||
#endif /* ! _HAVE_CONFIG_H */
|
||||
|
290
include/debug.h
Normal file
290
include/debug.h
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
american fuzzy lop++ - debug / error handling macros
|
||||
----------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _HAVE_DEBUG_H
|
||||
#define _HAVE_DEBUG_H
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
/*******************
|
||||
* Terminal colors *
|
||||
*******************/
|
||||
|
||||
#ifdef USE_COLOR
|
||||
|
||||
#define cBLK "\x1b[0;30m"
|
||||
#define cRED "\x1b[0;31m"
|
||||
#define cGRN "\x1b[0;32m"
|
||||
#define cBRN "\x1b[0;33m"
|
||||
#define cBLU "\x1b[0;34m"
|
||||
#define cMGN "\x1b[0;35m"
|
||||
#define cCYA "\x1b[0;36m"
|
||||
#define cLGR "\x1b[0;37m"
|
||||
#define cGRA "\x1b[1;90m"
|
||||
#define cLRD "\x1b[1;91m"
|
||||
#define cLGN "\x1b[1;92m"
|
||||
#define cYEL "\x1b[1;93m"
|
||||
#define cLBL "\x1b[1;94m"
|
||||
#define cPIN "\x1b[1;95m"
|
||||
#define cLCY "\x1b[1;96m"
|
||||
#define cBRI "\x1b[1;97m"
|
||||
#define cRST "\x1b[0m"
|
||||
|
||||
#define bgBLK "\x1b[40m"
|
||||
#define bgRED "\x1b[41m"
|
||||
#define bgGRN "\x1b[42m"
|
||||
#define bgBRN "\x1b[43m"
|
||||
#define bgBLU "\x1b[44m"
|
||||
#define bgMGN "\x1b[45m"
|
||||
#define bgCYA "\x1b[46m"
|
||||
#define bgLGR "\x1b[47m"
|
||||
#define bgGRA "\x1b[100m"
|
||||
#define bgLRD "\x1b[101m"
|
||||
#define bgLGN "\x1b[102m"
|
||||
#define bgYEL "\x1b[103m"
|
||||
#define bgLBL "\x1b[104m"
|
||||
#define bgPIN "\x1b[105m"
|
||||
#define bgLCY "\x1b[106m"
|
||||
#define bgBRI "\x1b[107m"
|
||||
|
||||
#else
|
||||
|
||||
#define cBLK ""
|
||||
#define cRED ""
|
||||
#define cGRN ""
|
||||
#define cBRN ""
|
||||
#define cBLU ""
|
||||
#define cMGN ""
|
||||
#define cCYA ""
|
||||
#define cLGR ""
|
||||
#define cGRA ""
|
||||
#define cLRD ""
|
||||
#define cLGN ""
|
||||
#define cYEL ""
|
||||
#define cLBL ""
|
||||
#define cPIN ""
|
||||
#define cLCY ""
|
||||
#define cBRI ""
|
||||
#define cRST ""
|
||||
|
||||
#define bgBLK ""
|
||||
#define bgRED ""
|
||||
#define bgGRN ""
|
||||
#define bgBRN ""
|
||||
#define bgBLU ""
|
||||
#define bgMGN ""
|
||||
#define bgCYA ""
|
||||
#define bgLGR ""
|
||||
#define bgGRA ""
|
||||
#define bgLRD ""
|
||||
#define bgLGN ""
|
||||
#define bgYEL ""
|
||||
#define bgLBL ""
|
||||
#define bgPIN ""
|
||||
#define bgLCY ""
|
||||
#define bgBRI ""
|
||||
|
||||
#endif /* ^USE_COLOR */
|
||||
|
||||
/*************************
|
||||
* Box drawing sequences *
|
||||
*************************/
|
||||
|
||||
#ifdef FANCY_BOXES
|
||||
|
||||
#define SET_G1 "\x1b)0" /* Set G1 for box drawing */
|
||||
#define RESET_G1 "\x1b)B" /* Reset G1 to ASCII */
|
||||
#define bSTART "\x0e" /* Enter G1 drawing mode */
|
||||
#define bSTOP "\x0f" /* Leave G1 drawing mode */
|
||||
#define bH "q" /* Horizontal line */
|
||||
#define bV "x" /* Vertical line */
|
||||
#define bLT "l" /* Left top corner */
|
||||
#define bRT "k" /* Right top corner */
|
||||
#define bLB "m" /* Left bottom corner */
|
||||
#define bRB "j" /* Right bottom corner */
|
||||
#define bX "n" /* Cross */
|
||||
#define bVR "t" /* Vertical, branch right */
|
||||
#define bVL "u" /* Vertical, branch left */
|
||||
#define bHT "v" /* Horizontal, branch top */
|
||||
#define bHB "w" /* Horizontal, branch bottom */
|
||||
|
||||
#else
|
||||
|
||||
#define SET_G1 ""
|
||||
#define RESET_G1 ""
|
||||
#define bSTART ""
|
||||
#define bSTOP ""
|
||||
#define bH "-"
|
||||
#define bV "|"
|
||||
#define bLT "+"
|
||||
#define bRT "+"
|
||||
#define bLB "+"
|
||||
#define bRB "+"
|
||||
#define bX "+"
|
||||
#define bVR "+"
|
||||
#define bVL "+"
|
||||
#define bHT "+"
|
||||
#define bHB "+"
|
||||
|
||||
#endif /* ^FANCY_BOXES */
|
||||
|
||||
/***********************
|
||||
* Misc terminal codes *
|
||||
***********************/
|
||||
|
||||
#define TERM_HOME "\x1b[H"
|
||||
#define TERM_CLEAR TERM_HOME "\x1b[2J"
|
||||
#define cEOL "\x1b[0K"
|
||||
#define CURSOR_HIDE "\x1b[?25l"
|
||||
#define CURSOR_SHOW "\x1b[?25h"
|
||||
|
||||
/************************
|
||||
* Debug & error macros *
|
||||
************************/
|
||||
|
||||
/* Just print stuff to the appropriate stream. */
|
||||
|
||||
#ifdef MESSAGES_TO_STDOUT
|
||||
#define SAYF(x...) printf(x)
|
||||
#else
|
||||
#define SAYF(x...) fprintf(stderr, x)
|
||||
#endif /* ^MESSAGES_TO_STDOUT */
|
||||
|
||||
/* Show a prefixed warning. */
|
||||
|
||||
#define WARNF(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(cYEL "[!] " cBRI "WARNING: " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Show a prefixed "doing something" message. */
|
||||
|
||||
#define ACTF(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(cLBL "[*] " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Show a prefixed "success" message. */
|
||||
|
||||
#define OKF(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(cLGN "[+] " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Show a prefixed fatal error message (not used in afl). */
|
||||
|
||||
#define BADF(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(cLRD "\n[-] " cRST x); \
|
||||
SAYF(cRST "\n"); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die with a verbose non-OS fatal error message. */
|
||||
|
||||
#define FATAL(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] PROGRAM ABORT : " cRST x); \
|
||||
SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __FUNCTION__, \
|
||||
__FILE__, __LINE__); \
|
||||
exit(1); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die by calling abort() to provide a core dump. */
|
||||
|
||||
#define ABORT(x...) \
|
||||
do { \
|
||||
\
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] PROGRAM ABORT : " cRST x); \
|
||||
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __FUNCTION__, \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die while also including the output of perror(). */
|
||||
|
||||
#define PFATAL(x...) \
|
||||
do { \
|
||||
\
|
||||
fflush(stdout); \
|
||||
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \
|
||||
"\n[-] SYSTEM ERROR : " cRST x); \
|
||||
SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __FUNCTION__, \
|
||||
__FILE__, __LINE__); \
|
||||
SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \
|
||||
exit(1); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Die with FAULT() or PFAULT() depending on the value of res (used to
|
||||
interpret different failure modes for read(), write(), etc). */
|
||||
|
||||
#define RPFATAL(res, x...) \
|
||||
do { \
|
||||
\
|
||||
if (res < 0) \
|
||||
PFATAL(x); \
|
||||
else \
|
||||
FATAL(x); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Error-checking versions of read() and write() that call RPFATAL() as
|
||||
appropriate. */
|
||||
|
||||
#define ck_write(fd, buf, len, fn) \
|
||||
do { \
|
||||
\
|
||||
u32 _len = (len); \
|
||||
s32 _res = write(fd, buf, _len); \
|
||||
if (_res != _len) RPFATAL(_res, "Short write to %s", fn); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define ck_read(fd, buf, len, fn) \
|
||||
do { \
|
||||
\
|
||||
u32 _len = (len); \
|
||||
s32 _res = read(fd, buf, _len); \
|
||||
if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#endif /* ! _HAVE_DEBUG_H */
|
||||
|
51
include/forkserver.h
Normal file
51
include/forkserver.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
american fuzzy lop++ - forkserver header
|
||||
----------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
Shared code that implements a forkserver. This is used by the fuzzer
|
||||
as well the other components like afl-tmin.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __AFL_FORKSERVER_H
|
||||
#define __AFL_FORKSERVER_H
|
||||
|
||||
void handle_timeout(int sig);
|
||||
void init_forkserver(char **argv);
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define MSG_FORK_ON_APPLE \
|
||||
" - On MacOS X, the semantics of fork() syscalls are non-standard and " \
|
||||
"may\n" \
|
||||
" break afl-fuzz performance optimizations when running " \
|
||||
"platform-specific\n" \
|
||||
" targets. To fix this, set AFL_NO_FORKSRV=1 in the environment.\n\n"
|
||||
#else
|
||||
#define MSG_FORK_ON_APPLE ""
|
||||
#endif
|
||||
|
||||
#ifdef RLIMIT_AS
|
||||
#define MSG_ULIMIT_USAGE " ( ulimit -Sv $[%llu << 10];"
|
||||
#else
|
||||
#define MSG_ULIMIT_USAGE " ( ulimit -Sd $[%llu << 10];"
|
||||
#endif /* ^RLIMIT_AS */
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
american fuzzy lop - hashing function
|
||||
-------------------------------------
|
||||
american fuzzy lop++ - hashing function
|
||||
---------------------------------------
|
||||
|
||||
The hash32() function is a variant of MurmurHash3, a good
|
||||
non-cryptosafe hashing function developed by Austin Appleby.
|
||||
@ -31,12 +31,12 @@
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define ROL64(_x, _r) ((((u64)(_x)) << (_r)) | (((u64)(_x)) >> (64 - (_r))))
|
||||
#define ROL64(_x, _r) ((((u64)(_x)) << (_r)) | (((u64)(_x)) >> (64 - (_r))))
|
||||
|
||||
static inline u32 hash32(const void* key, u32 len, u32 seed) {
|
||||
|
||||
const u64* data = (u64*)key;
|
||||
u64 h1 = seed ^ len;
|
||||
u64 h1 = seed ^ len;
|
||||
|
||||
len >>= 3;
|
||||
|
||||
@ -45,12 +45,12 @@ static inline u32 hash32(const void* key, u32 len, u32 seed) {
|
||||
u64 k1 = *data++;
|
||||
|
||||
k1 *= 0x87c37b91114253d5ULL;
|
||||
k1 = ROL64(k1, 31);
|
||||
k1 = ROL64(k1, 31);
|
||||
k1 *= 0x4cf5ad432745937fULL;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = ROL64(h1, 27);
|
||||
h1 = h1 * 5 + 0x52dce729;
|
||||
h1 = ROL64(h1, 27);
|
||||
h1 = h1 * 5 + 0x52dce729;
|
||||
|
||||
}
|
||||
|
||||
@ -64,14 +64,14 @@ static inline u32 hash32(const void* key, u32 len, u32 seed) {
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#else
|
||||
|
||||
#define ROL32(_x, _r) ((((u32)(_x)) << (_r)) | (((u32)(_x)) >> (32 - (_r))))
|
||||
#define ROL32(_x, _r) ((((u32)(_x)) << (_r)) | (((u32)(_x)) >> (32 - (_r))))
|
||||
|
||||
static inline u32 hash32(const void* key, u32 len, u32 seed) {
|
||||
|
||||
const u32* data = (u32*)key;
|
||||
u32 h1 = seed ^ len;
|
||||
const u32* data = (u32*)key;
|
||||
u32 h1 = seed ^ len;
|
||||
|
||||
len >>= 2;
|
||||
|
||||
@ -80,12 +80,12 @@ static inline u32 hash32(const void* key, u32 len, u32 seed) {
|
||||
u32 k1 = *data++;
|
||||
|
||||
k1 *= 0xcc9e2d51;
|
||||
k1 = ROL32(k1, 15);
|
||||
k1 = ROL32(k1, 15);
|
||||
k1 *= 0x1b873593;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = ROL32(h1, 13);
|
||||
h1 = h1 * 5 + 0xe6546b64;
|
||||
h1 = ROL32(h1, 13);
|
||||
h1 = h1 * 5 + 0xe6546b64;
|
||||
|
||||
}
|
||||
|
||||
@ -99,6 +99,7 @@ static inline u32 hash32(const void* key, u32 len, u32 seed) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* ^__x86_64__ */
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
#endif /* !_HAVE_HASH_H */
|
||||
|
||||
#endif /* !_HAVE_HASH_H */
|
34
include/sharedmem.h
Normal file
34
include/sharedmem.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
american fuzzy lop++ - shared memory related header
|
||||
---------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
Shared code to handle the shared memory. This is used by the fuzzer
|
||||
as well the other components like afl-tmin, afl-showmap, etc...
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __AFL_SHAREDMEM_H
|
||||
#define __AFL_SHAREDMEM_H
|
||||
|
||||
void setup_shm(unsigned char dumb_mode);
|
||||
void remove_shm(void);
|
||||
|
||||
#endif
|
||||
|
101
include/types.h
Normal file
101
include/types.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
american fuzzy lop++ - type definitions and minor macros
|
||||
--------------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _HAVE_TYPES_H
|
||||
#define _HAVE_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
|
||||
/*
|
||||
|
||||
Ugh. There is an unintended compiler / glibc #include glitch caused by
|
||||
combining the u64 type an %llu in format strings, necessitating a workaround.
|
||||
|
||||
In essence, the compiler is always looking for 'unsigned long long' for %llu.
|
||||
On 32-bit systems, the u64 type (aliased to uint64_t) is expanded to
|
||||
'unsigned long long' in <bits/types.h>, so everything checks out.
|
||||
|
||||
But on 64-bit systems, it is #ifdef'ed in the same file as 'unsigned long'.
|
||||
Now, it only happens in circumstances where the type happens to have the
|
||||
expected bit width, *but* the compiler does not know that... and complains
|
||||
about 'unsigned long' being unsafe to pass to %llu.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __x86_64__
|
||||
typedef unsigned long long u64;
|
||||
#else
|
||||
typedef uint64_t u64;
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(_a, _b) ((_a) > (_b) ? (_b) : (_a))
|
||||
#define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
|
||||
#endif /* !MIN */
|
||||
|
||||
#define SWAP16(_x) \
|
||||
({ \
|
||||
\
|
||||
u16 _ret = (_x); \
|
||||
(u16)((_ret << 8) | (_ret >> 8)); \
|
||||
\
|
||||
})
|
||||
|
||||
#define SWAP32(_x) \
|
||||
({ \
|
||||
\
|
||||
u32 _ret = (_x); \
|
||||
(u32)((_ret << 24) | (_ret >> 24) | ((_ret << 8) & 0x00FF0000) | \
|
||||
((_ret >> 8) & 0x0000FF00)); \
|
||||
\
|
||||
})
|
||||
|
||||
#ifdef AFL_LLVM_PASS
|
||||
#define AFL_R(x) (random() % (x))
|
||||
#else
|
||||
#define R(x) (random() % (x))
|
||||
#endif /* ^AFL_LLVM_PASS */
|
||||
|
||||
#define STRINGIFY_INTERNAL(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
|
||||
|
||||
#define MEM_BARRIER() __asm__ volatile("" ::: "memory")
|
||||
|
||||
#if __GNUC__ < 6
|
||||
#define likely(_x) (_x)
|
||||
#define unlikely(_x) (_x)
|
||||
#else
|
||||
#define likely(_x) __builtin_expect(!!(_x), 1)
|
||||
#define unlikely(_x) __builtin_expect(!!(_x), 0)
|
||||
#endif
|
||||
|
||||
#endif /* ! _HAVE_TYPES_H */
|
||||
|
@ -18,7 +18,7 @@ HELPER_PATH = $(PREFIX)/lib/afl
|
||||
|
||||
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops
|
||||
CFLAGS ?= -O3 -funroll-loops -I ../include/
|
||||
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
|
||||
|
||||
all: libdislocator.so
|
||||
|
@ -1,6 +1,4 @@
|
||||
===================================
|
||||
libdislocator, an abusive allocator
|
||||
===================================
|
||||
# libdislocator, an abusive allocator
|
||||
|
||||
(See ../docs/README for the general instruction manual.)
|
||||
|
||||
@ -45,7 +43,9 @@ when fuzzing small, self-contained binaries.
|
||||
|
||||
To use this library, run AFL like so:
|
||||
|
||||
```
|
||||
AFL_PRELOAD=/path/to/libdislocator.so ./afl-fuzz [...other params...]
|
||||
```
|
||||
|
||||
You *have* to specify path, even if it's just ./libdislocator.so or
|
||||
$PWD/libdislocator.so.
|
@ -25,36 +25,48 @@
|
||||
#include <limits.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "../config.h"
|
||||
#include "../types.h"
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
|
||||
#ifndef PAGE_SIZE
|
||||
# define PAGE_SIZE 4096
|
||||
#endif /* !PAGE_SIZE */
|
||||
#define PAGE_SIZE 4096
|
||||
#endif /* !PAGE_SIZE */
|
||||
|
||||
#ifndef MAP_ANONYMOUS
|
||||
# define MAP_ANONYMOUS MAP_ANON
|
||||
#endif /* !MAP_ANONYMOUS */
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#endif /* !MAP_ANONYMOUS */
|
||||
|
||||
/* Error / message handling: */
|
||||
|
||||
#define DEBUGF(_x...) do { \
|
||||
if (alloc_verbose) { \
|
||||
if (++call_depth == 1) { \
|
||||
#define DEBUGF(_x...) \
|
||||
do { \
|
||||
\
|
||||
if (alloc_verbose) { \
|
||||
\
|
||||
if (++call_depth == 1) { \
|
||||
\
|
||||
fprintf(stderr, "[AFL] " _x); \
|
||||
fprintf(stderr, "\n"); \
|
||||
} \
|
||||
call_depth--; \
|
||||
} \
|
||||
fprintf(stderr, "\n"); \
|
||||
\
|
||||
} \
|
||||
call_depth--; \
|
||||
\
|
||||
} \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
#define FATAL(_x...) do { \
|
||||
if (++call_depth == 1) { \
|
||||
#define FATAL(_x...) \
|
||||
do { \
|
||||
\
|
||||
if (++call_depth == 1) { \
|
||||
\
|
||||
fprintf(stderr, "*** [AFL] " _x); \
|
||||
fprintf(stderr, " ***\n"); \
|
||||
abort(); \
|
||||
} \
|
||||
call_depth--; \
|
||||
fprintf(stderr, " ***\n"); \
|
||||
abort(); \
|
||||
\
|
||||
} \
|
||||
call_depth--; \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* Macro to count the number of pages needed to store a buffer: */
|
||||
@ -63,7 +75,7 @@
|
||||
|
||||
/* Canary & clobber bytes: */
|
||||
|
||||
#define ALLOC_CANARY 0xAACCAACC
|
||||
#define ALLOC_CANARY 0xAACCAACC
|
||||
#define ALLOC_CLOBBER 0xCC
|
||||
|
||||
#define PTR_C(_p) (((u32*)(_p))[-1])
|
||||
@ -73,14 +85,13 @@
|
||||
|
||||
static u32 max_mem = MAX_ALLOC; /* Max heap usage to permit */
|
||||
static u8 alloc_verbose, /* Additional debug messages */
|
||||
hard_fail, /* abort() when max_mem exceeded? */
|
||||
no_calloc_over; /* abort() on calloc() overflows? */
|
||||
hard_fail, /* abort() when max_mem exceeded? */
|
||||
no_calloc_over; /* abort() on calloc() overflows? */
|
||||
|
||||
static __thread size_t total_mem; /* Currently allocated mem */
|
||||
|
||||
static __thread u32 call_depth; /* To avoid recursion via fprintf() */
|
||||
|
||||
|
||||
/* This is the main alloc function. It allocates one page more than necessary,
|
||||
sets that tailing page to PROT_NONE, and then increments the return address
|
||||
so that it is right-aligned to that boundary. Since it always uses mmap(),
|
||||
@ -90,14 +101,11 @@ static void* __dislocator_alloc(size_t len) {
|
||||
|
||||
void* ret;
|
||||
|
||||
|
||||
if (total_mem + len > max_mem || total_mem + len < total_mem) {
|
||||
|
||||
if (hard_fail)
|
||||
FATAL("total allocs exceed %u MB", max_mem / 1024 / 1024);
|
||||
if (hard_fail) FATAL("total allocs exceed %u MB", max_mem / 1024 / 1024);
|
||||
|
||||
DEBUGF("total allocs exceed %u MB, returning NULL",
|
||||
max_mem / 1024 / 1024);
|
||||
DEBUGF("total allocs exceed %u MB, returning NULL", max_mem / 1024 / 1024);
|
||||
|
||||
return NULL;
|
||||
|
||||
@ -142,7 +150,6 @@ static void* __dislocator_alloc(size_t len) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* The "user-facing" wrapper for calloc(). This just checks for overflows and
|
||||
displays debug messages if requested. */
|
||||
|
||||
@ -157,8 +164,11 @@ void* calloc(size_t elem_len, size_t elem_cnt) {
|
||||
if (elem_cnt && len / elem_cnt != elem_len) {
|
||||
|
||||
if (no_calloc_over) {
|
||||
DEBUGF("calloc(%zu, %zu) would overflow, returning NULL", elem_len, elem_cnt);
|
||||
|
||||
DEBUGF("calloc(%zu, %zu) would overflow, returning NULL", elem_len,
|
||||
elem_cnt);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
FATAL("calloc(%zu, %zu) would overflow", elem_len, elem_cnt);
|
||||
@ -174,7 +184,6 @@ void* calloc(size_t elem_len, size_t elem_cnt) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* The wrapper for malloc(). Roughly the same, also clobbers the returned
|
||||
memory (unlike calloc(), malloc() is not guaranteed to return zeroed
|
||||
memory). */
|
||||
@ -193,7 +202,6 @@ void* malloc(size_t len) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* The wrapper for free(). This simply marks the entire region as PROT_NONE.
|
||||
If the region is already freed, the code will segfault during the attempt to
|
||||
read the canary. Not very graceful, but works, right? */
|
||||
@ -224,7 +232,6 @@ void free(void* ptr) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Realloc is pretty straightforward, too. We forcibly reallocate the buffer,
|
||||
move data, and then free (aka mprotect()) the original one. */
|
||||
|
||||
@ -249,7 +256,6 @@ void* realloc(void* ptr, size_t len) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
__attribute__((constructor)) void __dislocator_init(void) {
|
||||
|
||||
u8* tmp = getenv("AFL_LD_LIMIT_MB");
|
||||
@ -266,3 +272,4 @@ __attribute__((constructor)) void __dislocator_init(void) {
|
||||
no_calloc_over = !!getenv("AFL_LD_NO_CALLOC_OVER");
|
||||
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ HELPER_PATH = $(PREFIX)/lib/afl
|
||||
|
||||
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops
|
||||
CFLAGS ?= -O3 -funroll-loops -I ../include/
|
||||
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
|
||||
|
||||
all: libtokencap.so
|
||||
|
@ -1,10 +1,8 @@
|
||||
=========================================
|
||||
strcmp() / memcmp() token capture library
|
||||
=========================================
|
||||
# strcmp() / memcmp() token capture library
|
||||
|
||||
(See ../docs/README for the general instruction manual.)
|
||||
|
||||
This Linux-only companion library allows you to instrument strcmp(), memcmp(),
|
||||
This Linux-only companion library allows you to instrument `strcmp()`, `memcmp()`,
|
||||
and related functions to automatically extract syntax tokens passed to any of
|
||||
these libcalls. The resulting list of tokens may be then given as a starting
|
||||
dictionary to afl-fuzz (the -x option) to improve coverage on subsequent
|
||||
@ -31,15 +29,18 @@ with -fno-builtin and is linked dynamically. If you wish to automate the first
|
||||
part without mucking with CFLAGS in Makefiles, you can set AFL_NO_BUILTIN=1
|
||||
when using afl-gcc. This setting specifically adds the following flags:
|
||||
|
||||
```
|
||||
-fno-builtin-strcmp -fno-builtin-strncmp -fno-builtin-strcasecmp
|
||||
-fno-builtin-strcasencmp -fno-builtin-memcmp -fno-builtin-strstr
|
||||
-fno-builtin-strcasestr
|
||||
```
|
||||
|
||||
The next step is simply loading this library via LD_PRELOAD. The optimal usage
|
||||
pattern is to allow afl-fuzz to fuzz normally for a while and build up a corpus,
|
||||
and then fire off the target binary, with libtokencap.so loaded, on every file
|
||||
found by AFL in that earlier run. This demonstrates the basic principle:
|
||||
|
||||
```
|
||||
export AFL_TOKEN_FILE=$PWD/temp_output.txt
|
||||
|
||||
for i in <out_dir>/queue/id*; do
|
||||
@ -48,6 +49,7 @@ found by AFL in that earlier run. This demonstrates the basic principle:
|
||||
done
|
||||
|
||||
sort -u temp_output.txt >afl_dictionary.txt
|
||||
```
|
||||
|
||||
If you don't get any results, the target library is probably not using strcmp()
|
||||
and memcmp() to parse input; or you haven't compiled it with -fno-builtin; or
|
||||
@ -55,7 +57,7 @@ the whole thing isn't dynamically linked, and LD_PRELOAD is having no effect.
|
||||
|
||||
PS. The library is Linux-only because there is probably no particularly portable
|
||||
and non-invasive way to distinguish between read-only and read-write memory
|
||||
mappings. The __tokencap_load_mappings() function is the only thing that would
|
||||
mappings. The `__tokencap_load_mappings()` function is the only thing that would
|
||||
need to be changed for other OSes. Porting to platforms with /proc/<pid>/maps
|
||||
(e.g., FreeBSD) should be trivial.
|
||||
|
@ -27,30 +27,26 @@
|
||||
#include "../config.h"
|
||||
|
||||
#ifndef __linux__
|
||||
# error "Sorry, this library is Linux-specific for now!"
|
||||
#endif /* !__linux__ */
|
||||
|
||||
#error "Sorry, this library is Linux-specific for now!"
|
||||
#endif /* !__linux__ */
|
||||
|
||||
/* Mapping data and such */
|
||||
|
||||
#define MAX_MAPPINGS 1024
|
||||
|
||||
static struct mapping {
|
||||
void *st, *en;
|
||||
} __tokencap_ro[MAX_MAPPINGS];
|
||||
static struct mapping { void *st, *en; } __tokencap_ro[MAX_MAPPINGS];
|
||||
|
||||
static u32 __tokencap_ro_cnt;
|
||||
static u8 __tokencap_ro_loaded;
|
||||
static FILE* __tokencap_out_file;
|
||||
|
||||
|
||||
/* Identify read-only regions in memory. Only parameters that fall into these
|
||||
ranges are worth dumping when passed to strcmp() and so on. Read-write
|
||||
regions are far more likely to contain user input instead. */
|
||||
|
||||
static void __tokencap_load_mappings(void) {
|
||||
|
||||
u8 buf[MAX_LINE];
|
||||
u8 buf[MAX_LINE];
|
||||
FILE* f = fopen("/proc/self/maps", "r");
|
||||
|
||||
__tokencap_ro_loaded = 1;
|
||||
@ -59,8 +55,8 @@ static void __tokencap_load_mappings(void) {
|
||||
|
||||
while (fgets(buf, MAX_LINE, f)) {
|
||||
|
||||
u8 rf, wf;
|
||||
void* st, *en;
|
||||
u8 rf, wf;
|
||||
void *st, *en;
|
||||
|
||||
if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue;
|
||||
if (wf == 'w' || rf != 'r') continue;
|
||||
@ -76,7 +72,6 @@ static void __tokencap_load_mappings(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Check an address against the list of read-only mappings. */
|
||||
|
||||
static u8 __tokencap_is_ro(const void* ptr) {
|
||||
@ -85,20 +80,19 @@ static u8 __tokencap_is_ro(const void* ptr) {
|
||||
|
||||
if (!__tokencap_ro_loaded) __tokencap_load_mappings();
|
||||
|
||||
for (i = 0; i < __tokencap_ro_cnt; i++)
|
||||
for (i = 0; i < __tokencap_ro_cnt; i++)
|
||||
if (ptr >= __tokencap_ro[i].st && ptr <= __tokencap_ro[i].en) return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Dump an interesting token to output file, quoting and escaping it
|
||||
properly. */
|
||||
|
||||
static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
|
||||
|
||||
u8 buf[MAX_AUTO_EXTRA * 4 + 1];
|
||||
u8 buf[MAX_AUTO_EXTRA * 4 + 1];
|
||||
u32 i;
|
||||
u32 pos = 0;
|
||||
|
||||
@ -120,9 +114,7 @@ static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
|
||||
pos += 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
buf[pos++] = ptr[i];
|
||||
default: buf[pos++] = ptr[i];
|
||||
|
||||
}
|
||||
|
||||
@ -130,11 +122,10 @@ static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
|
||||
|
||||
buf[pos] = 0;
|
||||
|
||||
fprintf(__tokencap_out_file, "\"%s\"\n", buf);
|
||||
fprintf(__tokencap_out_file, "\"%s\"\n", buf);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Replacements for strcmp(), memcmp(), and so on. Note that these will be used
|
||||
only if the target is compiled with -fno-builtins and linked dynamically. */
|
||||
|
||||
@ -151,13 +142,13 @@ int strcmp(const char* str1, const char* str2) {
|
||||
|
||||
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
||||
if (!c1) return 0;
|
||||
str1++; str2++;
|
||||
str1++;
|
||||
str2++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#undef strncmp
|
||||
|
||||
int strncmp(const char* str1, const char* str2, size_t len) {
|
||||
@ -171,7 +162,8 @@ int strncmp(const char* str1, const char* str2, size_t len) {
|
||||
|
||||
if (!c1) return 0;
|
||||
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
||||
str1++; str2++;
|
||||
str1++;
|
||||
str2++;
|
||||
|
||||
}
|
||||
|
||||
@ -179,7 +171,6 @@ int strncmp(const char* str1, const char* str2, size_t len) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
#undef strcasecmp
|
||||
|
||||
int strcasecmp(const char* str1, const char* str2) {
|
||||
@ -193,13 +184,13 @@ int strcasecmp(const char* str1, const char* str2) {
|
||||
|
||||
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
||||
if (!c1) return 0;
|
||||
str1++; str2++;
|
||||
str1++;
|
||||
str2++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#undef strncasecmp
|
||||
|
||||
int strncasecmp(const char* str1, const char* str2, size_t len) {
|
||||
@ -213,7 +204,8 @@ int strncasecmp(const char* str1, const char* str2, size_t len) {
|
||||
|
||||
if (!c1) return 0;
|
||||
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
||||
str1++; str2++;
|
||||
str1++;
|
||||
str2++;
|
||||
|
||||
}
|
||||
|
||||
@ -221,7 +213,6 @@ int strncasecmp(const char* str1, const char* str2, size_t len) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
#undef memcmp
|
||||
|
||||
int memcmp(const void* mem1, const void* mem2, size_t len) {
|
||||
@ -233,7 +224,8 @@ int memcmp(const void* mem1, const void* mem2, size_t len) {
|
||||
|
||||
unsigned char c1 = *(const char*)mem1, c2 = *(const char*)mem2;
|
||||
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
||||
mem1++; mem2++;
|
||||
mem1++;
|
||||
mem2++;
|
||||
|
||||
}
|
||||
|
||||
@ -241,7 +233,6 @@ int memcmp(const void* mem1, const void* mem2, size_t len) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
#undef strstr
|
||||
|
||||
char* strstr(const char* haystack, const char* needle) {
|
||||
@ -249,16 +240,17 @@ char* strstr(const char* haystack, const char* needle) {
|
||||
if (__tokencap_is_ro(haystack))
|
||||
__tokencap_dump(haystack, strlen(haystack), 1);
|
||||
|
||||
if (__tokencap_is_ro(needle))
|
||||
__tokencap_dump(needle, strlen(needle), 1);
|
||||
if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1);
|
||||
|
||||
do {
|
||||
|
||||
const char* n = needle;
|
||||
const char* h = haystack;
|
||||
|
||||
while(*n && *h && *n == *h) n++, h++;
|
||||
while (*n && *h && *n == *h)
|
||||
n++, h++;
|
||||
|
||||
if(!*n) return (char*)haystack;
|
||||
if (!*n) return (char*)haystack;
|
||||
|
||||
} while (*(haystack++));
|
||||
|
||||
@ -266,7 +258,6 @@ char* strstr(const char* haystack, const char* needle) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
#undef strcasestr
|
||||
|
||||
char* strcasestr(const char* haystack, const char* needle) {
|
||||
@ -274,25 +265,24 @@ char* strcasestr(const char* haystack, const char* needle) {
|
||||
if (__tokencap_is_ro(haystack))
|
||||
__tokencap_dump(haystack, strlen(haystack), 1);
|
||||
|
||||
if (__tokencap_is_ro(needle))
|
||||
__tokencap_dump(needle, strlen(needle), 1);
|
||||
if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1);
|
||||
|
||||
do {
|
||||
|
||||
const char* n = needle;
|
||||
const char* h = haystack;
|
||||
|
||||
while(*n && *h && tolower(*n) == tolower(*h)) n++, h++;
|
||||
while (*n && *h && tolower(*n) == tolower(*h))
|
||||
n++, h++;
|
||||
|
||||
if(!*n) return (char*)haystack;
|
||||
if (!*n) return (char*)haystack;
|
||||
|
||||
} while(*(haystack++));
|
||||
} while (*(haystack++));
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Init code to open the output file (or default to stderr). */
|
||||
|
||||
__attribute__((constructor)) void __tokencap_init(void) {
|
||||
|
@ -24,8 +24,8 @@
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include "../config.h"
|
||||
#include "../debug.h"
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "MarkNodes.h"
|
||||
|
||||
@ -37,267 +37,349 @@ static cl::opt<bool> LoopHeadOpt("loophead", cl::desc("LoopHead"),
|
||||
cl::init(false));
|
||||
|
||||
namespace {
|
||||
struct InsTrim : public ModulePass {
|
||||
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
struct InsTrim : public ModulePass {
|
||||
|
||||
private:
|
||||
std::mt19937 generator;
|
||||
int total_instr = 0;
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
|
||||
unsigned int genLabel() {
|
||||
return generator() & (MAP_SIZE - 1);
|
||||
}
|
||||
private:
|
||||
std::mt19937 generator;
|
||||
int total_instr = 0;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
InsTrim() : ModulePass(ID), generator(0) {
|
||||
char* instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instWhiteListFilename) {
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instWhiteListFilename);
|
||||
if (!fileStream)
|
||||
report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
||||
unsigned int genLabel() {
|
||||
|
||||
return generator() & (MAP_SIZE - 1);
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
InsTrim() : ModulePass(ID), generator(0) {
|
||||
|
||||
char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instWhiteListFilename) {
|
||||
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instWhiteListFilename);
|
||||
if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
||||
getline(fileStream, line);
|
||||
while (fileStream) {
|
||||
|
||||
myWhitelist.push_back(line);
|
||||
getline(fileStream, line);
|
||||
while (fileStream) {
|
||||
myWhitelist.push_back(line);
|
||||
getline(fileStream, line);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
AU.addRequired<DominatorTreeWrapperPass>();
|
||||
}
|
||||
}
|
||||
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||
|
||||
AU.addRequired<DominatorTreeWrapperPass>();
|
||||
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 4
|
||||
const char *
|
||||
const char *
|
||||
#else
|
||||
StringRef
|
||||
StringRef
|
||||
#endif
|
||||
getPassName() const override {
|
||||
return "InstTrim Instrumentation";
|
||||
getPassName() const override {
|
||||
|
||||
return "InstTrim Instrumentation";
|
||||
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override {
|
||||
|
||||
char be_quiet = 0;
|
||||
|
||||
if (isatty(2) && !getenv("AFL_QUIET")) {
|
||||
|
||||
SAYF(cCYA "LLVMInsTrim" VERSION cRST " by csienslab\n");
|
||||
|
||||
} else
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
char *neverZero_counters_str;
|
||||
if ((neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO")) != NULL)
|
||||
OKF("LLVM neverZero activated (by hexcoder)\n");
|
||||
#endif
|
||||
|
||||
if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL ||
|
||||
getenv("LOOPHEAD") != NULL) {
|
||||
|
||||
LoopHeadOpt = true;
|
||||
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override {
|
||||
char be_quiet = 0;
|
||||
|
||||
if (isatty(2) && !getenv("AFL_QUIET")) {
|
||||
SAYF(cCYA "LLVMInsTrim" VERSION cRST " by csienslab\n");
|
||||
} else be_quiet = 1;
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
char* neverZero_counters_str;
|
||||
if ((neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO")) != NULL)
|
||||
OKF("LLVM neverZero activated (by hexcoder)\n");
|
||||
#endif
|
||||
|
||||
if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL || getenv("LOOPHEAD") != NULL) {
|
||||
LoopHeadOpt = true;
|
||||
}
|
||||
// this is our default
|
||||
MarkSetOpt = true;
|
||||
|
||||
// this is our default
|
||||
MarkSetOpt = true;
|
||||
|
||||
/* // I dont think this makes sense to port into LLVMInsTrim
|
||||
char* inst_ratio_str = getenv("AFL_INST_RATIO");
|
||||
unsigned int inst_ratio = 100;
|
||||
if (inst_ratio_str) {
|
||||
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || inst_ratio > 100)
|
||||
FATAL("Bad value of AFL_INST_RATIO (must be between 1 and 100)");
|
||||
}
|
||||
*/
|
||||
/* // I dont think this makes sense to port into LLVMInsTrim
|
||||
char* inst_ratio_str = getenv("AFL_INST_RATIO");
|
||||
unsigned int inst_ratio = 100;
|
||||
if (inst_ratio_str) {
|
||||
|
||||
LLVMContext &C = M.getContext();
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio ||
|
||||
inst_ratio > 100) FATAL("Bad value of AFL_INST_RATIO (must be between 1
|
||||
and 100)");
|
||||
|
||||
GlobalVariable *CovMapPtr = new GlobalVariable(
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
LLVMContext &C = M.getContext();
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
|
||||
GlobalVariable *CovMapPtr = new GlobalVariable(
|
||||
M, PointerType::getUnqual(Int8Ty), false, GlobalValue::ExternalLinkage,
|
||||
nullptr, "__afl_area_ptr");
|
||||
|
||||
GlobalVariable *OldPrev = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc",
|
||||
0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
GlobalVariable *OldPrev = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0,
|
||||
GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
|
||||
u64 total_rs = 0;
|
||||
u64 total_hs = 0;
|
||||
u64 total_rs = 0;
|
||||
u64 total_hs = 0;
|
||||
|
||||
for (Function &F : M) {
|
||||
|
||||
if (!F.size()) { continue; }
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
|
||||
bool instrumentBlock = false;
|
||||
DebugLoc Loc;
|
||||
StringRef instFilename;
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
if (!Loc) Loc = IP->getDebugLoc();
|
||||
|
||||
for (Function &F : M) {
|
||||
if (!F.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
bool instrumentBlock = false;
|
||||
DebugLoc Loc;
|
||||
StringRef instFilename;
|
||||
if (Loc) {
|
||||
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
instFilename = cDILoc->getFilename();
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
|
||||
/* If the original location is empty, try using the inlined location
|
||||
*/
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
|
||||
}
|
||||
|
||||
for (auto &BB : F) {
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
if (!Loc)
|
||||
Loc = IP->getDebugLoc();
|
||||
}
|
||||
|
||||
if ( Loc ) {
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
instFilename = cDILoc->getFilename();
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.end(); ++it) {
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
/* If the original location is empty, try using the inlined location */
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
}
|
||||
}
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin(); it != myWhitelist.end(); ++it) {
|
||||
if (instFilename.str().length() >= it->length()) {
|
||||
if (instFilename.str().compare(instFilename.str().length() - it->length(), it->length(), *it) == 0) {
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) {
|
||||
if (!instFilename.str().empty())
|
||||
SAYF(cYEL "[!] " cBRI "Not in whitelist, skipping %s ...\n", instFilename.str().c_str());
|
||||
else
|
||||
SAYF(cYEL "[!] " cBRI "No filename information found, skipping it");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<BasicBlock *> MS;
|
||||
if (!MarkSetOpt) {
|
||||
for (auto &BB : F) {
|
||||
MS.insert(&BB);
|
||||
}
|
||||
total_rs += F.size();
|
||||
} else {
|
||||
auto Result = markNodes(&F);
|
||||
auto RS = Result.first;
|
||||
auto HS = Result.second;
|
||||
|
||||
MS.insert(RS.begin(), RS.end());
|
||||
if (!LoopHeadOpt) {
|
||||
MS.insert(HS.begin(), HS.end());
|
||||
total_rs += MS.size();
|
||||
} else {
|
||||
DenseSet<std::pair<BasicBlock *, BasicBlock *>> EdgeSet;
|
||||
DominatorTreeWrapperPass *DTWP = &getAnalysis<DominatorTreeWrapperPass>(F);
|
||||
auto DT = &DTWP->getDomTree();
|
||||
|
||||
total_rs += RS.size();
|
||||
total_hs += HS.size();
|
||||
|
||||
for (BasicBlock *BB : HS) {
|
||||
bool Inserted = false;
|
||||
for (auto BI = pred_begin(BB), BE = pred_end(BB);
|
||||
BI != BE; ++BI
|
||||
) {
|
||||
auto Edge = BasicBlockEdge(*BI, BB);
|
||||
if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) {
|
||||
EdgeSet.insert({*BI, BB});
|
||||
Inserted = true;
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (!Inserted) {
|
||||
MS.insert(BB);
|
||||
total_rs += 1;
|
||||
total_hs -= 1;
|
||||
}
|
||||
|
||||
}
|
||||
for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) {
|
||||
auto PredBB = I->first;
|
||||
auto SuccBB = I->second;
|
||||
auto NewBB = SplitBlockPredecessors(SuccBB, {PredBB}, ".split",
|
||||
DT, nullptr,
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) {
|
||||
|
||||
if (!instFilename.str().empty())
|
||||
SAYF(cYEL "[!] " cBRI "Not in whitelist, skipping %s ...\n",
|
||||
instFilename.str().c_str());
|
||||
else
|
||||
SAYF(cYEL "[!] " cBRI "No filename information found, skipping it");
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::unordered_set<BasicBlock *> MS;
|
||||
if (!MarkSetOpt) {
|
||||
|
||||
for (auto &BB : F) {
|
||||
|
||||
MS.insert(&BB);
|
||||
|
||||
}
|
||||
|
||||
total_rs += F.size();
|
||||
|
||||
} else {
|
||||
|
||||
auto Result = markNodes(&F);
|
||||
auto RS = Result.first;
|
||||
auto HS = Result.second;
|
||||
|
||||
MS.insert(RS.begin(), RS.end());
|
||||
if (!LoopHeadOpt) {
|
||||
|
||||
MS.insert(HS.begin(), HS.end());
|
||||
total_rs += MS.size();
|
||||
|
||||
} else {
|
||||
|
||||
DenseSet<std::pair<BasicBlock *, BasicBlock *>> EdgeSet;
|
||||
DominatorTreeWrapperPass * DTWP =
|
||||
&getAnalysis<DominatorTreeWrapperPass>(F);
|
||||
auto DT = &DTWP->getDomTree();
|
||||
|
||||
total_rs += RS.size();
|
||||
total_hs += HS.size();
|
||||
|
||||
for (BasicBlock *BB : HS) {
|
||||
|
||||
bool Inserted = false;
|
||||
for (auto BI = pred_begin(BB), BE = pred_end(BB); BI != BE; ++BI) {
|
||||
|
||||
auto Edge = BasicBlockEdge(*BI, BB);
|
||||
if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) {
|
||||
|
||||
EdgeSet.insert({*BI, BB});
|
||||
Inserted = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!Inserted) {
|
||||
|
||||
MS.insert(BB);
|
||||
total_rs += 1;
|
||||
total_hs -= 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) {
|
||||
|
||||
auto PredBB = I->first;
|
||||
auto SuccBB = I->second;
|
||||
auto NewBB =
|
||||
SplitBlockPredecessors(SuccBB, {PredBB}, ".split", DT, nullptr,
|
||||
#if LLVM_VERSION_MAJOR >= 8
|
||||
nullptr,
|
||||
nullptr,
|
||||
#endif
|
||||
false);
|
||||
MS.insert(NewBB);
|
||||
}
|
||||
false);
|
||||
MS.insert(NewBB);
|
||||
|
||||
}
|
||||
|
||||
auto *EBB = &F.getEntryBlock();
|
||||
if (succ_begin(EBB) == succ_end(EBB)) {
|
||||
MS.insert(EBB);
|
||||
total_rs += 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto *EBB = &F.getEntryBlock();
|
||||
if (succ_begin(EBB) == succ_end(EBB)) {
|
||||
|
||||
MS.insert(EBB);
|
||||
total_rs += 1;
|
||||
|
||||
for (BasicBlock &BB : F) {
|
||||
if (MS.find(&BB) == MS.end()) {
|
||||
continue;
|
||||
}
|
||||
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||
IRB.CreateStore(ConstantInt::get(Int32Ty, genLabel()), OldPrev);
|
||||
}
|
||||
}
|
||||
|
||||
for (BasicBlock &BB : F) {
|
||||
auto PI = pred_begin(&BB);
|
||||
auto PE = pred_end(&BB);
|
||||
if (MarkSetOpt && MS.find(&BB) == MS.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MS.find(&BB) == MS.end()) { continue; }
|
||||
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||
Value *L = NULL;
|
||||
if (PI == PE) {
|
||||
L = ConstantInt::get(Int32Ty, genLabel());
|
||||
} else {
|
||||
auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin());
|
||||
DenseMap<BasicBlock *, unsigned> PredMap;
|
||||
for (auto PI = pred_begin(&BB), PE = pred_end(&BB);
|
||||
PI != PE; ++PI
|
||||
) {
|
||||
BasicBlock *PBB = *PI;
|
||||
auto It = PredMap.insert({PBB, genLabel()});
|
||||
unsigned Label = It.first->second;
|
||||
PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB);
|
||||
}
|
||||
L = PN;
|
||||
IRB.CreateStore(ConstantInt::get(Int32Ty, genLabel()), OldPrev);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (BasicBlock &BB : F) {
|
||||
|
||||
auto PI = pred_begin(&BB);
|
||||
auto PE = pred_end(&BB);
|
||||
if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; }
|
||||
|
||||
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||
Value * L = NULL;
|
||||
if (PI == PE) {
|
||||
|
||||
L = ConstantInt::get(Int32Ty, genLabel());
|
||||
|
||||
} else {
|
||||
|
||||
auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin());
|
||||
DenseMap<BasicBlock *, unsigned> PredMap;
|
||||
for (auto PI = pred_begin(&BB), PE = pred_end(&BB); PI != PE; ++PI) {
|
||||
|
||||
BasicBlock *PBB = *PI;
|
||||
auto It = PredMap.insert({PBB, genLabel()});
|
||||
unsigned Label = It.first->second;
|
||||
PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB);
|
||||
|
||||
}
|
||||
|
||||
/* Load prev_loc */
|
||||
LoadInst *PrevLoc = IRB.CreateLoad(OldPrev);
|
||||
PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
|
||||
L = PN;
|
||||
|
||||
/* Load SHM pointer */
|
||||
LoadInst *MapPtr = IRB.CreateLoad(CovMapPtr);
|
||||
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
Value *MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, L));
|
||||
}
|
||||
|
||||
/* Update bitmap */
|
||||
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
|
||||
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
|
||||
/* Load prev_loc */
|
||||
LoadInst *PrevLoc = IRB.CreateLoad(OldPrev);
|
||||
PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
|
||||
|
||||
/* Load SHM pointer */
|
||||
LoadInst *MapPtr = IRB.CreateLoad(CovMapPtr);
|
||||
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
Value *MapPtrIdx =
|
||||
IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, L));
|
||||
|
||||
/* Update bitmap */
|
||||
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
|
||||
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
if (neverZero_counters_str != NULL) { // with llvm 9 we make this the default as the bug in llvm is then fixed
|
||||
if (neverZero_counters_str !=
|
||||
NULL) // with llvm 9 we make this the default as the bug in llvm is
|
||||
// then fixed
|
||||
#else
|
||||
#warning "neverZero implementation needs to be reviewed!"
|
||||
if (1) // with llvm 9 we make this the default as the bug in llvm is
|
||||
// then fixed
|
||||
#endif
|
||||
{
|
||||
|
||||
/* hexcoder: Realize a counter that skips zero during overflow.
|
||||
* Once this counter reaches its maximum value, it next increments to 1
|
||||
* Once this counter reaches its maximum value, it next increments to
|
||||
* 1
|
||||
*
|
||||
* Instead of
|
||||
* Counter + 1 -> Counter
|
||||
@ -305,40 +387,52 @@ namespace {
|
||||
* Counter + 1 -> {Counter, OverflowFlag}
|
||||
* Counter + OverflowFlag -> Counter
|
||||
*/
|
||||
auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
}
|
||||
#endif
|
||||
|
||||
IRB.CreateStore(Incr, MapPtrIdx)->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
/* Set prev_loc to cur_loc >> 1 */
|
||||
/*
|
||||
StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
|
||||
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
*/
|
||||
auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
|
||||
total_instr++;
|
||||
}
|
||||
|
||||
IRB.CreateStore(Incr, MapPtrIdx)
|
||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
/* Set prev_loc to cur_loc >> 1 */
|
||||
/*
|
||||
StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int32Ty, L >> 1),
|
||||
OldPrev); Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C,
|
||||
None));
|
||||
*/
|
||||
|
||||
total_instr++;
|
||||
|
||||
}
|
||||
|
||||
OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n"/*", ratio %u%%)."*/,
|
||||
total_instr, total_rs, total_hs,
|
||||
getenv("AFL_HARDEN") ? "hardened" :
|
||||
((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) ?
|
||||
"ASAN/MSAN" : "non-hardened")/*, inst_ratio*/);
|
||||
return false;
|
||||
}
|
||||
}; // end of struct InsTrim
|
||||
|
||||
OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n" /*", ratio
|
||||
%u%%)."*/
|
||||
,
|
||||
total_instr, total_rs, total_hs,
|
||||
getenv("AFL_HARDEN")
|
||||
? "hardened"
|
||||
: ((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN"))
|
||||
? "ASAN/MSAN"
|
||||
: "non-hardened") /*, inst_ratio*/);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}; // end of struct InsTrim
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
char InsTrim::ID = 0;
|
||||
|
||||
static void registerAFLPass(const PassManagerBuilder &,
|
||||
legacy::PassManagerBase &PM) {
|
||||
|
||||
PM.add(new InsTrim());
|
||||
|
||||
}
|
||||
|
||||
static RegisterStandardPasses RegisterAFLPass(
|
||||
@ -346,3 +440,4 @@ static RegisterStandardPasses RegisterAFLPass(
|
||||
|
||||
static RegisterStandardPasses RegisterAFLPass0(
|
||||
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
|
||||
|
||||
|
@ -25,13 +25,22 @@ BIN_PATH = $(PREFIX)/bin
|
||||
|
||||
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
|
||||
LLVM_CONFIG ?= llvm-config
|
||||
ifeq "$(shell uname)" "OpenBSD"
|
||||
LLVM_CONFIG ?= $(BIN_PATH)/llvm-config
|
||||
HAS_OPT = $(shell test -x $(BIN_PATH)/opt && echo 0 || echo 1)
|
||||
ifeq "$(HAS_OPT)" "1"
|
||||
$(error llvm_mode needs a complete llvm installation (versions 3.8.0 up to 9) -> e.g. "pkg_add llvm-7.0.1p9")
|
||||
endif
|
||||
else
|
||||
LLVM_CONFIG ?= llvm-config
|
||||
endif
|
||||
|
||||
LLVMVER = $(shell $(LLVM_CONFIG) --version)
|
||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version | egrep -q '^9|3.0' && echo 1 || echo 0 )
|
||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version | egrep -q '^[12]|^3\.0|^1[0-9]' && echo 1 || echo 0 )
|
||||
LLVM_MAJOR = ($shell $(LLVM_CONFIG) --version | sed 's/\..*//')
|
||||
|
||||
ifeq "$(LLVM_UNSUPPORTED)" "1"
|
||||
$(warn llvm_mode only supports versions 3.8.0 up to 8.x )
|
||||
$(warn llvm_mode only supports versions 3.8.0 up to 9)
|
||||
endif
|
||||
|
||||
# this is not visible yet:
|
||||
@ -40,7 +49,7 @@ ifeq "$(LLVM_MAJOR)" "9"
|
||||
endif
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops
|
||||
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \
|
||||
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I ../include/ \
|
||||
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
||||
-DVERSION=\"$(VERSION)\"
|
||||
ifdef AFL_TRACE_PC
|
||||
@ -48,7 +57,7 @@ ifdef AFL_TRACE_PC
|
||||
endif
|
||||
|
||||
CXXFLAGS ?= -O3 -funroll-loops
|
||||
CXXFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \
|
||||
CXXFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -I ../include/ \
|
||||
-DVERSION=\"$(VERSION)\" -Wno-variadic-macros
|
||||
|
||||
CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -Wl,-znodelete -fno-rtti -fpic $(CXXFLAGS)
|
||||
@ -61,7 +70,7 @@ ifeq "$(shell uname)" "Darwin"
|
||||
endif
|
||||
|
||||
ifeq "$(shell uname)" "OpenBSD"
|
||||
CLANG_LFL += `$(LLVM_CONFIG) --libdir`/libLLVM.so.0.0
|
||||
CLANG_LFL += `$(LLVM_CONFIG) --libdir`/libLLVM.so
|
||||
endif
|
||||
|
||||
# We were using llvm-config --bindir to get the location of clang, but
|
||||
@ -69,13 +78,18 @@ endif
|
||||
# probably better.
|
||||
|
||||
ifeq "$(origin CC)" "default"
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
ifeq "$(shell uname)" "OpenBSD"
|
||||
CC = $(BIN_PATH)/clang
|
||||
CXX = $(BIN_PATH)/clang++
|
||||
else
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
endif
|
||||
endif
|
||||
|
||||
# sanity check.
|
||||
# Are versions of clang --version and llvm-config --version equal?
|
||||
CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*([0-9]\.[0-9]\.[0-9]).*/s//\1/p')
|
||||
CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*version\ ([0-9]\.[0-9]\.[0-9]).*/s//\1/p')
|
||||
|
||||
|
||||
ifeq "$(shell echo '\#include <sys/ipc.h>@\#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' | $(CC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 )" "1"
|
||||
@ -174,10 +188,10 @@ endif
|
||||
test_build: $(PROGS)
|
||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||
unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_CC=$(CC) 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)
|
||||
echo 0 | ../afl-showmap -m none -q -o .test-instr0 ./test-instr
|
||||
../afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
|
||||
echo 1 | ../afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||
@rm -f test-instr
|
||||
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please ping <lcamtuf@google.com> 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/vanhauser-thc/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
|
||||
@echo "[+] All right, the instrumentation seems to be working!"
|
||||
|
||||
all_done: test_build
|
||||
|
@ -19,207 +19,267 @@
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
DenseMap<BasicBlock *, uint32_t> LMap;
|
||||
std::vector<BasicBlock *> Blocks;
|
||||
std::set<uint32_t> Marked , Markabove;
|
||||
std::vector< std::vector<uint32_t> > Succs , Preds;
|
||||
DenseMap<BasicBlock *, uint32_t> LMap;
|
||||
std::vector<BasicBlock *> Blocks;
|
||||
std::set<uint32_t> Marked, Markabove;
|
||||
std::vector<std::vector<uint32_t> > Succs, Preds;
|
||||
|
||||
void reset() {
|
||||
|
||||
void reset(){
|
||||
LMap.clear();
|
||||
Blocks.clear();
|
||||
Marked.clear();
|
||||
Markabove.clear();
|
||||
|
||||
}
|
||||
|
||||
uint32_t start_point;
|
||||
|
||||
void labelEachBlock(Function *F) {
|
||||
|
||||
// Fake single endpoint;
|
||||
LMap[NULL] = Blocks.size();
|
||||
Blocks.push_back(NULL);
|
||||
|
||||
|
||||
// Assign the unique LabelID to each block;
|
||||
for (auto I = F->begin(), E = F->end(); I != E; ++I) {
|
||||
|
||||
BasicBlock *BB = &*I;
|
||||
LMap[BB] = Blocks.size();
|
||||
Blocks.push_back(BB);
|
||||
|
||||
}
|
||||
|
||||
|
||||
start_point = LMap[&F->getEntryBlock()];
|
||||
|
||||
}
|
||||
|
||||
void buildCFG(Function *F) {
|
||||
Succs.resize( Blocks.size() );
|
||||
Preds.resize( Blocks.size() );
|
||||
for( size_t i = 0 ; i < Succs.size() ; i ++ ){
|
||||
Succs[ i ].clear();
|
||||
Preds[ i ].clear();
|
||||
|
||||
Succs.resize(Blocks.size());
|
||||
Preds.resize(Blocks.size());
|
||||
for (size_t i = 0; i < Succs.size(); i++) {
|
||||
|
||||
Succs[i].clear();
|
||||
Preds[i].clear();
|
||||
|
||||
}
|
||||
|
||||
//uint32_t FakeID = 0;
|
||||
// uint32_t FakeID = 0;
|
||||
for (auto S = F->begin(), E = F->end(); S != E; ++S) {
|
||||
|
||||
BasicBlock *BB = &*S;
|
||||
uint32_t MyID = LMap[BB];
|
||||
//if (succ_begin(BB) == succ_end(BB)) {
|
||||
//Succs[MyID].push_back(FakeID);
|
||||
//Marked.insert(MyID);
|
||||
uint32_t MyID = LMap[BB];
|
||||
// if (succ_begin(BB) == succ_end(BB)) {
|
||||
|
||||
// Succs[MyID].push_back(FakeID);
|
||||
// Marked.insert(MyID);
|
||||
//}
|
||||
for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) {
|
||||
|
||||
Succs[MyID].push_back(LMap[*I]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector< std::vector<uint32_t> > tSuccs;
|
||||
std::vector<bool> tag , indfs;
|
||||
std::vector<std::vector<uint32_t> > tSuccs;
|
||||
std::vector<bool> tag, indfs;
|
||||
|
||||
void DFStree(size_t now_id) {
|
||||
if(tag[now_id]) return;
|
||||
tag[now_id]=true;
|
||||
indfs[now_id]=true;
|
||||
for (auto succ: tSuccs[now_id]) {
|
||||
if(tag[succ] and indfs[succ]) {
|
||||
|
||||
if (tag[now_id]) return;
|
||||
tag[now_id] = true;
|
||||
indfs[now_id] = true;
|
||||
for (auto succ : tSuccs[now_id]) {
|
||||
|
||||
if (tag[succ] and indfs[succ]) {
|
||||
|
||||
Marked.insert(succ);
|
||||
Markabove.insert(succ);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
Succs[now_id].push_back(succ);
|
||||
Preds[succ].push_back(now_id);
|
||||
DFStree(succ);
|
||||
|
||||
}
|
||||
indfs[now_id]=false;
|
||||
|
||||
indfs[now_id] = false;
|
||||
|
||||
}
|
||||
|
||||
void turnCFGintoDAG(Function *F) {
|
||||
|
||||
tSuccs = Succs;
|
||||
tag.resize(Blocks.size());
|
||||
indfs.resize(Blocks.size());
|
||||
for (size_t i = 0; i < Blocks.size(); ++ i) {
|
||||
for (size_t i = 0; i < Blocks.size(); ++i) {
|
||||
|
||||
Succs[i].clear();
|
||||
tag[i]=false;
|
||||
indfs[i]=false;
|
||||
tag[i] = false;
|
||||
indfs[i] = false;
|
||||
|
||||
}
|
||||
|
||||
DFStree(start_point);
|
||||
for (size_t i = 0; i < Blocks.size(); ++ i)
|
||||
if( Succs[i].empty() ){
|
||||
for (size_t i = 0; i < Blocks.size(); ++i)
|
||||
if (Succs[i].empty()) {
|
||||
|
||||
Succs[i].push_back(0);
|
||||
Preds[0].push_back(i);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint32_t timeStamp;
|
||||
namespace DominatorTree{
|
||||
std::vector< std::vector<uint32_t> > cov;
|
||||
std::vector<uint32_t> dfn, nfd, par, sdom, idom, mom, mn;
|
||||
namespace DominatorTree {
|
||||
|
||||
std::vector<std::vector<uint32_t> > cov;
|
||||
std::vector<uint32_t> dfn, nfd, par, sdom, idom, mom, mn;
|
||||
|
||||
bool Compare(uint32_t u, uint32_t v) {
|
||||
|
||||
return dfn[u] < dfn[v];
|
||||
|
||||
}
|
||||
|
||||
uint32_t eval(uint32_t u) {
|
||||
|
||||
if (mom[u] == u) return u;
|
||||
uint32_t res = eval(mom[u]);
|
||||
if (Compare(sdom[mn[mom[u]]], sdom[mn[u]])) { mn[u] = mn[mom[u]]; }
|
||||
return mom[u] = res;
|
||||
|
||||
}
|
||||
|
||||
void DFS(uint32_t now) {
|
||||
|
||||
timeStamp += 1;
|
||||
dfn[now] = timeStamp;
|
||||
nfd[timeStamp - 1] = now;
|
||||
for (auto succ : Succs[now]) {
|
||||
|
||||
if (dfn[succ] == 0) {
|
||||
|
||||
par[succ] = now;
|
||||
DFS(succ);
|
||||
|
||||
bool Compare(uint32_t u, uint32_t v) {
|
||||
return dfn[u] < dfn[v];
|
||||
}
|
||||
uint32_t eval(uint32_t u) {
|
||||
if( mom[u] == u ) return u;
|
||||
uint32_t res = eval( mom[u] );
|
||||
if(Compare(sdom[mn[mom[u]]] , sdom[mn[u]])) {
|
||||
mn[u] = mn[mom[u]];
|
||||
}
|
||||
return mom[u] = res;
|
||||
|
||||
}
|
||||
|
||||
void DFS(uint32_t now) {
|
||||
timeStamp += 1;
|
||||
dfn[now] = timeStamp;
|
||||
nfd[timeStamp - 1] = now;
|
||||
for( auto succ : Succs[now] ) {
|
||||
if( dfn[succ] == 0 ) {
|
||||
par[succ] = now;
|
||||
DFS(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DominatorTree(Function *F) {
|
||||
|
||||
if (Blocks.empty()) return;
|
||||
uint32_t s = start_point;
|
||||
|
||||
// Initialization
|
||||
mn.resize(Blocks.size());
|
||||
cov.resize(Blocks.size());
|
||||
dfn.resize(Blocks.size());
|
||||
nfd.resize(Blocks.size());
|
||||
par.resize(Blocks.size());
|
||||
mom.resize(Blocks.size());
|
||||
sdom.resize(Blocks.size());
|
||||
idom.resize(Blocks.size());
|
||||
|
||||
for (uint32_t i = 0; i < Blocks.size(); i++) {
|
||||
|
||||
dfn[i] = 0;
|
||||
nfd[i] = Blocks.size();
|
||||
cov[i].clear();
|
||||
idom[i] = mom[i] = mn[i] = sdom[i] = i;
|
||||
|
||||
}
|
||||
|
||||
void DominatorTree(Function *F) {
|
||||
if( Blocks.empty() ) return;
|
||||
uint32_t s = start_point;
|
||||
timeStamp = 0;
|
||||
DFS(s);
|
||||
|
||||
// Initialization
|
||||
mn.resize(Blocks.size());
|
||||
cov.resize(Blocks.size());
|
||||
dfn.resize(Blocks.size());
|
||||
nfd.resize(Blocks.size());
|
||||
par.resize(Blocks.size());
|
||||
mom.resize(Blocks.size());
|
||||
sdom.resize(Blocks.size());
|
||||
idom.resize(Blocks.size());
|
||||
for (uint32_t i = Blocks.size() - 1; i >= 1u; i--) {
|
||||
|
||||
uint32_t now = nfd[i];
|
||||
if (now == Blocks.size()) { continue; }
|
||||
for (uint32_t pre : Preds[now]) {
|
||||
|
||||
if (dfn[pre]) {
|
||||
|
||||
eval(pre);
|
||||
if (Compare(sdom[mn[pre]], sdom[now])) { sdom[now] = sdom[mn[pre]]; }
|
||||
|
||||
}
|
||||
|
||||
for( uint32_t i = 0 ; i < Blocks.size() ; i ++ ) {
|
||||
dfn[i] = 0;
|
||||
nfd[i] = Blocks.size();
|
||||
cov[i].clear();
|
||||
idom[i] = mom[i] = mn[i] = sdom[i] = i;
|
||||
}
|
||||
|
||||
timeStamp = 0;
|
||||
DFS(s);
|
||||
cov[sdom[now]].push_back(now);
|
||||
mom[now] = par[now];
|
||||
for (uint32_t x : cov[par[now]]) {
|
||||
|
||||
eval(x);
|
||||
if (Compare(sdom[mn[x]], par[now])) {
|
||||
|
||||
idom[x] = mn[x];
|
||||
|
||||
} else {
|
||||
|
||||
idom[x] = par[now];
|
||||
|
||||
for( uint32_t i = Blocks.size() - 1 ; i >= 1u ; i -- ) {
|
||||
uint32_t now = nfd[i];
|
||||
if( now == Blocks.size() ) {
|
||||
continue;
|
||||
}
|
||||
for( uint32_t pre : Preds[ now ] ) {
|
||||
if( dfn[ pre ] ) {
|
||||
eval(pre);
|
||||
if( Compare(sdom[mn[pre]], sdom[now]) ) {
|
||||
sdom[now] = sdom[mn[pre]];
|
||||
}
|
||||
}
|
||||
}
|
||||
cov[sdom[now]].push_back(now);
|
||||
mom[now] = par[now];
|
||||
for( uint32_t x : cov[par[now]] ) {
|
||||
eval(x);
|
||||
if( Compare(sdom[mn[x]], par[now]) ) {
|
||||
idom[x] = mn[x];
|
||||
} else {
|
||||
idom[x] = par[now];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for( uint32_t i = 1 ; i < Blocks.size() ; i += 1 ) {
|
||||
uint32_t now = nfd[i];
|
||||
if( now == Blocks.size() ) {
|
||||
continue;
|
||||
}
|
||||
if(idom[now] != sdom[now])
|
||||
idom[now] = idom[idom[now]];
|
||||
}
|
||||
}
|
||||
}; // End of DominatorTree
|
||||
|
||||
std::vector<uint32_t> Visited, InStack;
|
||||
std::vector<uint32_t> TopoOrder, InDeg;
|
||||
std::vector< std::vector<uint32_t> > t_Succ , t_Pred;
|
||||
for (uint32_t i = 1; i < Blocks.size(); i += 1) {
|
||||
|
||||
uint32_t now = nfd[i];
|
||||
if (now == Blocks.size()) { continue; }
|
||||
if (idom[now] != sdom[now]) idom[now] = idom[idom[now]];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace DominatorTree
|
||||
|
||||
std::vector<uint32_t> Visited, InStack;
|
||||
std::vector<uint32_t> TopoOrder, InDeg;
|
||||
std::vector<std::vector<uint32_t> > t_Succ, t_Pred;
|
||||
|
||||
void Go(uint32_t now, uint32_t tt) {
|
||||
if( now == tt ) return;
|
||||
|
||||
if (now == tt) return;
|
||||
Visited[now] = InStack[now] = timeStamp;
|
||||
|
||||
for(uint32_t nxt : Succs[now]) {
|
||||
if(Visited[nxt] == timeStamp and InStack[nxt] == timeStamp) {
|
||||
for (uint32_t nxt : Succs[now]) {
|
||||
|
||||
if (Visited[nxt] == timeStamp and InStack[nxt] == timeStamp) {
|
||||
|
||||
Marked.insert(nxt);
|
||||
|
||||
}
|
||||
|
||||
t_Succ[now].push_back(nxt);
|
||||
t_Pred[nxt].push_back(now);
|
||||
InDeg[nxt] += 1;
|
||||
if(Visited[nxt] == timeStamp) {
|
||||
continue;
|
||||
}
|
||||
if (Visited[nxt] == timeStamp) { continue; }
|
||||
Go(nxt, tt);
|
||||
|
||||
}
|
||||
|
||||
InStack[now] = 0;
|
||||
|
||||
}
|
||||
|
||||
void TopologicalSort(uint32_t ss, uint32_t tt) {
|
||||
|
||||
timeStamp += 1;
|
||||
|
||||
Go(ss, tt);
|
||||
@ -227,76 +287,111 @@ void TopologicalSort(uint32_t ss, uint32_t tt) {
|
||||
TopoOrder.clear();
|
||||
std::queue<uint32_t> wait;
|
||||
wait.push(ss);
|
||||
while( not wait.empty() ) {
|
||||
uint32_t now = wait.front(); wait.pop();
|
||||
while (not wait.empty()) {
|
||||
|
||||
uint32_t now = wait.front();
|
||||
wait.pop();
|
||||
TopoOrder.push_back(now);
|
||||
for(uint32_t nxt : t_Succ[now]) {
|
||||
for (uint32_t nxt : t_Succ[now]) {
|
||||
|
||||
InDeg[nxt] -= 1;
|
||||
if(InDeg[nxt] == 0u) {
|
||||
wait.push(nxt);
|
||||
}
|
||||
if (InDeg[nxt] == 0u) { wait.push(nxt); }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector< std::set<uint32_t> > NextMarked;
|
||||
bool Indistinguish(uint32_t node1, uint32_t node2) {
|
||||
if(NextMarked[node1].size() > NextMarked[node2].size()){
|
||||
std::vector<std::set<uint32_t> > NextMarked;
|
||||
bool Indistinguish(uint32_t node1, uint32_t node2) {
|
||||
|
||||
if (NextMarked[node1].size() > NextMarked[node2].size()) {
|
||||
|
||||
uint32_t _swap = node1;
|
||||
node1 = node2;
|
||||
node2 = _swap;
|
||||
|
||||
}
|
||||
for(uint32_t x : NextMarked[node1]) {
|
||||
if( NextMarked[node2].find(x) != NextMarked[node2].end() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (uint32_t x : NextMarked[node1]) {
|
||||
|
||||
if (NextMarked[node2].find(x) != NextMarked[node2].end()) { return true; }
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void MakeUniq(uint32_t now) {
|
||||
|
||||
bool StopFlag = false;
|
||||
if (Marked.find(now) == Marked.end()) {
|
||||
for(uint32_t pred1 : t_Pred[now]) {
|
||||
for(uint32_t pred2 : t_Pred[now]) {
|
||||
if(pred1 == pred2) continue;
|
||||
if(Indistinguish(pred1, pred2)) {
|
||||
|
||||
for (uint32_t pred1 : t_Pred[now]) {
|
||||
|
||||
for (uint32_t pred2 : t_Pred[now]) {
|
||||
|
||||
if (pred1 == pred2) continue;
|
||||
if (Indistinguish(pred1, pred2)) {
|
||||
|
||||
Marked.insert(now);
|
||||
StopFlag = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (StopFlag) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (StopFlag) { break; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if(Marked.find(now) != Marked.end()) {
|
||||
|
||||
if (Marked.find(now) != Marked.end()) {
|
||||
|
||||
NextMarked[now].insert(now);
|
||||
|
||||
} else {
|
||||
for(uint32_t pred : t_Pred[now]) {
|
||||
for(uint32_t x : NextMarked[pred]) {
|
||||
|
||||
for (uint32_t pred : t_Pred[now]) {
|
||||
|
||||
for (uint32_t x : NextMarked[pred]) {
|
||||
|
||||
NextMarked[now].insert(x);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MarkSubGraph(uint32_t ss, uint32_t tt) {
|
||||
TopologicalSort(ss, tt);
|
||||
if(TopoOrder.empty()) return;
|
||||
|
||||
for(uint32_t i : TopoOrder) {
|
||||
TopologicalSort(ss, tt);
|
||||
if (TopoOrder.empty()) return;
|
||||
|
||||
for (uint32_t i : TopoOrder) {
|
||||
|
||||
NextMarked[i].clear();
|
||||
|
||||
}
|
||||
|
||||
NextMarked[TopoOrder[0]].insert(TopoOrder[0]);
|
||||
for(uint32_t i = 1 ; i < TopoOrder.size() ; i += 1) {
|
||||
for (uint32_t i = 1; i < TopoOrder.size(); i += 1) {
|
||||
|
||||
MakeUniq(TopoOrder[i]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MarkVertice(Function *F) {
|
||||
|
||||
uint32_t s = start_point;
|
||||
|
||||
InDeg.resize(Blocks.size());
|
||||
@ -306,26 +401,32 @@ void MarkVertice(Function *F) {
|
||||
t_Pred.resize(Blocks.size());
|
||||
NextMarked.resize(Blocks.size());
|
||||
|
||||
for( uint32_t i = 0 ; i < Blocks.size() ; i += 1 ) {
|
||||
for (uint32_t i = 0; i < Blocks.size(); i += 1) {
|
||||
|
||||
Visited[i] = InStack[i] = InDeg[i] = 0;
|
||||
t_Succ[i].clear();
|
||||
t_Pred[i].clear();
|
||||
|
||||
}
|
||||
|
||||
timeStamp = 0;
|
||||
uint32_t t = 0;
|
||||
//MarkSubGraph(s, t);
|
||||
//return;
|
||||
// MarkSubGraph(s, t);
|
||||
// return;
|
||||
|
||||
while (s != t) {
|
||||
|
||||
while( s != t ) {
|
||||
MarkSubGraph(DominatorTree::idom[t], t);
|
||||
t = DominatorTree::idom[t];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// return {marked nodes}
|
||||
std::pair<std::vector<BasicBlock *>,
|
||||
std::vector<BasicBlock *> >markNodes(Function *F) {
|
||||
std::pair<std::vector<BasicBlock *>, std::vector<BasicBlock *> > markNodes(
|
||||
Function *F) {
|
||||
|
||||
assert(F->size() > 0 && "Function can not be empty");
|
||||
|
||||
reset();
|
||||
@ -335,21 +436,30 @@ std::pair<std::vector<BasicBlock *>,
|
||||
DominatorTree::DominatorTree(F);
|
||||
MarkVertice(F);
|
||||
|
||||
std::vector<BasicBlock *> Result , ResultAbove;
|
||||
for( uint32_t x : Markabove ) {
|
||||
auto it = Marked.find( x );
|
||||
if( it != Marked.end() )
|
||||
Marked.erase( it );
|
||||
if( x )
|
||||
ResultAbove.push_back(Blocks[x]);
|
||||
}
|
||||
for( uint32_t x : Marked ) {
|
||||
if (x == 0) {
|
||||
continue;
|
||||
} else {
|
||||
Result.push_back(Blocks[x]);
|
||||
}
|
||||
std::vector<BasicBlock *> Result, ResultAbove;
|
||||
for (uint32_t x : Markabove) {
|
||||
|
||||
auto it = Marked.find(x);
|
||||
if (it != Marked.end()) Marked.erase(it);
|
||||
if (x) ResultAbove.push_back(Blocks[x]);
|
||||
|
||||
}
|
||||
|
||||
return { Result , ResultAbove };
|
||||
for (uint32_t x : Marked) {
|
||||
|
||||
if (x == 0) {
|
||||
|
||||
continue;
|
||||
|
||||
} else {
|
||||
|
||||
Result.push_back(Blocks[x]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {Result, ResultAbove};
|
||||
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,10 @@
|
||||
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include<vector>
|
||||
#include <vector>
|
||||
|
||||
std::pair<std::vector<llvm::BasicBlock *>,
|
||||
std::vector<llvm::BasicBlock *>> markNodes(llvm::Function *F);
|
||||
std::pair<std::vector<llvm::BasicBlock *>, std::vector<llvm::BasicBlock *>>
|
||||
markNodes(llvm::Function *F);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# InsTrim
|
||||
InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing
|
||||
|
||||
InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing
|
||||
|
||||
## Introduction
|
||||
|
||||
@ -8,17 +8,15 @@ InsTrim uses CFG and markers to instrument just what is necessary in the
|
||||
binary in llvm_mode. It is about 20-25% faster but as a cost has a lower
|
||||
path discovery.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Set the environment variable AFL_LLVM_INSTRIM=1
|
||||
Set the environment variable `AFL_LLVM_INSTRIM=1`.
|
||||
|
||||
There is also an advanced 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
|
||||
|
||||
To enable this mode set `AFL_LLVM_INSTRIM_LOOPHEAD=1`.
|
||||
|
||||
## Background
|
||||
|
@ -1,20 +0,0 @@
|
||||
Usage
|
||||
=====
|
||||
|
||||
By default the passes will not run when you compile programs using
|
||||
afl-clang-fast. Hence, you can use AFL as usual.
|
||||
To enable the passes you must set environment variables before you
|
||||
compile the target project.
|
||||
|
||||
The following options exist:
|
||||
|
||||
export AFL_LLVM_LAF_SPLIT_SWITCHES=1 Enables the split-switches pass.
|
||||
|
||||
export AFL_LLVM_LAF_TRANSFORM_COMPARES=1 Enables the transform-compares pass
|
||||
(strcmp, memcmp, strncmp, strcasecmp, strncasecmp).
|
||||
|
||||
export AFL_LLVM_LAF_SPLIT_COMPARES=1 Enables the split-compares pass.
|
||||
By default it will split all compares with a bit width <= 64 bits.
|
||||
You can change this behaviour by setting
|
||||
export AFL_LLVM_LAF_SPLIT_COMPARES_BITW=<bit_width>.
|
||||
|
25
llvm_mode/README.laf-intel.md
Normal file
25
llvm_mode/README.laf-intel.md
Normal file
@ -0,0 +1,25 @@
|
||||
# laf-intel instrumentation
|
||||
|
||||
## Usage
|
||||
|
||||
By default the passes will not run when you compile programs using
|
||||
afl-clang-fast. Hence, you can use AFL as usual.
|
||||
To enable the passes you must set environment variables before you
|
||||
compile the target project.
|
||||
|
||||
The following options exist:
|
||||
|
||||
`export AFL_LLVM_LAF_SPLIT_SWITCHES=1`
|
||||
|
||||
Enables the split-switches pass.
|
||||
|
||||
`export AFL_LLVM_LAF_TRANSFORM_COMPARES=1`
|
||||
|
||||
Enables the transform-compares pass (strcmp, memcmp, strncmp, strcasecmp, strncasecmp).
|
||||
|
||||
`export AFL_LLVM_LAF_SPLIT_COMPARES=1`
|
||||
|
||||
Enables the split-compares pass.
|
||||
By default it will split all compares with a bit width <= 64 bits.
|
||||
You can change this behaviour by setting `export AFL_LLVM_LAF_SPLIT_COMPARES_BITW=<bit_width>`.
|
||||
|
@ -1,15 +1,11 @@
|
||||
============================================
|
||||
Fast LLVM-based instrumentation for afl-fuzz
|
||||
============================================
|
||||
# Fast LLVM-based instrumentation for afl-fuzz
|
||||
|
||||
(See ../docs/README for the general instruction manual.)
|
||||
(See ../gcc_plugin/README.gcc for the GCC-based instrumentation.)
|
||||
|
||||
1) Introduction
|
||||
---------------
|
||||
## 1) Introduction
|
||||
|
||||
! llvm_mode works with llvm version 3.8.1 up to 8.x !
|
||||
! llvm version 9 does not work yet !
|
||||
! llvm_mode works with llvm versions 3.8.0 up to 9 !
|
||||
|
||||
The code in this directory allows you to instrument programs for AFL using
|
||||
true compiler-level instrumentation, instead of the more crude
|
||||
@ -39,8 +35,7 @@ co-exists with the original code.
|
||||
|
||||
The idea and much of the implementation comes from Laszlo Szekeres.
|
||||
|
||||
2) How to use this
|
||||
------------------
|
||||
## 2) How to use this
|
||||
|
||||
In order to leverage this mechanism, you need to have clang installed on your
|
||||
system. You should also make sure that the llvm-config tool is in your path
|
||||
@ -64,8 +59,10 @@ called afl-clang-fast and afl-clang-fast++ in the parent directory. Once this
|
||||
is done, you can instrument third-party code in a way similar to the standard
|
||||
operating mode of AFL, e.g.:
|
||||
|
||||
```
|
||||
CC=/path/to/afl/afl-clang-fast ./configure [...options...]
|
||||
make
|
||||
```
|
||||
|
||||
Be sure to also include CXX set to afl-clang-fast++ for C++ code.
|
||||
|
||||
@ -79,7 +76,7 @@ Note: if you want the LLVM helper to be installed on your system for all
|
||||
users, you need to build it before issuing 'make install' in the parent
|
||||
directory.
|
||||
|
||||
3) Options
|
||||
## 3) Options
|
||||
|
||||
Several options are present to make llvm_mode faster or help it rearrange
|
||||
the code to make afl-fuzz path discovery easier.
|
||||
@ -102,15 +99,12 @@ is not optimal and was only fixed in llvm 9.
|
||||
You can set this with AFL_LLVM_NOT_ZERO=1
|
||||
See README.neverzero
|
||||
|
||||
|
||||
4) Gotchas, feedback, bugs
|
||||
--------------------------
|
||||
## 4) Gotchas, feedback, bugs
|
||||
|
||||
This is an early-stage mechanism, so field reports are welcome. You can send bug
|
||||
reports to <afl-users@googlegroups.com>.
|
||||
|
||||
5) Bonus feature #1: deferred initialization
|
||||
--------------------------------------------
|
||||
## 5) Bonus feature #1: deferred initialization
|
||||
|
||||
AFL tries to optimize performance by executing the targeted binary just once,
|
||||
stopping it just before main(), and then cloning this "master" process to get
|
||||
@ -146,9 +140,11 @@ a location after:
|
||||
|
||||
With the location selected, add this code in the appropriate spot:
|
||||
|
||||
```c
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
__AFL_INIT();
|
||||
#endif
|
||||
```
|
||||
|
||||
You don't need the #ifdef guards, but including them ensures that the program
|
||||
will keep working normally when compiled with a tool other than afl-clang-fast.
|
||||
@ -156,8 +152,7 @@ will keep working normally when compiled with a tool other than afl-clang-fast.
|
||||
Finally, recompile the program with afl-clang-fast (afl-gcc or afl-clang will
|
||||
*not* generate a deferred-initialization binary) - and you should be all set!
|
||||
|
||||
6) Bonus feature #2: persistent mode
|
||||
------------------------------------
|
||||
## 6) Bonus feature #2: persistent mode
|
||||
|
||||
Some libraries provide APIs that are stateless, or whose state can be reset in
|
||||
between processing different input files. When such a reset is performed, a
|
||||
@ -166,6 +161,7 @@ eliminating the need for repeated fork() calls and the associated OS overhead.
|
||||
|
||||
The basic structure of the program that does this would be:
|
||||
|
||||
```c
|
||||
while (__AFL_LOOP(1000)) {
|
||||
|
||||
/* Read input data. */
|
||||
@ -175,6 +171,7 @@ The basic structure of the program that does this would be:
|
||||
}
|
||||
|
||||
/* Exit normally */
|
||||
```
|
||||
|
||||
The numerical value specified within the loop controls the maximum number
|
||||
of iterations before AFL will restart the process from scratch. This minimizes
|
||||
@ -183,8 +180,8 @@ and going much higher increases the likelihood of hiccups without giving you
|
||||
any real performance benefits.
|
||||
|
||||
A more detailed template is shown in ../experimental/persistent_demo/.
|
||||
Similarly to the previous mode, the feature works only with afl-clang-fast;
|
||||
#ifdef guards can be used to suppress it when using other compilers.
|
||||
Similarly to the previous mode, the feature works only with afl-clang-fast; #ifdef
|
||||
guards can be used to suppress it when using other compilers.
|
||||
|
||||
Note that as with the previous mode, the feature is easy to misuse; if you
|
||||
do not fully reset the critical state, you may end up with false positives or
|
||||
@ -196,8 +193,7 @@ PS. Because there are task switches still involved, the mode isn't as fast as
|
||||
faster than the normal fork() model, and compared to in-process fuzzing,
|
||||
should be a lot more robust.
|
||||
|
||||
8) Bonus feature #3: new 'trace-pc-guard' mode
|
||||
----------------------------------------------
|
||||
## 8) Bonus feature #3: new 'trace-pc-guard' mode
|
||||
|
||||
Recent versions of LLVM are shipping with a built-in execution tracing feature
|
||||
that provides AFL with the necessary tracing data without the need to
|
||||
@ -208,7 +204,9 @@ post-process the assembly or install any compiler plugins. See:
|
||||
If you have a sufficiently recent compiler and want to give it a try, build
|
||||
afl-clang-fast this way:
|
||||
|
||||
```
|
||||
AFL_TRACE_PC=1 make clean all
|
||||
```
|
||||
|
||||
Note that this mode is currently about 20% slower than "vanilla" afl-clang-fast,
|
||||
and about 5-10% slower than afl-clang. This is likely because the
|
@ -1,5 +1,6 @@
|
||||
Usage
|
||||
=====
|
||||
# NeverZero counters for LLVM instrumentation
|
||||
|
||||
## Usage
|
||||
|
||||
In larger, complex or reiterative programs the map that collects the edge pairs
|
||||
can easily fill up and wrap.
|
||||
@ -18,5 +19,6 @@ in version 9 and onwards.
|
||||
|
||||
If you want to enable this for llvm < 9 then set
|
||||
|
||||
```
|
||||
export AFL_LLVM_NOT_ZERO=1
|
||||
|
||||
```
|
@ -1,6 +1,4 @@
|
||||
========================================
|
||||
Using afl++ with partial instrumentation
|
||||
========================================
|
||||
# Using afl++ with partial instrumentation
|
||||
|
||||
This file describes how you can selectively instrument only the source files
|
||||
that are interesting to you using the LLVM instrumentation provided by
|
||||
@ -8,9 +6,7 @@ Using afl++ with partial instrumentation
|
||||
|
||||
Originally developed by Christian Holler (:decoder) <choller@mozilla.com>.
|
||||
|
||||
|
||||
1) Description and purpose
|
||||
--------------------------
|
||||
## 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
|
||||
@ -23,15 +19,13 @@ mode of AFLFuzz that allows you to specify on a source file level which files
|
||||
should be compiled with or without instrumentation.
|
||||
|
||||
|
||||
2) Building the LLVM module
|
||||
---------------------------
|
||||
## 2) Building the LLVM module
|
||||
|
||||
The new code is part of the existing afl++ LLVM module in the llvm_mode/
|
||||
subdirectory. There is nothing specifically to do :)
|
||||
|
||||
|
||||
3) How to use the partial instrumentation mode
|
||||
----------------------------------------------
|
||||
## 3) How to use the partial instrumentation mode
|
||||
|
||||
In order to build with partial instrumentation, you need to build with
|
||||
afl-clang-fast and afl-clang-fast++ respectively. The only required change is
|
||||
@ -45,21 +39,27 @@ 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 whitelist file containing:
|
||||
|
||||
```
|
||||
feature_a/a1.cpp
|
||||
feature_a/a2.cpp
|
||||
```
|
||||
|
||||
However if the whitelist file contains 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.
|
@ -23,10 +23,10 @@
|
||||
|
||||
#define AFL_MAIN
|
||||
|
||||
#include "../config.h"
|
||||
#include "../types.h"
|
||||
#include "../debug.h"
|
||||
#include "../alloc-inl.h"
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "alloc-inl.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
@ -34,16 +34,15 @@
|
||||
#include <string.h>
|
||||
#include <assert.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 */
|
||||
|
||||
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 */
|
||||
|
||||
/* Try to find the runtime libraries. If that fails, abort. */
|
||||
|
||||
static void find_obj(u8* argv0) {
|
||||
|
||||
u8 *afl_path = getenv("AFL_PATH");
|
||||
u8* afl_path = getenv("AFL_PATH");
|
||||
u8 *slash, *tmp;
|
||||
|
||||
if (afl_path) {
|
||||
@ -51,9 +50,11 @@ static void find_obj(u8* argv0) {
|
||||
tmp = alloc_printf("%s/afl-llvm-rt.o", afl_path);
|
||||
|
||||
if (!access(tmp, R_OK)) {
|
||||
|
||||
obj_path = afl_path;
|
||||
ck_free(tmp);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ck_free(tmp);
|
||||
@ -64,7 +65,7 @@ static void find_obj(u8* argv0) {
|
||||
|
||||
if (slash) {
|
||||
|
||||
u8 *dir;
|
||||
u8* dir;
|
||||
|
||||
*slash = 0;
|
||||
dir = ck_strdup(argv0);
|
||||
@ -73,9 +74,11 @@ static void find_obj(u8* argv0) {
|
||||
tmp = alloc_printf("%s/afl-llvm-rt.o", dir);
|
||||
|
||||
if (!access(tmp, R_OK)) {
|
||||
|
||||
obj_path = dir;
|
||||
ck_free(tmp);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ck_free(tmp);
|
||||
@ -84,33 +87,43 @@ static void find_obj(u8* argv0) {
|
||||
}
|
||||
|
||||
if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) {
|
||||
|
||||
obj_path = AFL_PATH;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so.cc'. Please set AFL_PATH");
|
||||
|
||||
}
|
||||
FATAL(
|
||||
"Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so.cc'. 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, bit_mode = 0;
|
||||
u8 *name;
|
||||
u8 fortify_set = 0, asan_set = 0, x_set = 0, maybe_linking = 1, bit_mode = 0;
|
||||
u8* name;
|
||||
|
||||
cc_params = ck_alloc((argc + 128) * sizeof(u8*));
|
||||
|
||||
name = strrchr(argv[0], '/');
|
||||
if (!name) name = argv[0]; else name++;
|
||||
if (!name)
|
||||
name = argv[0];
|
||||
else
|
||||
name++;
|
||||
|
||||
if (!strcmp(name, "afl-clang-fast++")) {
|
||||
|
||||
u8* alt_cxx = getenv("AFL_CXX");
|
||||
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
|
||||
|
||||
} else {
|
||||
|
||||
u8* alt_cc = getenv("AFL_CC");
|
||||
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
|
||||
|
||||
}
|
||||
|
||||
/* There are three ways to compile with afl-clang-fast. In the traditional
|
||||
@ -118,36 +131,50 @@ static void edit_params(u32 argc, char** argv) {
|
||||
much faster but has less coverage. Finally tere is the experimental
|
||||
'trace-pc-guard' mode, we use native LLVM instrumentation callbacks
|
||||
instead. For trace-pc-guard see:
|
||||
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards */
|
||||
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards
|
||||
*/
|
||||
|
||||
// laf
|
||||
if (getenv("LAF_SPLIT_SWITCHES")||getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) {
|
||||
if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = alloc_printf("%s/split-switches-pass.so", obj_path);
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/split-switches-pass.so", obj_path);
|
||||
|
||||
}
|
||||
|
||||
if (getenv("LAF_TRANSFORM_COMPARES")||getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
|
||||
if (getenv("LAF_TRANSFORM_COMPARES") ||
|
||||
getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = alloc_printf("%s/compare-transform-pass.so", obj_path);
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/compare-transform-pass.so", obj_path);
|
||||
|
||||
}
|
||||
|
||||
if (getenv("LAF_SPLIT_COMPARES")||getenv("AFL_LLVM_LAF_SPLIT_COMPARES")) {
|
||||
if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES")) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = alloc_printf("%s/split-compares-pass.so", obj_path);
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("%s/split-compares-pass.so", obj_path);
|
||||
|
||||
}
|
||||
|
||||
// /laf
|
||||
|
||||
#ifdef USE_TRACE_PC
|
||||
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default
|
||||
//cc_params[cc_par_cnt++] = "-mllvm";
|
||||
//cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-cmp,trace-div,trace-gep";
|
||||
//cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
|
||||
cc_params[cc_par_cnt++] =
|
||||
"-fsanitize-coverage=trace-pc-guard"; // edge coverage by default
|
||||
// cc_params[cc_par_cnt++] = "-mllvm";
|
||||
// cc_params[cc_par_cnt++] =
|
||||
// "-fsanitize-coverage=trace-cmp,trace-div,trace-gep";
|
||||
// cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
|
||||
#else
|
||||
cc_params[cc_par_cnt++] = "-Xclang";
|
||||
cc_params[cc_par_cnt++] = "-load";
|
||||
@ -156,7 +183,7 @@ static void edit_params(u32 argc, char** argv) {
|
||||
cc_params[cc_par_cnt++] = alloc_printf("%s/libLLVMInsTrim.so", obj_path);
|
||||
else
|
||||
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
|
||||
#endif /* ^USE_TRACE_PC */
|
||||
#endif /* ^USE_TRACE_PC */
|
||||
|
||||
cc_params[cc_par_cnt++] = "-Qunused-arguments";
|
||||
|
||||
@ -165,6 +192,7 @@ static void edit_params(u32 argc, char** argv) {
|
||||
if (argc == 1 && !strcmp(argv[1], "-v")) maybe_linking = 0;
|
||||
|
||||
while (--argc) {
|
||||
|
||||
u8* cur = *(++argv);
|
||||
|
||||
if (!strcmp(cur, "-m32")) bit_mode = 32;
|
||||
@ -175,15 +203,15 @@ static void edit_params(u32 argc, char** argv) {
|
||||
if (!strcmp(cur, "-c") || !strcmp(cur, "-S") || !strcmp(cur, "-E"))
|
||||
maybe_linking = 0;
|
||||
|
||||
if (!strcmp(cur, "-fsanitize=address") ||
|
||||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;
|
||||
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;
|
||||
|
||||
if (!strcmp(cur, "-Wl,-z,defs") ||
|
||||
!strcmp(cur, "-Wl,--no-undefined")) continue;
|
||||
if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined"))
|
||||
continue;
|
||||
|
||||
cc_params[cc_par_cnt++] = cur;
|
||||
|
||||
@ -193,8 +221,7 @@ static void edit_params(u32 argc, char** argv) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-fstack-protector-all";
|
||||
|
||||
if (!fortify_set)
|
||||
cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
|
||||
if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
|
||||
|
||||
}
|
||||
|
||||
@ -202,8 +229,7 @@ static void edit_params(u32 argc, char** argv) {
|
||||
|
||||
if (getenv("AFL_USE_ASAN")) {
|
||||
|
||||
if (getenv("AFL_USE_MSAN"))
|
||||
FATAL("ASAN and MSAN are mutually exclusive");
|
||||
if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive");
|
||||
|
||||
if (getenv("AFL_HARDEN"))
|
||||
FATAL("ASAN and AFL_HARDEN are mutually exclusive");
|
||||
@ -213,8 +239,7 @@ static void edit_params(u32 argc, char** argv) {
|
||||
|
||||
} else if (getenv("AFL_USE_MSAN")) {
|
||||
|
||||
if (getenv("AFL_USE_ASAN"))
|
||||
FATAL("ASAN and MSAN are mutually exclusive");
|
||||
if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive");
|
||||
|
||||
if (getenv("AFL_HARDEN"))
|
||||
FATAL("MSAN and AFL_HARDEN are mutually exclusive");
|
||||
@ -231,7 +256,7 @@ static void edit_params(u32 argc, char** argv) {
|
||||
if (getenv("AFL_INST_RATIO"))
|
||||
FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'.");
|
||||
|
||||
#endif /* USE_TRACE_PC */
|
||||
#endif /* USE_TRACE_PC */
|
||||
|
||||
if (!getenv("AFL_DONT_OPTIMIZE")) {
|
||||
|
||||
@ -279,35 +304,41 @@ static void edit_params(u32 argc, char** argv) {
|
||||
|
||||
*/
|
||||
|
||||
cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)="
|
||||
"({ static volatile char *_B __attribute__((used)); "
|
||||
" _B = (char*)\"" PERSIST_SIG "\"; "
|
||||
cc_params[cc_par_cnt++] =
|
||||
"-D__AFL_LOOP(_A)="
|
||||
"({ static volatile char *_B __attribute__((used)); "
|
||||
" _B = (char*)\"" PERSIST_SIG
|
||||
"\"; "
|
||||
#ifdef __APPLE__
|
||||
"__attribute__((visibility(\"default\"))) "
|
||||
"int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
|
||||
"__attribute__((visibility(\"default\"))) "
|
||||
"int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
|
||||
#else
|
||||
"__attribute__((visibility(\"default\"))) "
|
||||
"int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
|
||||
#endif /* ^__APPLE__ */
|
||||
"_L(_A); })";
|
||||
"__attribute__((visibility(\"default\"))) "
|
||||
"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 "\"; "
|
||||
cc_params[cc_par_cnt++] =
|
||||
"-D__AFL_INIT()="
|
||||
"do { static volatile char *_A __attribute__((used)); "
|
||||
" _A = (char*)\"" DEFER_SIG
|
||||
"\"; "
|
||||
#ifdef __APPLE__
|
||||
"__attribute__((visibility(\"default\"))) "
|
||||
"void _I(void) __asm__(\"___afl_manual_init\"); "
|
||||
"__attribute__((visibility(\"default\"))) "
|
||||
"void _I(void) __asm__(\"___afl_manual_init\"); "
|
||||
#else
|
||||
"__attribute__((visibility(\"default\"))) "
|
||||
"void _I(void) __asm__(\"__afl_manual_init\"); "
|
||||
#endif /* ^__APPLE__ */
|
||||
"_I(); } while (0)";
|
||||
"__attribute__((visibility(\"default\"))) "
|
||||
"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";
|
||||
|
||||
}
|
||||
|
||||
switch (bit_mode) {
|
||||
@ -340,7 +371,6 @@ static void edit_params(u32 argc, char** argv) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Main entry point */
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@ -348,46 +378,53 @@ int main(int argc, char** argv) {
|
||||
if (isatty(2) && !getenv("AFL_QUIET")) {
|
||||
|
||||
#ifdef USE_TRACE_PC
|
||||
SAYF(cCYA "afl-clang-fast" VERSION cRST " [tpcg] by <lszekeres@google.com>\n");
|
||||
SAYF(cCYA "afl-clang-fast" VERSION cRST
|
||||
" [tpcg] by <lszekeres@google.com>\n");
|
||||
#else
|
||||
SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n");
|
||||
#endif /* ^USE_TRACE_PC */
|
||||
SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n");
|
||||
#endif /* ^USE_TRACE_PC */
|
||||
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
|
||||
SAYF("\n"
|
||||
"This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
|
||||
"for clang, letting you recompile third-party code with the required runtime\n"
|
||||
"instrumentation. A common use pattern would be one of the following:\n\n"
|
||||
SAYF(
|
||||
"\n"
|
||||
"This is a helper application for afl-fuzz. It serves as a drop-in "
|
||||
"replacement\n"
|
||||
"for clang, 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-clang-fast ./configure\n"
|
||||
" CXX=%s/afl-clang-fast++ ./configure\n\n"
|
||||
" CC=%s/afl-clang-fast ./configure\n"
|
||||
" CXX=%s/afl-clang-fast++ ./configure\n\n"
|
||||
|
||||
"In contrast to the traditional afl-clang tool, this version is implemented as\n"
|
||||
"an LLVM pass and tends to offer improved performance with slow programs.\n\n"
|
||||
"In contrast to the traditional afl-clang tool, this version is "
|
||||
"implemented as\n"
|
||||
"an LLVM pass and tends to offer improved performance with slow "
|
||||
"programs.\n\n"
|
||||
|
||||
"You can specify custom next-stage toolchain via AFL_CC and AFL_CXX. Setting\n"
|
||||
"AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
|
||||
BIN_PATH, BIN_PATH);
|
||||
"You can specify custom next-stage toolchain via AFL_CC and AFL_CXX. "
|
||||
"Setting\n"
|
||||
"AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
|
||||
BIN_PATH, BIN_PATH);
|
||||
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
find_obj(argv[0]);
|
||||
|
||||
edit_params(argc, argv);
|
||||
|
||||
/*
|
||||
int i = 0;
|
||||
printf("EXEC:");
|
||||
while (cc_params[i] != NULL)
|
||||
printf(" %s", cc_params[i++]);
|
||||
printf("\n");
|
||||
*/
|
||||
/*
|
||||
int i = 0;
|
||||
printf("EXEC:");
|
||||
while (cc_params[i] != NULL)
|
||||
printf(" %s", cc_params[i++]);
|
||||
printf("\n");
|
||||
*/
|
||||
|
||||
execvp(cc_params[0], (char**)cc_params);
|
||||
|
||||
@ -396,3 +433,4 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,8 @@
|
||||
|
||||
#define AFL_LLVM_PASS
|
||||
|
||||
#include "../config.h"
|
||||
#include "../debug.h"
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -48,50 +48,52 @@ using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
class AFLCoverage : public ModulePass {
|
||||
class AFLCoverage : public ModulePass {
|
||||
|
||||
public:
|
||||
public:
|
||||
static char ID;
|
||||
AFLCoverage() : ModulePass(ID) {
|
||||
|
||||
char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instWhiteListFilename) {
|
||||
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instWhiteListFilename);
|
||||
if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
||||
getline(fileStream, line);
|
||||
while (fileStream) {
|
||||
|
||||
myWhitelist.push_back(line);
|
||||
getline(fileStream, line);
|
||||
|
||||
static char ID;
|
||||
AFLCoverage() : ModulePass(ID) {
|
||||
char* instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||
if (instWhiteListFilename) {
|
||||
std::string line;
|
||||
std::ifstream fileStream;
|
||||
fileStream.open(instWhiteListFilename);
|
||||
if (!fileStream)
|
||||
report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
||||
getline(fileStream, line);
|
||||
while (fileStream) {
|
||||
myWhitelist.push_back(line);
|
||||
getline(fileStream, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override;
|
||||
}
|
||||
|
||||
// StringRef getPassName() const override {
|
||||
// return "American Fuzzy Lop Instrumentation";
|
||||
// }
|
||||
}
|
||||
|
||||
protected:
|
||||
bool runOnModule(Module &M) override;
|
||||
|
||||
std::list<std::string> myWhitelist;
|
||||
// StringRef getPassName() const override {
|
||||
|
||||
};
|
||||
// return "American Fuzzy Lop Instrumentation";
|
||||
// }
|
||||
|
||||
}
|
||||
protected:
|
||||
std::list<std::string> myWhitelist;
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
char AFLCoverage::ID = 0;
|
||||
|
||||
|
||||
bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
LLVMContext &C = M.getContext();
|
||||
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
unsigned int cur_loc = 0;
|
||||
|
||||
@ -103,11 +105,13 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
SAYF(cCYA "afl-llvm-pass" VERSION cRST " by <lszekeres@google.com>\n");
|
||||
|
||||
} else be_quiet = 1;
|
||||
} else
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
/* Decide instrumentation ratio */
|
||||
|
||||
char* inst_ratio_str = getenv("AFL_INST_RATIO");
|
||||
char * inst_ratio_str = getenv("AFL_INST_RATIO");
|
||||
unsigned int inst_ratio = 100;
|
||||
|
||||
if (inst_ratio_str) {
|
||||
@ -119,7 +123,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
char* neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO");
|
||||
char *neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO");
|
||||
#endif
|
||||
|
||||
/* Get globals for the SHM region and the previous location. Note that
|
||||
@ -129,9 +133,14 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
|
||||
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
||||
|
||||
#ifdef __ANDROID__
|
||||
GlobalVariable *AFLPrevLoc = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc",
|
||||
0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc");
|
||||
#else
|
||||
GlobalVariable *AFLPrevLoc = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0,
|
||||
GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
#endif
|
||||
|
||||
/* Instrument all the things! */
|
||||
|
||||
@ -141,58 +150,77 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
for (auto &BB : F) {
|
||||
|
||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
|
||||
if (!myWhitelist.empty()) {
|
||||
bool instrumentBlock = false;
|
||||
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
if ( Loc ) {
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
bool instrumentBlock = false;
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
StringRef instFilename = cDILoc->getFilename();
|
||||
/* Get the current location using debug information.
|
||||
* For now, just instrument the block if we are not able
|
||||
* to determine our location. */
|
||||
DebugLoc Loc = IP->getDebugLoc();
|
||||
if (Loc) {
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
/* If the original location is empty, try using the inlined location */
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
}
|
||||
}
|
||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||
|
||||
unsigned int instLine = cDILoc->getLine();
|
||||
StringRef instFilename = cDILoc->getFilename();
|
||||
|
||||
if (instFilename.str().empty()) {
|
||||
|
||||
/* If the original location is empty, try using the inlined location
|
||||
*/
|
||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||
if (oDILoc) {
|
||||
|
||||
instFilename = oDILoc->getFilename();
|
||||
instLine = oDILoc->getLine();
|
||||
|
||||
}
|
||||
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin(); it != myWhitelist.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.str().length() >= it->length()) {
|
||||
if (instFilename.str().compare(instFilename.str().length() - it->length(), it->length(), *it) == 0) {
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) continue;
|
||||
}
|
||||
/* Continue only if we know where we actually are */
|
||||
if (!instFilename.str().empty()) {
|
||||
|
||||
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||
it != myWhitelist.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.str().length() >= it->length()) {
|
||||
|
||||
if (instFilename.str().compare(
|
||||
instFilename.str().length() - it->length(),
|
||||
it->length(), *it) == 0) {
|
||||
|
||||
instrumentBlock = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Either we couldn't figure out our location or the location is
|
||||
* not whitelisted, so we skip instrumentation. */
|
||||
if (!instrumentBlock) continue;
|
||||
|
||||
}
|
||||
|
||||
if (AFL_R(100) >= inst_ratio) continue;
|
||||
|
||||
/* Make up cur_loc */
|
||||
|
||||
//cur_loc++;
|
||||
// cur_loc++;
|
||||
cur_loc = AFL_R(MAP_SIZE);
|
||||
|
||||
// only instrument if this basic block is the destination of a previous
|
||||
@ -200,24 +228,27 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
// 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;
|
||||
//fprintf(stderr, "BB %u: ", cur_loc);
|
||||
// fprintf(stderr, "BB %u: ", cur_loc);
|
||||
for (BasicBlock *Pred : predecessors(&BB)) {
|
||||
|
||||
int count = 0;
|
||||
if (more_than_one == -1)
|
||||
more_than_one = 0;
|
||||
//fprintf(stderr, " %p=>", Pred);
|
||||
if (more_than_one == -1) more_than_one = 0;
|
||||
// fprintf(stderr, " %p=>", Pred);
|
||||
for (BasicBlock *Succ : successors(Pred)) {
|
||||
//if (count > 0)
|
||||
|
||||
// if (count > 0)
|
||||
// fprintf(stderr, "|");
|
||||
if (Succ != NULL) count++;
|
||||
//fprintf(stderr, "%p", Succ);
|
||||
// fprintf(stderr, "%p", Succ);
|
||||
|
||||
}
|
||||
if (count > 1)
|
||||
more_than_one = 1;
|
||||
|
||||
if (count > 1) more_than_one = 1;
|
||||
|
||||
}
|
||||
//fprintf(stderr, " == %d\n", more_than_one);
|
||||
if (more_than_one != 1)
|
||||
continue;
|
||||
|
||||
// fprintf(stderr, " == %d\n", more_than_one);
|
||||
if (more_than_one != 1) continue;
|
||||
|
||||
ConstantInt *CurLoc = ConstantInt::get(Int32Ty, cur_loc);
|
||||
|
||||
@ -231,7 +262,8 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
|
||||
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
Value *MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc));
|
||||
Value *MapPtrIdx =
|
||||
IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc));
|
||||
|
||||
/* Update bitmap */
|
||||
|
||||
@ -241,7 +273,9 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
if (neverZero_counters_str != NULL) { // with llvm 9 we make this the default as the bug in llvm is then fixed
|
||||
if (neverZero_counters_str !=
|
||||
NULL) { // with llvm 9 we make this the default as the bug in llvm is
|
||||
// then fixed
|
||||
#endif
|
||||
/* hexcoder: Realize a counter that skips zero during overflow.
|
||||
* Once this counter reaches its maximum value, it next increments to 1
|
||||
@ -252,48 +286,67 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
* Counter + 1 -> {Counter, OverflowFlag}
|
||||
* Counter + OverflowFlag -> Counter
|
||||
*/
|
||||
/* // we keep the old solutions just in case
|
||||
// Solution #1
|
||||
if (neverZero_counters_str[0] == '1') {
|
||||
CallInst *AddOv = IRB.CreateBinaryIntrinsic(Intrinsic::uadd_with_overflow, Counter, ConstantInt::get(Int8Ty, 1));
|
||||
AddOv->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
Value *SumWithOverflowBit = AddOv;
|
||||
Incr = IRB.CreateAdd(IRB.CreateExtractValue(SumWithOverflowBit, 0), // sum
|
||||
IRB.CreateZExt( // convert from one bit type to 8 bits type
|
||||
IRB.CreateExtractValue(SumWithOverflowBit, 1), // overflow
|
||||
Int8Ty));
|
||||
// Solution #2
|
||||
} else if (neverZero_counters_str[0] == '2') {
|
||||
auto cf = IRB.CreateICmpEQ(Counter, ConstantInt::get(Int8Ty, 255));
|
||||
Value *HowMuch = IRB.CreateAdd(ConstantInt::get(Int8Ty, 1), cf);
|
||||
Incr = IRB.CreateAdd(Counter, HowMuch);
|
||||
// Solution #3
|
||||
} else if (neverZero_counters_str[0] == '3') {
|
||||
*/
|
||||
// this is the solution we choose because llvm9 should do the right thing here
|
||||
auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
/* // we keep the old solutions just in case
|
||||
// Solution #1
|
||||
if (neverZero_counters_str[0] == '1') {
|
||||
|
||||
CallInst *AddOv =
|
||||
IRB.CreateBinaryIntrinsic(Intrinsic::uadd_with_overflow, Counter,
|
||||
ConstantInt::get(Int8Ty, 1));
|
||||
AddOv->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None)); Value *SumWithOverflowBit = AddOv; Incr =
|
||||
IRB.CreateAdd(IRB.CreateExtractValue(SumWithOverflowBit, 0), // sum
|
||||
IRB.CreateZExt( // convert from one bit
|
||||
type to 8 bits type IRB.CreateExtractValue(SumWithOverflowBit, 1), //
|
||||
overflow Int8Ty));
|
||||
// Solution #2
|
||||
|
||||
} else if (neverZero_counters_str[0] == '2') {
|
||||
|
||||
auto cf = IRB.CreateICmpEQ(Counter,
|
||||
ConstantInt::get(Int8Ty, 255)); Value *HowMuch =
|
||||
IRB.CreateAdd(ConstantInt::get(Int8Ty, 1), cf); Incr =
|
||||
IRB.CreateAdd(Counter, HowMuch);
|
||||
// Solution #3
|
||||
|
||||
} else if (neverZero_counters_str[0] == '3') {
|
||||
|
||||
*/
|
||||
// this is the solution we choose because llvm9 should do the right
|
||||
// thing here
|
||||
auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
/*
|
||||
// Solution #4
|
||||
|
||||
} else if (neverZero_counters_str[0] == '4') {
|
||||
|
||||
auto cf = IRB.CreateICmpULT(Incr, ConstantInt::get(Int8Ty, 1));
|
||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||
Incr = IRB.CreateAdd(Incr, carry);
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "Error: unknown value for AFL_NZERO_COUNTS: %s (valid is 1-4)\n", neverZero_counters_str);
|
||||
exit(-1);
|
||||
|
||||
fprintf(stderr, "Error: unknown value for AFL_NZERO_COUNTS: %s
|
||||
(valid is 1-4)\n", neverZero_counters_str); exit(-1);
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
IRB.CreateStore(Incr, MapPtrIdx)->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
IRB.CreateStore(Incr, MapPtrIdx)
|
||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
/* Set prev_loc to cur_loc >> 1 */
|
||||
|
||||
StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
|
||||
StoreInst *Store =
|
||||
IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
|
||||
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
inst_blocks++;
|
||||
@ -304,11 +357,16 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
if (!be_quiet) {
|
||||
|
||||
if (!inst_blocks) WARNF("No instrumentation targets found.");
|
||||
else OKF("Instrumented %u locations (%s mode, ratio %u%%).",
|
||||
inst_blocks, getenv("AFL_HARDEN") ? "hardened" :
|
||||
((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) ?
|
||||
"ASAN/MSAN" : "non-hardened"), inst_ratio);
|
||||
if (!inst_blocks)
|
||||
WARNF("No instrumentation targets found.");
|
||||
else
|
||||
OKF("Instrumented %u locations (%s mode, ratio %u%%).", inst_blocks,
|
||||
getenv("AFL_HARDEN")
|
||||
? "hardened"
|
||||
: ((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN"))
|
||||
? "ASAN/MSAN"
|
||||
: "non-hardened"),
|
||||
inst_ratio);
|
||||
|
||||
}
|
||||
|
||||
@ -316,7 +374,6 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void registerAFLPass(const PassManagerBuilder &,
|
||||
legacy::PassManagerBase &PM) {
|
||||
|
||||
@ -324,9 +381,9 @@ static void registerAFLPass(const PassManagerBuilder &,
|
||||
|
||||
}
|
||||
|
||||
|
||||
static RegisterStandardPasses RegisterAFLPass(
|
||||
PassManagerBuilder::EP_OptimizerLast, registerAFLPass);
|
||||
|
||||
static RegisterStandardPasses RegisterAFLPass0(
|
||||
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
|
||||
|
||||
|
@ -19,8 +19,11 @@
|
||||
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
#include "../types.h"
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -39,61 +42,67 @@
|
||||
the LLVM-generated runtime initialization pass, not before. */
|
||||
|
||||
#ifdef USE_TRACE_PC
|
||||
# define CONST_PRIO 5
|
||||
#define CONST_PRIO 5
|
||||
#else
|
||||
# define CONST_PRIO 0
|
||||
#endif /* ^USE_TRACE_PC */
|
||||
#define CONST_PRIO 0
|
||||
#endif /* ^USE_TRACE_PC */
|
||||
|
||||
#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. */
|
||||
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;
|
||||
#else
|
||||
__thread u32 __afl_prev_loc;
|
||||
|
||||
#endif
|
||||
|
||||
/* Running in persistent mode? */
|
||||
|
||||
static u8 is_persistent;
|
||||
|
||||
|
||||
/* SHM setup. */
|
||||
|
||||
static void __afl_map_shm(void) {
|
||||
|
||||
u8 *id_str = getenv(SHM_ENV_VAR);
|
||||
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;
|
||||
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) {
|
||||
|
||||
printf("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;
|
||||
|
||||
printf("mmap() failed\n");
|
||||
exit(2);
|
||||
|
||||
}
|
||||
|
||||
__afl_area_ptr = shm_base;
|
||||
@ -105,7 +114,7 @@ static void __afl_map_shm(void) {
|
||||
|
||||
/* Whooooops. */
|
||||
|
||||
if (__afl_area_ptr == (void *)-1) _exit(1);
|
||||
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. */
|
||||
@ -116,16 +125,15 @@ static void __afl_map_shm(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Fork server logic. */
|
||||
|
||||
static void __afl_start_forkserver(void) {
|
||||
|
||||
static u8 tmp[4];
|
||||
s32 child_pid;
|
||||
s32 child_pid;
|
||||
|
||||
u8 child_stopped = 0;
|
||||
|
||||
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,
|
||||
@ -147,8 +155,10 @@ static void __afl_start_forkserver(void) {
|
||||
process. */
|
||||
|
||||
if (child_stopped && was_killed) {
|
||||
|
||||
child_stopped = 0;
|
||||
if (waitpid(child_pid, &status, 0) < 0) _exit(1);
|
||||
|
||||
}
|
||||
|
||||
if (!child_stopped) {
|
||||
@ -161,12 +171,13 @@ static void __afl_start_forkserver(void) {
|
||||
/* In child process: close fds, resume execution. */
|
||||
|
||||
if (!child_pid) {
|
||||
|
||||
signal(SIGCHLD, old_sigchld_handler);
|
||||
|
||||
close(FORKSRV_FD);
|
||||
close(FORKSRV_FD + 1);
|
||||
return;
|
||||
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -200,7 +211,6 @@ static void __afl_start_forkserver(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* A simplified persistent mode handler, used as explained in README.llvm. */
|
||||
|
||||
int __afl_persistent_loop(unsigned int max_cnt) {
|
||||
@ -220,9 +230,10 @@ int __afl_persistent_loop(unsigned int max_cnt) {
|
||||
memset(__afl_area_ptr, 0, MAP_SIZE);
|
||||
__afl_area_ptr[0] = 1;
|
||||
__afl_prev_loc = 0;
|
||||
|
||||
}
|
||||
|
||||
cycle_cnt = max_cnt;
|
||||
cycle_cnt = max_cnt;
|
||||
first_pass = 0;
|
||||
return 1;
|
||||
|
||||
@ -255,7 +266,6 @@ int __afl_persistent_loop(unsigned int max_cnt) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* This one can be called from user code when deferred forkserver mode
|
||||
is enabled. */
|
||||
|
||||
@ -273,7 +283,6 @@ void __afl_manual_init(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Proper initialization routine. */
|
||||
|
||||
__attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) {
|
||||
@ -286,7 +295,6 @@ __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard.
|
||||
It remains non-operational in the traditional, plugin-backed LLVM mode.
|
||||
For more info about 'trace-pc-guard', see README.llvm.
|
||||
@ -295,9 +303,10 @@ __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) {
|
||||
edge (as opposed to every basic block). */
|
||||
|
||||
void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
|
||||
__afl_area_ptr[*guard]++;
|
||||
}
|
||||
|
||||
__afl_area_ptr[*guard]++;
|
||||
|
||||
}
|
||||
|
||||
/* Init callback. Populates instrumentation IDs. Note that we're using
|
||||
ID of 0 as a special value to indicate non-instrumented bits. That may
|
||||
@ -314,8 +323,10 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) {
|
||||
if (x) inst_ratio = atoi(x);
|
||||
|
||||
if (!inst_ratio || inst_ratio > 100) {
|
||||
|
||||
fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n");
|
||||
abort();
|
||||
|
||||
}
|
||||
|
||||
/* Make sure that the first element in the range is always set - we use that
|
||||
@ -326,11 +337,14 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) {
|
||||
|
||||
while (start < stop) {
|
||||
|
||||
if (R(100) < inst_ratio) *start = R(MAP_SIZE - 1) + 1;
|
||||
else *start = 0;
|
||||
if (R(100) < inst_ratio)
|
||||
*start = R(MAP_SIZE - 1) + 1;
|
||||
else
|
||||
*start = 0;
|
||||
|
||||
start++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -36,198 +36,236 @@ using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
class CompareTransform : public ModulePass {
|
||||
class CompareTransform : public ModulePass {
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
CompareTransform() : ModulePass(ID) {
|
||||
}
|
||||
public:
|
||||
static char ID;
|
||||
CompareTransform() : ModulePass(ID) {
|
||||
|
||||
bool runOnModule(Module &M) override;
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override;
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 4
|
||||
const char * getPassName() const override {
|
||||
#else
|
||||
StringRef getPassName() const override {
|
||||
#endif
|
||||
return "transforms compare functions";
|
||||
}
|
||||
private:
|
||||
bool transformCmps(Module &M, const bool processStrcmp, const bool processMemcmp
|
||||
,const bool processStrncmp, const bool processStrcasecmp, const bool processStrncasecmp);
|
||||
};
|
||||
}
|
||||
const char *getPassName() const override {
|
||||
|
||||
#else
|
||||
StringRef getPassName() const override {
|
||||
|
||||
#endif
|
||||
return "transforms compare functions";
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
bool transformCmps(Module &M, const bool processStrcmp,
|
||||
const bool processMemcmp, const bool processStrncmp,
|
||||
const bool processStrcasecmp,
|
||||
const bool processStrncasecmp);
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
char CompareTransform::ID = 0;
|
||||
|
||||
bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const bool processMemcmp
|
||||
, const bool processStrncmp, const bool processStrcasecmp, const bool processStrncasecmp) {
|
||||
bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
const bool processMemcmp,
|
||||
const bool processStrncmp,
|
||||
const bool processStrcasecmp,
|
||||
const bool processStrncasecmp) {
|
||||
|
||||
std::vector<CallInst*> calls;
|
||||
LLVMContext &C = M.getContext();
|
||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||
IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
|
||||
std::vector<CallInst *> calls;
|
||||
LLVMContext & C = M.getContext();
|
||||
IntegerType * Int8Ty = IntegerType::getInt8Ty(C);
|
||||
IntegerType * Int32Ty = IntegerType::getInt32Ty(C);
|
||||
IntegerType * Int64Ty = IntegerType::getInt64Ty(C);
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
Constant*
|
||||
Constant *
|
||||
#else
|
||||
FunctionCallee
|
||||
#endif
|
||||
c = M.getOrInsertFunction("tolower",
|
||||
Int32Ty,
|
||||
Int32Ty
|
||||
c = M.getOrInsertFunction("tolower", Int32Ty, Int32Ty
|
||||
#if LLVM_VERSION_MAJOR < 5
|
||||
, nullptr
|
||||
,
|
||||
nullptr
|
||||
#endif
|
||||
);
|
||||
#if LLVM_VERSION_MAJOR < 9
|
||||
Function *tolowerFn = cast<Function>(c);
|
||||
#else
|
||||
FunctionCallee tolowerFn = c;
|
||||
#endif
|
||||
);
|
||||
Function* tolowerFn = cast<Function>(c);
|
||||
|
||||
/* iterate over all functions, bbs and instruction and add suitable calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp */
|
||||
/* iterate over all functions, bbs and instruction and add suitable calls to
|
||||
* strcmp/memcmp/strncmp/strcasecmp/strncasecmp */
|
||||
for (auto &F : M) {
|
||||
|
||||
for (auto &BB : F) {
|
||||
for(auto &IN: BB) {
|
||||
CallInst* callInst = nullptr;
|
||||
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CallInst *callInst = nullptr;
|
||||
|
||||
if ((callInst = dyn_cast<CallInst>(&IN))) {
|
||||
|
||||
bool isStrcmp = processStrcmp;
|
||||
bool isMemcmp = processMemcmp;
|
||||
bool isStrncmp = processStrncmp;
|
||||
bool isStrcasecmp = processStrcasecmp;
|
||||
bool isStrcmp = processStrcmp;
|
||||
bool isMemcmp = processMemcmp;
|
||||
bool isStrncmp = processStrncmp;
|
||||
bool isStrcasecmp = processStrcasecmp;
|
||||
bool isStrncasecmp = processStrncasecmp;
|
||||
|
||||
Function *Callee = callInst->getCalledFunction();
|
||||
if (!Callee)
|
||||
continue;
|
||||
if (callInst->getCallingConv() != llvm::CallingConv::C)
|
||||
continue;
|
||||
if (!Callee) continue;
|
||||
if (callInst->getCallingConv() != llvm::CallingConv::C) continue;
|
||||
StringRef FuncName = Callee->getName();
|
||||
isStrcmp &= !FuncName.compare(StringRef("strcmp"));
|
||||
isMemcmp &= !FuncName.compare(StringRef("memcmp"));
|
||||
isStrncmp &= !FuncName.compare(StringRef("strncmp"));
|
||||
isStrcasecmp &= !FuncName.compare(StringRef("strcasecmp"));
|
||||
isStrcmp &= !FuncName.compare(StringRef("strcmp"));
|
||||
isMemcmp &= !FuncName.compare(StringRef("memcmp"));
|
||||
isStrncmp &= !FuncName.compare(StringRef("strncmp"));
|
||||
isStrcasecmp &= !FuncName.compare(StringRef("strcasecmp"));
|
||||
isStrncasecmp &= !FuncName.compare(StringRef("strncasecmp"));
|
||||
|
||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && !isStrncasecmp)
|
||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
|
||||
!isStrncasecmp)
|
||||
continue;
|
||||
|
||||
/* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function prototype */
|
||||
/* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function
|
||||
* prototype */
|
||||
FunctionType *FT = Callee->getFunctionType();
|
||||
|
||||
|
||||
isStrcmp &= FT->getNumParams() == 2 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext());
|
||||
isStrcasecmp &= FT->getNumParams() == 2 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext());
|
||||
isMemcmp &= FT->getNumParams() == 3 &&
|
||||
isStrcmp &=
|
||||
FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext());
|
||||
isStrcasecmp &=
|
||||
FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext());
|
||||
isMemcmp &= FT->getNumParams() == 3 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0)->isPointerTy() &&
|
||||
FT->getParamType(1)->isPointerTy() &&
|
||||
FT->getParamType(2)->isIntegerTy();
|
||||
isStrncmp &= FT->getNumParams() == 3 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()) &&
|
||||
FT->getParamType(2)->isIntegerTy();
|
||||
isStrncmp &= FT->getNumParams() == 3 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) ==
|
||||
IntegerType::getInt8PtrTy(M.getContext()) &&
|
||||
FT->getParamType(2)->isIntegerTy();
|
||||
isStrncasecmp &= FT->getNumParams() == 3 &&
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()) &&
|
||||
FT->getParamType(2)->isIntegerTy();
|
||||
FT->getReturnType()->isIntegerTy(32) &&
|
||||
FT->getParamType(0) == FT->getParamType(1) &&
|
||||
FT->getParamType(0) ==
|
||||
IntegerType::getInt8PtrTy(M.getContext()) &&
|
||||
FT->getParamType(2)->isIntegerTy();
|
||||
|
||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && !isStrncasecmp)
|
||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
|
||||
!isStrncasecmp)
|
||||
continue;
|
||||
|
||||
/* is a str{n,}{case,}cmp/memcmp, check if we have
|
||||
* str{case,}cmp(x, "const") or str{case,}cmp("const", x)
|
||||
* strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..)
|
||||
* memcmp(x, "const", ..) or memcmp("const", x, ..) */
|
||||
Value *Str1P = callInst->getArgOperand(0), *Str2P = callInst->getArgOperand(1);
|
||||
Value *Str1P = callInst->getArgOperand(0),
|
||||
*Str2P = callInst->getArgOperand(1);
|
||||
StringRef Str1, Str2;
|
||||
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
||||
bool HasStr2 = getConstantStringInfo(Str2P, Str2);
|
||||
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
||||
bool HasStr2 = getConstantStringInfo(Str2P, Str2);
|
||||
|
||||
/* handle cases of one string is const, one string is variable */
|
||||
if (!(HasStr1 ^ HasStr2))
|
||||
continue;
|
||||
if (!(HasStr1 ^ HasStr2)) continue;
|
||||
|
||||
if (isMemcmp || isStrncmp || isStrncasecmp) {
|
||||
|
||||
/* check if third operand is a constant integer
|
||||
* strlen("constStr") and sizeof() are treated as constant */
|
||||
Value *op2 = callInst->getArgOperand(2);
|
||||
ConstantInt* ilen = dyn_cast<ConstantInt>(op2);
|
||||
if (!ilen)
|
||||
continue;
|
||||
/* final precaution: if size of compare is larger than constant string skip it*/
|
||||
uint64_t literalLength = HasStr1 ? GetStringLength(Str1P) : GetStringLength(Str2P);
|
||||
if (literalLength < ilen->getZExtValue())
|
||||
continue;
|
||||
Value * op2 = callInst->getArgOperand(2);
|
||||
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
|
||||
if (!ilen) continue;
|
||||
/* final precaution: if size of compare is larger than constant
|
||||
* string skip it*/
|
||||
uint64_t literalLength =
|
||||
HasStr1 ? GetStringLength(Str1P) : GetStringLength(Str2P);
|
||||
if (literalLength < ilen->getZExtValue()) continue;
|
||||
|
||||
}
|
||||
|
||||
calls.push_back(callInst);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!calls.size())
|
||||
return false;
|
||||
errs() << "Replacing " << calls.size() << " calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp\n";
|
||||
if (!calls.size()) return false;
|
||||
errs() << "Replacing " << calls.size()
|
||||
<< " calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp\n";
|
||||
|
||||
for (auto &callInst: calls) {
|
||||
for (auto &callInst : calls) {
|
||||
|
||||
Value *Str1P = callInst->getArgOperand(0), *Str2P = callInst->getArgOperand(1);
|
||||
StringRef Str1, Str2, ConstStr;
|
||||
Value *Str1P = callInst->getArgOperand(0),
|
||||
*Str2P = callInst->getArgOperand(1);
|
||||
StringRef Str1, Str2, ConstStr;
|
||||
std::string TmpConstStr;
|
||||
Value *VarStr;
|
||||
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
||||
Value * VarStr;
|
||||
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
||||
getConstantStringInfo(Str2P, Str2);
|
||||
uint64_t constLen, sizedLen;
|
||||
bool isMemcmp = !callInst->getCalledFunction()->getName().compare(StringRef("memcmp"));
|
||||
bool isSizedcmp = isMemcmp
|
||||
|| !callInst->getCalledFunction()->getName().compare(StringRef("strncmp"))
|
||||
|| !callInst->getCalledFunction()->getName().compare(StringRef("strncasecmp"));
|
||||
bool isCaseInsensitive = !callInst->getCalledFunction()->getName().compare(StringRef("strcasecmp"))
|
||||
|| !callInst->getCalledFunction()->getName().compare(StringRef("strncasecmp"));
|
||||
bool isMemcmp =
|
||||
!callInst->getCalledFunction()->getName().compare(StringRef("memcmp"));
|
||||
bool isSizedcmp = isMemcmp ||
|
||||
!callInst->getCalledFunction()->getName().compare(
|
||||
StringRef("strncmp")) ||
|
||||
!callInst->getCalledFunction()->getName().compare(
|
||||
StringRef("strncasecmp"));
|
||||
bool isCaseInsensitive = !callInst->getCalledFunction()->getName().compare(
|
||||
StringRef("strcasecmp")) ||
|
||||
!callInst->getCalledFunction()->getName().compare(
|
||||
StringRef("strncasecmp"));
|
||||
|
||||
if (isSizedcmp) {
|
||||
Value *op2 = callInst->getArgOperand(2);
|
||||
ConstantInt* ilen = dyn_cast<ConstantInt>(op2);
|
||||
|
||||
Value * op2 = callInst->getArgOperand(2);
|
||||
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
|
||||
sizedLen = ilen->getZExtValue();
|
||||
|
||||
}
|
||||
|
||||
if (HasStr1) {
|
||||
|
||||
TmpConstStr = Str1.str();
|
||||
VarStr = Str2P;
|
||||
constLen = isMemcmp ? sizedLen : GetStringLength(Str1P);
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
|
||||
TmpConstStr = Str2.str();
|
||||
VarStr = Str1P;
|
||||
constLen = isMemcmp ? sizedLen : GetStringLength(Str2P);
|
||||
|
||||
}
|
||||
|
||||
/* properly handle zero terminated C strings by adding the terminating 0 to
|
||||
* the StringRef (in comparison to std::string a StringRef has built-in
|
||||
* runtime bounds checking, which makes debugging easier) */
|
||||
TmpConstStr.append("\0", 1); ConstStr = StringRef(TmpConstStr);
|
||||
TmpConstStr.append("\0", 1);
|
||||
ConstStr = StringRef(TmpConstStr);
|
||||
|
||||
if (isSizedcmp && constLen > sizedLen) {
|
||||
constLen = sizedLen;
|
||||
}
|
||||
if (isSizedcmp && constLen > sizedLen) { constLen = sizedLen; }
|
||||
|
||||
errs() << callInst->getCalledFunction()->getName() << ": len " << constLen << ": " << ConstStr << "\n";
|
||||
errs() << callInst->getCalledFunction()->getName() << ": len " << constLen
|
||||
<< ": " << ConstStr << "\n";
|
||||
|
||||
/* split before the call instruction */
|
||||
BasicBlock *bb = callInst->getParent();
|
||||
BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(callInst));
|
||||
BasicBlock *next_bb = BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb);
|
||||
BasicBlock *next_bb =
|
||||
BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb);
|
||||
BranchInst::Create(end_bb, next_bb);
|
||||
PHINode *PN = PHINode::Create(Int32Ty, constLen + 1, "cmp_phi");
|
||||
|
||||
@ -245,71 +283,81 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const
|
||||
|
||||
char c = isCaseInsensitive ? tolower(ConstStr[i]) : ConstStr[i];
|
||||
|
||||
|
||||
BasicBlock::iterator IP = next_bb->getFirstInsertionPt();
|
||||
IRBuilder<> IRB(&*IP);
|
||||
IRBuilder<> IRB(&*IP);
|
||||
|
||||
Value* v = ConstantInt::get(Int64Ty, i);
|
||||
Value *ele = IRB.CreateInBoundsGEP(VarStr, v, "empty");
|
||||
Value *v = ConstantInt::get(Int64Ty, i);
|
||||
Value *ele = IRB.CreateInBoundsGEP(VarStr, v, "empty");
|
||||
Value *load = IRB.CreateLoad(ele);
|
||||
if (isCaseInsensitive) {
|
||||
|
||||
// load >= 'A' && load <= 'Z' ? load | 0x020 : load
|
||||
std::vector<Value *> args;
|
||||
args.push_back(load);
|
||||
load = IRB.CreateCall(tolowerFn, args, "tmp");
|
||||
load = IRB.CreateTrunc(load, Int8Ty);
|
||||
|
||||
}
|
||||
|
||||
Value *isub;
|
||||
if (HasStr1)
|
||||
isub = IRB.CreateSub(ConstantInt::get(Int8Ty, c), load);
|
||||
else
|
||||
isub = IRB.CreateSub(load, ConstantInt::get(Int8Ty, c));
|
||||
|
||||
Value *sext = IRB.CreateSExt(isub, Int32Ty);
|
||||
Value *sext = IRB.CreateSExt(isub, Int32Ty);
|
||||
PN->addIncoming(sext, cur_bb);
|
||||
|
||||
|
||||
if (i < constLen - 1) {
|
||||
next_bb = BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb);
|
||||
|
||||
next_bb =
|
||||
BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb);
|
||||
BranchInst::Create(end_bb, next_bb);
|
||||
|
||||
Value *icmp = IRB.CreateICmpEQ(isub, ConstantInt::get(Int8Ty, 0));
|
||||
IRB.CreateCondBr(icmp, next_bb, end_bb);
|
||||
cur_bb->getTerminator()->eraseFromParent();
|
||||
|
||||
} else {
|
||||
//IRB.CreateBr(end_bb);
|
||||
|
||||
// IRB.CreateBr(end_bb);
|
||||
|
||||
}
|
||||
|
||||
//add offset to varstr
|
||||
//create load
|
||||
//create signed isub
|
||||
//create icmp
|
||||
//create jcc
|
||||
//create next_bb
|
||||
// add offset to varstr
|
||||
// create load
|
||||
// create signed isub
|
||||
// create icmp
|
||||
// create jcc
|
||||
// create next_bb
|
||||
|
||||
}
|
||||
|
||||
/* since the call is the first instruction of the bb it is safe to
|
||||
* replace it with a phi instruction */
|
||||
BasicBlock::iterator ii(callInst);
|
||||
ReplaceInstWithInst(callInst->getParent()->getInstList(), ii, PN);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool CompareTransform::runOnModule(Module &M) {
|
||||
|
||||
if (getenv("AFL_QUIET") == NULL)
|
||||
llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, extended by heiko@hexco.de\n";
|
||||
llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, "
|
||||
"extended by heiko@hexco.de\n";
|
||||
transformCmps(M, true, true, true, true, true);
|
||||
verifyModule(M);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
static void registerCompTransPass(const PassManagerBuilder &,
|
||||
legacy::PassManagerBase &PM) {
|
||||
legacy::PassManagerBase &PM) {
|
||||
|
||||
auto p = new CompareTransform();
|
||||
PM.add(p);
|
||||
|
@ -27,117 +27,126 @@
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
class SplitComparesTransform : public ModulePass {
|
||||
public:
|
||||
static char ID;
|
||||
SplitComparesTransform() : ModulePass(ID) {}
|
||||
|
||||
bool runOnModule(Module &M) override;
|
||||
class SplitComparesTransform : public ModulePass {
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
SplitComparesTransform() : ModulePass(ID) {
|
||||
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override;
|
||||
#if LLVM_VERSION_MAJOR >= 4
|
||||
StringRef getPassName() const override {
|
||||
#else
|
||||
const char * getPassName() const override {
|
||||
#endif
|
||||
return "simplifies and splits ICMP instructions";
|
||||
}
|
||||
private:
|
||||
bool splitCompares(Module &M, unsigned bitw);
|
||||
bool simplifyCompares(Module &M);
|
||||
bool simplifySignedness(Module &M);
|
||||
StringRef getPassName() const override {
|
||||
|
||||
};
|
||||
}
|
||||
#else
|
||||
const char *getPassName() const override {
|
||||
|
||||
#endif
|
||||
return "simplifies and splits ICMP instructions";
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
bool splitCompares(Module &M, unsigned bitw);
|
||||
bool simplifyCompares(Module &M);
|
||||
bool simplifySignedness(Module &M);
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
char SplitComparesTransform::ID = 0;
|
||||
|
||||
/* This function splits ICMP instructions with xGE or xLE predicates into two
|
||||
/* This function splits ICMP instructions with xGE or xLE predicates into two
|
||||
* ICMP instructions with predicate xGT or xLT and EQ */
|
||||
bool SplitComparesTransform::simplifyCompares(Module &M) {
|
||||
LLVMContext &C = M.getContext();
|
||||
std::vector<Instruction*> icomps;
|
||||
IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
|
||||
|
||||
LLVMContext & C = M.getContext();
|
||||
std::vector<Instruction *> icomps;
|
||||
IntegerType * Int1Ty = IntegerType::getInt1Ty(C);
|
||||
|
||||
/* iterate over all functions, bbs and instruction and add
|
||||
* all integer comparisons with >= and <= predicates to the icomps vector */
|
||||
for (auto &F : M) {
|
||||
|
||||
for (auto &BB : F) {
|
||||
for (auto &IN: BB) {
|
||||
CmpInst* selectcmpInst = nullptr;
|
||||
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CmpInst *selectcmpInst = nullptr;
|
||||
|
||||
if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) {
|
||||
|
||||
if (selectcmpInst->getPredicate() != CmpInst::ICMP_UGE &&
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_SGE &&
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_ULE &&
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_SLE ) {
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_SLE) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
auto op0 = selectcmpInst->getOperand(0);
|
||||
auto op1 = selectcmpInst->getOperand(1);
|
||||
|
||||
IntegerType* intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||
IntegerType* intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
||||
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
||||
|
||||
/* this is probably not needed but we do it anyway */
|
||||
if (!intTyOp0 || !intTyOp1) {
|
||||
continue;
|
||||
}
|
||||
if (!intTyOp0 || !intTyOp1) { continue; }
|
||||
|
||||
icomps.push_back(selectcmpInst);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!icomps.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!icomps.size()) { return false; }
|
||||
|
||||
for (auto &IcmpInst : icomps) {
|
||||
|
||||
for (auto &IcmpInst: icomps) {
|
||||
BasicBlock* bb = IcmpInst->getParent();
|
||||
BasicBlock *bb = IcmpInst->getParent();
|
||||
|
||||
auto op0 = IcmpInst->getOperand(0);
|
||||
auto op1 = IcmpInst->getOperand(1);
|
||||
|
||||
/* find out what the new predicate is going to be */
|
||||
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
||||
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
||||
CmpInst::Predicate new_pred;
|
||||
switch(pred) {
|
||||
case CmpInst::ICMP_UGE:
|
||||
new_pred = CmpInst::ICMP_UGT;
|
||||
break;
|
||||
case CmpInst::ICMP_SGE:
|
||||
new_pred = CmpInst::ICMP_SGT;
|
||||
break;
|
||||
case CmpInst::ICMP_ULE:
|
||||
new_pred = CmpInst::ICMP_ULT;
|
||||
break;
|
||||
case CmpInst::ICMP_SLE:
|
||||
new_pred = CmpInst::ICMP_SLT;
|
||||
break;
|
||||
default: // keep the compiler happy
|
||||
switch (pred) {
|
||||
|
||||
case CmpInst::ICMP_UGE: new_pred = CmpInst::ICMP_UGT; break;
|
||||
case CmpInst::ICMP_SGE: new_pred = CmpInst::ICMP_SGT; break;
|
||||
case CmpInst::ICMP_ULE: new_pred = CmpInst::ICMP_ULT; break;
|
||||
case CmpInst::ICMP_SLE: new_pred = CmpInst::ICMP_SLT; break;
|
||||
default: // keep the compiler happy
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* split before the icmp instruction */
|
||||
BasicBlock* end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst));
|
||||
BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst));
|
||||
|
||||
/* the old bb now contains a unconditional jump to the new one (end_bb)
|
||||
* we need to delete it later */
|
||||
|
||||
/* create the ICMP instruction with new_pred and add it to the old basic
|
||||
* block bb it is now at the position where the old IcmpInst was */
|
||||
Instruction* icmp_np;
|
||||
Instruction *icmp_np;
|
||||
icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1);
|
||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_np);
|
||||
|
||||
/* create a new basic block which holds the new EQ icmp */
|
||||
Instruction *icmp_eq;
|
||||
/* insert middle_bb before end_bb */
|
||||
BasicBlock* middle_bb = BasicBlock::Create(C, "injected",
|
||||
end_bb->getParent(), end_bb);
|
||||
BasicBlock *middle_bb =
|
||||
BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
|
||||
icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1);
|
||||
middle_bb->getInstList().push_back(icmp_eq);
|
||||
/* add an unconditional branch to the end of middle_bb with destination
|
||||
@ -150,7 +159,6 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
|
||||
BranchInst::Create(end_bb, middle_bb, icmp_np, bb);
|
||||
term->eraseFromParent();
|
||||
|
||||
|
||||
/* replace the old IcmpInst (which is the first inst in end_bb) with a PHI
|
||||
* inst to wire up the loose ends */
|
||||
PHINode *PN = PHINode::Create(Int1Ty, 2, "");
|
||||
@ -162,118 +170,139 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
|
||||
/* replace the old IcmpInst with our new and shiny PHI inst */
|
||||
BasicBlock::iterator ii(IcmpInst);
|
||||
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/* this function transforms signed compares to equivalent unsigned compares */
|
||||
bool SplitComparesTransform::simplifySignedness(Module &M) {
|
||||
LLVMContext &C = M.getContext();
|
||||
std::vector<Instruction*> icomps;
|
||||
IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
|
||||
|
||||
LLVMContext & C = M.getContext();
|
||||
std::vector<Instruction *> icomps;
|
||||
IntegerType * Int1Ty = IntegerType::getInt1Ty(C);
|
||||
|
||||
/* iterate over all functions, bbs and instruction and add
|
||||
* all signed compares to icomps vector */
|
||||
for (auto &F : M) {
|
||||
|
||||
for (auto &BB : F) {
|
||||
for(auto &IN: BB) {
|
||||
CmpInst* selectcmpInst = nullptr;
|
||||
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CmpInst *selectcmpInst = nullptr;
|
||||
|
||||
if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) {
|
||||
|
||||
if (selectcmpInst->getPredicate() != CmpInst::ICMP_SGT &&
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_SLT
|
||||
) {
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_SLT) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
auto op0 = selectcmpInst->getOperand(0);
|
||||
auto op1 = selectcmpInst->getOperand(1);
|
||||
|
||||
IntegerType* intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||
IntegerType* intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
||||
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
||||
|
||||
/* see above */
|
||||
if (!intTyOp0 || !intTyOp1) {
|
||||
continue;
|
||||
}
|
||||
if (!intTyOp0 || !intTyOp1) { continue; }
|
||||
|
||||
/* i think this is not possible but to lazy to look it up */
|
||||
if (intTyOp0->getBitWidth() != intTyOp1->getBitWidth()) {
|
||||
continue;
|
||||
}
|
||||
if (intTyOp0->getBitWidth() != intTyOp1->getBitWidth()) { continue; }
|
||||
|
||||
icomps.push_back(selectcmpInst);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!icomps.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!icomps.size()) { return false; }
|
||||
|
||||
for (auto &IcmpInst: icomps) {
|
||||
BasicBlock* bb = IcmpInst->getParent();
|
||||
for (auto &IcmpInst : icomps) {
|
||||
|
||||
BasicBlock *bb = IcmpInst->getParent();
|
||||
|
||||
auto op0 = IcmpInst->getOperand(0);
|
||||
auto op1 = IcmpInst->getOperand(1);
|
||||
|
||||
IntegerType* intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||
unsigned bitw = intTyOp0->getBitWidth();
|
||||
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||
unsigned bitw = intTyOp0->getBitWidth();
|
||||
IntegerType *IntType = IntegerType::get(C, bitw);
|
||||
|
||||
|
||||
/* get the new predicate */
|
||||
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
||||
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
||||
CmpInst::Predicate new_pred;
|
||||
if (pred == CmpInst::ICMP_SGT) {
|
||||
|
||||
new_pred = CmpInst::ICMP_UGT;
|
||||
|
||||
} else {
|
||||
|
||||
new_pred = CmpInst::ICMP_ULT;
|
||||
|
||||
}
|
||||
|
||||
BasicBlock* end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst));
|
||||
BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst));
|
||||
|
||||
/* create a 1 bit compare for the sign bit. to do this shift and trunc
|
||||
* the original operands so only the first bit remains.*/
|
||||
Instruction *s_op0, *t_op0, *s_op1, *t_op1, *icmp_sign_bit;
|
||||
|
||||
s_op0 = BinaryOperator::Create(Instruction::LShr, op0, ConstantInt::get(IntType, bitw - 1));
|
||||
s_op0 = BinaryOperator::Create(Instruction::LShr, op0,
|
||||
ConstantInt::get(IntType, bitw - 1));
|
||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op0);
|
||||
t_op0 = new TruncInst(s_op0, Int1Ty);
|
||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), t_op0);
|
||||
|
||||
s_op1 = BinaryOperator::Create(Instruction::LShr, op1, ConstantInt::get(IntType, bitw - 1));
|
||||
s_op1 = BinaryOperator::Create(Instruction::LShr, op1,
|
||||
ConstantInt::get(IntType, bitw - 1));
|
||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op1);
|
||||
t_op1 = new TruncInst(s_op1, Int1Ty);
|
||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), t_op1);
|
||||
|
||||
/* compare of the sign bits */
|
||||
icmp_sign_bit = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_op0, t_op1);
|
||||
icmp_sign_bit =
|
||||
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_op0, t_op1);
|
||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_sign_bit);
|
||||
|
||||
/* create a new basic block which is executed if the signedness bit is
|
||||
* different */
|
||||
* different */
|
||||
Instruction *icmp_inv_sig_cmp;
|
||||
BasicBlock* sign_bb = BasicBlock::Create(C, "sign", end_bb->getParent(), end_bb);
|
||||
BasicBlock * sign_bb =
|
||||
BasicBlock::Create(C, "sign", end_bb->getParent(), end_bb);
|
||||
if (pred == CmpInst::ICMP_SGT) {
|
||||
|
||||
/* if we check for > and the op0 positive and op1 negative then the final
|
||||
* result is true. if op0 negative and op1 pos, the cmp must result
|
||||
* in false
|
||||
*/
|
||||
icmp_inv_sig_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_op0, t_op1);
|
||||
icmp_inv_sig_cmp =
|
||||
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_op0, t_op1);
|
||||
|
||||
} else {
|
||||
|
||||
/* just the inverse of the above statement */
|
||||
icmp_inv_sig_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_op0, t_op1);
|
||||
icmp_inv_sig_cmp =
|
||||
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_op0, t_op1);
|
||||
|
||||
}
|
||||
|
||||
sign_bb->getInstList().push_back(icmp_inv_sig_cmp);
|
||||
BranchInst::Create(end_bb, sign_bb);
|
||||
|
||||
/* create a new bb which is executed if signedness is equal */
|
||||
Instruction *icmp_usign_cmp;
|
||||
BasicBlock* middle_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
|
||||
BasicBlock * middle_bb =
|
||||
BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
|
||||
/* we can do a normal unsigned compare now */
|
||||
icmp_usign_cmp = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1);
|
||||
middle_bb->getInstList().push_back(icmp_usign_cmp);
|
||||
@ -285,7 +314,6 @@ bool SplitComparesTransform::simplifySignedness(Module &M) {
|
||||
BranchInst::Create(middle_bb, sign_bb, icmp_sign_bit, bb);
|
||||
term->eraseFromParent();
|
||||
|
||||
|
||||
PHINode *PN = PHINode::Create(Int1Ty, 2, "");
|
||||
|
||||
PN->addIncoming(icmp_usign_cmp, middle_bb);
|
||||
@ -293,91 +321,100 @@ bool SplitComparesTransform::simplifySignedness(Module &M) {
|
||||
|
||||
BasicBlock::iterator ii(IcmpInst);
|
||||
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/* splits icmps of size bitw into two nested icmps with bitw/2 size each */
|
||||
bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
||||
|
||||
LLVMContext &C = M.getContext();
|
||||
|
||||
IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
|
||||
IntegerType *OldIntType = IntegerType::get(C, bitw);
|
||||
IntegerType *NewIntType = IntegerType::get(C, bitw / 2);
|
||||
|
||||
std::vector<Instruction*> icomps;
|
||||
std::vector<Instruction *> icomps;
|
||||
|
||||
if (bitw % 2) {
|
||||
return false;
|
||||
}
|
||||
if (bitw % 2) { return false; }
|
||||
|
||||
/* not supported yet */
|
||||
if (bitw > 64) {
|
||||
return false;
|
||||
}
|
||||
if (bitw > 64) { return false; }
|
||||
|
||||
/* get all EQ, NE, UGT, and ULT icmps of width bitw. if the other two
|
||||
/* get all EQ, NE, UGT, and ULT icmps of width bitw. if the other two
|
||||
* unctions were executed only these four predicates should exist */
|
||||
for (auto &F : M) {
|
||||
|
||||
for (auto &BB : F) {
|
||||
for(auto &IN: BB) {
|
||||
CmpInst* selectcmpInst = nullptr;
|
||||
|
||||
for (auto &IN : BB) {
|
||||
|
||||
CmpInst *selectcmpInst = nullptr;
|
||||
|
||||
if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) {
|
||||
|
||||
if(selectcmpInst->getPredicate() != CmpInst::ICMP_EQ &&
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_NE &&
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_UGT &&
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_ULT
|
||||
) {
|
||||
if (selectcmpInst->getPredicate() != CmpInst::ICMP_EQ &&
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_NE &&
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_UGT &&
|
||||
selectcmpInst->getPredicate() != CmpInst::ICMP_ULT) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
auto op0 = selectcmpInst->getOperand(0);
|
||||
auto op1 = selectcmpInst->getOperand(1);
|
||||
|
||||
IntegerType* intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||
IntegerType* intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
||||
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
||||
|
||||
if (!intTyOp0 || !intTyOp1) {
|
||||
continue;
|
||||
}
|
||||
if (!intTyOp0 || !intTyOp1) { continue; }
|
||||
|
||||
/* check if the bitwidths are the one we are looking for */
|
||||
if (intTyOp0->getBitWidth() != bitw || intTyOp1->getBitWidth() != bitw) {
|
||||
if (intTyOp0->getBitWidth() != bitw ||
|
||||
intTyOp1->getBitWidth() != bitw) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
icomps.push_back(selectcmpInst);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!icomps.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!icomps.size()) { return false; }
|
||||
|
||||
for (auto &IcmpInst: icomps) {
|
||||
BasicBlock* bb = IcmpInst->getParent();
|
||||
for (auto &IcmpInst : icomps) {
|
||||
|
||||
BasicBlock *bb = IcmpInst->getParent();
|
||||
|
||||
auto op0 = IcmpInst->getOperand(0);
|
||||
auto op1 = IcmpInst->getOperand(1);
|
||||
|
||||
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
||||
|
||||
BasicBlock* end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst));
|
||||
BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst));
|
||||
|
||||
/* create the comparison of the top halves of the original operands */
|
||||
Instruction *s_op0, *op0_high, *s_op1, *op1_high, *icmp_high;
|
||||
|
||||
s_op0 = BinaryOperator::Create(Instruction::LShr, op0, ConstantInt::get(OldIntType, bitw / 2));
|
||||
s_op0 = BinaryOperator::Create(Instruction::LShr, op0,
|
||||
ConstantInt::get(OldIntType, bitw / 2));
|
||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op0);
|
||||
op0_high = new TruncInst(s_op0, NewIntType);
|
||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), op0_high);
|
||||
|
||||
s_op1 = BinaryOperator::Create(Instruction::LShr, op1, ConstantInt::get(OldIntType, bitw / 2));
|
||||
s_op1 = BinaryOperator::Create(Instruction::LShr, op1,
|
||||
ConstantInt::get(OldIntType, bitw / 2));
|
||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op1);
|
||||
op1_high = new TruncInst(s_op1, NewIntType);
|
||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), op1_high);
|
||||
@ -387,11 +424,13 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
||||
|
||||
/* now we have to destinguish between == != and > < */
|
||||
if (pred == CmpInst::ICMP_EQ || pred == CmpInst::ICMP_NE) {
|
||||
|
||||
/* transformation for == and != icmps */
|
||||
|
||||
/* create a compare for the lower half of the original operands */
|
||||
Instruction *op0_low, *op1_low, *icmp_low;
|
||||
BasicBlock* cmp_low_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
|
||||
BasicBlock * cmp_low_bb =
|
||||
BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
|
||||
|
||||
op0_low = new TruncInst(op0, NewIntType);
|
||||
cmp_low_bb->getInstList().push_back(op0_low);
|
||||
@ -407,21 +446,30 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
||||
* the comparison */
|
||||
auto term = bb->getTerminator();
|
||||
if (pred == CmpInst::ICMP_EQ) {
|
||||
|
||||
BranchInst::Create(cmp_low_bb, end_bb, icmp_high, bb);
|
||||
|
||||
} else {
|
||||
|
||||
/* CmpInst::ICMP_NE */
|
||||
BranchInst::Create(end_bb, cmp_low_bb, icmp_high, bb);
|
||||
|
||||
}
|
||||
|
||||
term->eraseFromParent();
|
||||
|
||||
/* create the PHI and connect the edges accordingly */
|
||||
PHINode *PN = PHINode::Create(Int1Ty, 2, "");
|
||||
PN->addIncoming(icmp_low, cmp_low_bb);
|
||||
if (pred == CmpInst::ICMP_EQ) {
|
||||
|
||||
PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb);
|
||||
|
||||
} else {
|
||||
|
||||
/* CmpInst::ICMP_NE */
|
||||
PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb);
|
||||
|
||||
}
|
||||
|
||||
/* replace the old icmp with the new PHI */
|
||||
@ -429,19 +477,28 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
||||
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
||||
|
||||
} else {
|
||||
|
||||
/* CmpInst::ICMP_UGT and CmpInst::ICMP_ULT */
|
||||
/* transformations for < and > */
|
||||
|
||||
/* create a basic block which checks for the inverse predicate.
|
||||
/* create a basic block which checks for the inverse predicate.
|
||||
* if this is true we can go to the end if not we have to got to the
|
||||
* bb which checks the lower half of the operands */
|
||||
Instruction *icmp_inv_cmp, *op0_low, *op1_low, *icmp_low;
|
||||
BasicBlock* inv_cmp_bb = BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb);
|
||||
BasicBlock * inv_cmp_bb =
|
||||
BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb);
|
||||
if (pred == CmpInst::ICMP_UGT) {
|
||||
icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, op0_high, op1_high);
|
||||
|
||||
icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT,
|
||||
op0_high, op1_high);
|
||||
|
||||
} else {
|
||||
icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, op0_high, op1_high);
|
||||
|
||||
icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT,
|
||||
op0_high, op1_high);
|
||||
|
||||
}
|
||||
|
||||
inv_cmp_bb->getInstList().push_back(icmp_inv_cmp);
|
||||
|
||||
auto term = bb->getTerminator();
|
||||
@ -449,7 +506,8 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
||||
BranchInst::Create(end_bb, inv_cmp_bb, icmp_high, bb);
|
||||
|
||||
/* create a bb which handles the cmp of the lower halves */
|
||||
BasicBlock* cmp_low_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
|
||||
BasicBlock *cmp_low_bb =
|
||||
BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
|
||||
op0_low = new TruncInst(op0, NewIntType);
|
||||
cmp_low_bb->getInstList().push_back(op0_low);
|
||||
op1_low = new TruncInst(op1, NewIntType);
|
||||
@ -468,59 +526,64 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
||||
|
||||
BasicBlock::iterator ii(IcmpInst);
|
||||
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool SplitComparesTransform::runOnModule(Module &M) {
|
||||
|
||||
int bitw = 64;
|
||||
|
||||
char* bitw_env = getenv("LAF_SPLIT_COMPARES_BITW");
|
||||
if (!bitw_env)
|
||||
bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW");
|
||||
if (bitw_env) {
|
||||
bitw = atoi(bitw_env);
|
||||
}
|
||||
char *bitw_env = getenv("LAF_SPLIT_COMPARES_BITW");
|
||||
if (!bitw_env) bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW");
|
||||
if (bitw_env) { bitw = atoi(bitw_env); }
|
||||
|
||||
simplifyCompares(M);
|
||||
|
||||
simplifySignedness(M);
|
||||
|
||||
if (getenv("AFL_QUIET") == NULL)
|
||||
errs() << "Split-compare-pass by laf.intel@gmail.com\n";
|
||||
errs() << "Split-compare-pass by laf.intel@gmail.com\n";
|
||||
|
||||
switch (bitw) {
|
||||
|
||||
case 64:
|
||||
errs() << "Running split-compare-pass " << 64 << "\n";
|
||||
errs() << "Running split-compare-pass " << 64 << "\n";
|
||||
splitCompares(M, 64);
|
||||
|
||||
[[clang::fallthrough]];
|
||||
/* fallthrough */
|
||||
[[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */
|
||||
case 32:
|
||||
errs() << "Running split-compare-pass " << 32 << "\n";
|
||||
errs() << "Running split-compare-pass " << 32 << "\n";
|
||||
splitCompares(M, 32);
|
||||
|
||||
[[clang::fallthrough]];
|
||||
/* fallthrough */
|
||||
[[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */
|
||||
case 16:
|
||||
errs() << "Running split-compare-pass " << 16 << "\n";
|
||||
errs() << "Running split-compare-pass " << 16 << "\n";
|
||||
splitCompares(M, 16);
|
||||
break;
|
||||
|
||||
default:
|
||||
errs() << "NOT Running split-compare-pass \n";
|
||||
errs() << "NOT Running split-compare-pass \n";
|
||||
return false;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
verifyModule(M);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
static void registerSplitComparesPass(const PassManagerBuilder &,
|
||||
legacy::PassManagerBase &PM) {
|
||||
legacy::PassManagerBase &PM) {
|
||||
|
||||
PM.add(new SplitComparesTransform());
|
||||
|
||||
}
|
||||
|
||||
static RegisterStandardPasses RegisterSplitComparesPass(
|
||||
@ -528,3 +591,4 @@ static RegisterStandardPasses RegisterSplitComparesPass(
|
||||
|
||||
static RegisterStandardPasses RegisterSplitComparesTransPass0(
|
||||
PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass);
|
||||
|
||||
|
@ -36,54 +36,65 @@ using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
class SplitSwitchesTransform : public ModulePass {
|
||||
class SplitSwitchesTransform : public ModulePass {
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
SplitSwitchesTransform() : ModulePass(ID) {
|
||||
}
|
||||
public:
|
||||
static char ID;
|
||||
SplitSwitchesTransform() : ModulePass(ID) {
|
||||
|
||||
bool runOnModule(Module &M) override;
|
||||
}
|
||||
|
||||
bool runOnModule(Module &M) override;
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 4
|
||||
StringRef getPassName() const override {
|
||||
StringRef getPassName() const override {
|
||||
|
||||
#else
|
||||
const char * getPassName() const override {
|
||||
const char *getPassName() const override {
|
||||
|
||||
#endif
|
||||
return "splits switch constructs";
|
||||
}
|
||||
struct CaseExpr {
|
||||
ConstantInt* Val;
|
||||
BasicBlock* BB;
|
||||
return "splits switch constructs";
|
||||
|
||||
CaseExpr(ConstantInt *val = nullptr, BasicBlock *bb = nullptr) :
|
||||
Val(val), BB(bb) { }
|
||||
};
|
||||
}
|
||||
|
||||
typedef std::vector<CaseExpr> CaseVector;
|
||||
struct CaseExpr {
|
||||
|
||||
ConstantInt *Val;
|
||||
BasicBlock * BB;
|
||||
|
||||
CaseExpr(ConstantInt *val = nullptr, BasicBlock *bb = nullptr)
|
||||
: Val(val), BB(bb) {
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
bool splitSwitches(Module &M);
|
||||
bool transformCmps(Module &M, const bool processStrcmp, const bool processMemcmp);
|
||||
BasicBlock* switchConvert(CaseVector Cases, std::vector<bool> bytesChecked,
|
||||
BasicBlock* OrigBlock, BasicBlock* NewDefault,
|
||||
Value* Val, unsigned level);
|
||||
};
|
||||
|
||||
}
|
||||
typedef std::vector<CaseExpr> CaseVector;
|
||||
|
||||
private:
|
||||
bool splitSwitches(Module &M);
|
||||
bool transformCmps(Module &M, const bool processStrcmp,
|
||||
const bool processMemcmp);
|
||||
BasicBlock *switchConvert(CaseVector Cases, std::vector<bool> bytesChecked,
|
||||
BasicBlock *OrigBlock, BasicBlock *NewDefault,
|
||||
Value *Val, unsigned level);
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
char SplitSwitchesTransform::ID = 0;
|
||||
|
||||
|
||||
/* switchConvert - Transform simple list of Cases into list of CaseRange's */
|
||||
BasicBlock* SplitSwitchesTransform::switchConvert(CaseVector Cases, std::vector<bool> bytesChecked,
|
||||
BasicBlock* OrigBlock, BasicBlock* NewDefault,
|
||||
Value* Val, unsigned level) {
|
||||
BasicBlock *SplitSwitchesTransform::switchConvert(
|
||||
CaseVector Cases, std::vector<bool> bytesChecked, BasicBlock *OrigBlock,
|
||||
BasicBlock *NewDefault, Value *Val, unsigned level) {
|
||||
|
||||
unsigned ValTypeBitWidth = Cases[0].Val->getBitWidth();
|
||||
IntegerType *ValType = IntegerType::get(OrigBlock->getContext(), ValTypeBitWidth);
|
||||
IntegerType *ByteType = IntegerType::get(OrigBlock->getContext(), 8);
|
||||
unsigned BytesInValue = bytesChecked.size();
|
||||
unsigned ValTypeBitWidth = Cases[0].Val->getBitWidth();
|
||||
IntegerType *ValType =
|
||||
IntegerType::get(OrigBlock->getContext(), ValTypeBitWidth);
|
||||
IntegerType * ByteType = IntegerType::get(OrigBlock->getContext(), 8);
|
||||
unsigned BytesInValue = bytesChecked.size();
|
||||
std::vector<uint8_t> setSizes;
|
||||
std::vector<std::set<uint8_t>> byteSets(BytesInValue, std::set<uint8_t>());
|
||||
|
||||
@ -91,43 +102,54 @@ BasicBlock* SplitSwitchesTransform::switchConvert(CaseVector Cases, std::vector<
|
||||
|
||||
/* for each of the possible cases we iterate over all bytes of the values
|
||||
* build a set of possible values at each byte position in byteSets */
|
||||
for (CaseExpr& Case: Cases) {
|
||||
for (CaseExpr &Case : Cases) {
|
||||
|
||||
for (unsigned i = 0; i < BytesInValue; i++) {
|
||||
|
||||
uint8_t byte = (Case.Val->getZExtValue() >> (i*8)) & 0xFF;
|
||||
uint8_t byte = (Case.Val->getZExtValue() >> (i * 8)) & 0xFF;
|
||||
byteSets[i].insert(byte);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* find the index of the first byte position that was not yet checked. then
|
||||
* save the number of possible values at that byte position */
|
||||
unsigned smallestIndex = 0;
|
||||
unsigned smallestSize = 257;
|
||||
for(unsigned i = 0; i < byteSets.size(); i++) {
|
||||
if (bytesChecked[i])
|
||||
continue;
|
||||
for (unsigned i = 0; i < byteSets.size(); i++) {
|
||||
|
||||
if (bytesChecked[i]) continue;
|
||||
if (byteSets[i].size() < smallestSize) {
|
||||
|
||||
smallestIndex = i;
|
||||
smallestSize = byteSets[i].size();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
assert(bytesChecked[smallestIndex] == false);
|
||||
|
||||
/* there are only smallestSize different bytes at index smallestIndex */
|
||||
|
||||
|
||||
Instruction *Shift, *Trunc;
|
||||
Function* F = OrigBlock->getParent();
|
||||
BasicBlock* NewNode = BasicBlock::Create(Val->getContext(), "NodeBlock", F);
|
||||
Shift = BinaryOperator::Create(Instruction::LShr, Val, ConstantInt::get(ValType, smallestIndex * 8));
|
||||
Function * F = OrigBlock->getParent();
|
||||
BasicBlock * NewNode = BasicBlock::Create(Val->getContext(), "NodeBlock", F);
|
||||
Shift = BinaryOperator::Create(Instruction::LShr, Val,
|
||||
ConstantInt::get(ValType, smallestIndex * 8));
|
||||
NewNode->getInstList().push_back(Shift);
|
||||
|
||||
if (ValTypeBitWidth > 8) {
|
||||
|
||||
Trunc = new TruncInst(Shift, ByteType);
|
||||
NewNode->getInstList().push_back(Trunc);
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
|
||||
/* not necessary to trunc */
|
||||
Trunc = Shift;
|
||||
|
||||
}
|
||||
|
||||
/* this is a trivial case, we can directly check for the byte,
|
||||
@ -135,118 +157,155 @@ BasicBlock* SplitSwitchesTransform::switchConvert(CaseVector Cases, std::vector<
|
||||
* mark the byte as checked. if this was the last byte to check
|
||||
* we can finally execute the block belonging to this case */
|
||||
|
||||
|
||||
if (smallestSize == 1) {
|
||||
|
||||
uint8_t byte = *(byteSets[smallestIndex].begin());
|
||||
|
||||
/* insert instructions to check whether the value we are switching on is equal to byte */
|
||||
ICmpInst* Comp = new ICmpInst(ICmpInst::ICMP_EQ, Trunc, ConstantInt::get(ByteType, byte), "byteMatch");
|
||||
/* insert instructions to check whether the value we are switching on is
|
||||
* equal to byte */
|
||||
ICmpInst *Comp =
|
||||
new ICmpInst(ICmpInst::ICMP_EQ, Trunc, ConstantInt::get(ByteType, byte),
|
||||
"byteMatch");
|
||||
NewNode->getInstList().push_back(Comp);
|
||||
|
||||
bytesChecked[smallestIndex] = true;
|
||||
if (std::all_of(bytesChecked.begin(), bytesChecked.end(), [](bool b){return b;} )) {
|
||||
if (std::all_of(bytesChecked.begin(), bytesChecked.end(),
|
||||
[](bool b) { return b; })) {
|
||||
|
||||
assert(Cases.size() == 1);
|
||||
BranchInst::Create(Cases[0].BB, NewDefault, Comp, NewNode);
|
||||
|
||||
/* we have to update the phi nodes! */
|
||||
for (BasicBlock::iterator I = Cases[0].BB->begin(); I != Cases[0].BB->end(); ++I) {
|
||||
if (!isa<PHINode>(&*I)) {
|
||||
continue;
|
||||
}
|
||||
for (BasicBlock::iterator I = Cases[0].BB->begin();
|
||||
I != Cases[0].BB->end(); ++I) {
|
||||
|
||||
if (!isa<PHINode>(&*I)) { continue; }
|
||||
PHINode *PN = cast<PHINode>(I);
|
||||
|
||||
/* Only update the first occurrence. */
|
||||
unsigned Idx = 0, E = PN->getNumIncomingValues();
|
||||
for (; Idx != E; ++Idx) {
|
||||
|
||||
if (PN->getIncomingBlock(Idx) == OrigBlock) {
|
||||
|
||||
PN->setIncomingBlock(Idx, NewNode);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
BasicBlock* BB = switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, level + 1);
|
||||
|
||||
} else {
|
||||
|
||||
BasicBlock *BB = switchConvert(Cases, bytesChecked, OrigBlock, NewDefault,
|
||||
Val, level + 1);
|
||||
BranchInst::Create(BB, NewDefault, Comp, NewNode);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* there is no byte which we can directly check on, split the tree */
|
||||
else {
|
||||
|
||||
std::vector<uint8_t> byteVector;
|
||||
std::copy(byteSets[smallestIndex].begin(), byteSets[smallestIndex].end(), std::back_inserter(byteVector));
|
||||
std::copy(byteSets[smallestIndex].begin(), byteSets[smallestIndex].end(),
|
||||
std::back_inserter(byteVector));
|
||||
std::sort(byteVector.begin(), byteVector.end());
|
||||
uint8_t pivot = byteVector[byteVector.size() / 2];
|
||||
|
||||
/* we already chose to divide the cases based on the value of byte at index smallestIndex
|
||||
* the pivot value determines the threshold for the decicion; if a case value
|
||||
* is smaller at this byte index move it to the LHS vector, otherwise to the RHS vector */
|
||||
/* we already chose to divide the cases based on the value of byte at index
|
||||
* smallestIndex the pivot value determines the threshold for the decicion;
|
||||
* if a case value
|
||||
* is smaller at this byte index move it to the LHS vector, otherwise to the
|
||||
* RHS vector */
|
||||
|
||||
CaseVector LHSCases, RHSCases;
|
||||
|
||||
for (CaseExpr& Case: Cases) {
|
||||
uint8_t byte = (Case.Val->getZExtValue() >> (smallestIndex*8)) & 0xFF;
|
||||
for (CaseExpr &Case : Cases) {
|
||||
|
||||
uint8_t byte = (Case.Val->getZExtValue() >> (smallestIndex * 8)) & 0xFF;
|
||||
|
||||
if (byte < pivot) {
|
||||
LHSCases.push_back(Case);
|
||||
}
|
||||
else {
|
||||
RHSCases.push_back(Case);
|
||||
}
|
||||
}
|
||||
BasicBlock *LBB, *RBB;
|
||||
LBB = switchConvert(LHSCases, bytesChecked, OrigBlock, NewDefault, Val, level + 1);
|
||||
RBB = switchConvert(RHSCases, bytesChecked, OrigBlock, NewDefault, Val, level + 1);
|
||||
|
||||
/* insert instructions to check whether the value we are switching on is equal to byte */
|
||||
ICmpInst* Comp = new ICmpInst(ICmpInst::ICMP_ULT, Trunc, ConstantInt::get(ByteType, pivot), "byteMatch");
|
||||
LHSCases.push_back(Case);
|
||||
|
||||
} else {
|
||||
|
||||
RHSCases.push_back(Case);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BasicBlock *LBB, *RBB;
|
||||
LBB = switchConvert(LHSCases, bytesChecked, OrigBlock, NewDefault, Val,
|
||||
level + 1);
|
||||
RBB = switchConvert(RHSCases, bytesChecked, OrigBlock, NewDefault, Val,
|
||||
level + 1);
|
||||
|
||||
/* insert instructions to check whether the value we are switching on is
|
||||
* equal to byte */
|
||||
ICmpInst *Comp =
|
||||
new ICmpInst(ICmpInst::ICMP_ULT, Trunc,
|
||||
ConstantInt::get(ByteType, pivot), "byteMatch");
|
||||
NewNode->getInstList().push_back(Comp);
|
||||
BranchInst::Create(LBB, RBB, Comp, NewNode);
|
||||
|
||||
}
|
||||
|
||||
return NewNode;
|
||||
|
||||
}
|
||||
|
||||
bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
||||
|
||||
std::vector<SwitchInst*> switches;
|
||||
std::vector<SwitchInst *> switches;
|
||||
|
||||
/* iterate over all functions, bbs and instruction and add
|
||||
* all switches to switches vector for later processing */
|
||||
for (auto &F : M) {
|
||||
|
||||
for (auto &BB : F) {
|
||||
SwitchInst* switchInst = nullptr;
|
||||
|
||||
SwitchInst *switchInst = nullptr;
|
||||
|
||||
if ((switchInst = dyn_cast<SwitchInst>(BB.getTerminator()))) {
|
||||
if (switchInst->getNumCases() < 1)
|
||||
continue;
|
||||
switches.push_back(switchInst);
|
||||
|
||||
if (switchInst->getNumCases() < 1) continue;
|
||||
switches.push_back(switchInst);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!switches.size())
|
||||
return false;
|
||||
errs() << "Rewriting " << switches.size() << " switch statements " << "\n";
|
||||
if (!switches.size()) return false;
|
||||
errs() << "Rewriting " << switches.size() << " switch statements "
|
||||
<< "\n";
|
||||
|
||||
for (auto &SI: switches) {
|
||||
for (auto &SI : switches) {
|
||||
|
||||
BasicBlock *CurBlock = SI->getParent();
|
||||
BasicBlock *OrigBlock = CurBlock;
|
||||
Function *F = CurBlock->getParent();
|
||||
Function * F = CurBlock->getParent();
|
||||
/* this is the value we are switching on */
|
||||
Value *Val = SI->getCondition();
|
||||
BasicBlock* Default = SI->getDefaultDest();
|
||||
unsigned bitw = Val->getType()->getIntegerBitWidth();
|
||||
Value * Val = SI->getCondition();
|
||||
BasicBlock *Default = SI->getDefaultDest();
|
||||
unsigned bitw = Val->getType()->getIntegerBitWidth();
|
||||
|
||||
errs() << "switch: " << SI->getNumCases() << " cases " << bitw << " bit\n";
|
||||
|
||||
/* If there is only the default destination or the condition checks 8 bit or less, don't bother with the code below. */
|
||||
/* If there is only the default destination or the condition checks 8 bit or
|
||||
* less, don't bother with the code below. */
|
||||
if (!SI->getNumCases() || bitw <= 8) {
|
||||
if (getenv("AFL_QUIET") == NULL)
|
||||
errs() << "skip trivial switch..\n";
|
||||
|
||||
if (getenv("AFL_QUIET") == NULL) errs() << "skip trivial switch..\n";
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* Create a new, empty default block so that the new hierarchy of
|
||||
@ -258,10 +317,10 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
||||
NewDefault->insertInto(F, Default);
|
||||
BranchInst::Create(Default, NewDefault);
|
||||
|
||||
|
||||
/* Prepare cases vector. */
|
||||
CaseVector Cases;
|
||||
for (SwitchInst::CaseIt i = SI->case_begin(), e = SI->case_end(); i != e; ++i)
|
||||
for (SwitchInst::CaseIt i = SI->case_begin(), e = SI->case_end(); i != e;
|
||||
++i)
|
||||
#if LLVM_VERSION_MAJOR < 5
|
||||
Cases.push_back(CaseExpr(i.getCaseValue(), i.getCaseSuccessor()));
|
||||
#else
|
||||
@ -269,8 +328,10 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
||||
#endif
|
||||
/* bugfix thanks to pbst
|
||||
* round up bytesChecked (in case getBitWidth() % 8 != 0) */
|
||||
std::vector<bool> bytesChecked((7 + Cases[0].Val->getBitWidth()) / 8, false);
|
||||
BasicBlock* SwitchBlock = switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, 0);
|
||||
std::vector<bool> bytesChecked((7 + Cases[0].Val->getBitWidth()) / 8,
|
||||
false);
|
||||
BasicBlock * SwitchBlock =
|
||||
switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, 0);
|
||||
|
||||
/* Branch to our shiny new if-then stuff... */
|
||||
BranchInst::Create(SwitchBlock, OrigBlock);
|
||||
@ -278,41 +339,47 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
||||
/* We are now done with the switch instruction, delete it. */
|
||||
CurBlock->getInstList().erase(SI);
|
||||
|
||||
/* we have to update the phi nodes! */
|
||||
for (BasicBlock::iterator I = Default->begin(); I != Default->end(); ++I) {
|
||||
|
||||
/* we have to update the phi nodes! */
|
||||
for (BasicBlock::iterator I = Default->begin(); I != Default->end(); ++I) {
|
||||
if (!isa<PHINode>(&*I)) {
|
||||
continue;
|
||||
}
|
||||
PHINode *PN = cast<PHINode>(I);
|
||||
if (!isa<PHINode>(&*I)) { continue; }
|
||||
PHINode *PN = cast<PHINode>(I);
|
||||
|
||||
/* Only update the first occurrence. */
|
||||
unsigned Idx = 0, E = PN->getNumIncomingValues();
|
||||
for (; Idx != E; ++Idx) {
|
||||
if (PN->getIncomingBlock(Idx) == OrigBlock) {
|
||||
PN->setIncomingBlock(Idx, NewDefault);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Only update the first occurrence. */
|
||||
unsigned Idx = 0, E = PN->getNumIncomingValues();
|
||||
for (; Idx != E; ++Idx) {
|
||||
|
||||
if (PN->getIncomingBlock(Idx) == OrigBlock) {
|
||||
|
||||
PN->setIncomingBlock(Idx, NewDefault);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
verifyModule(M);
|
||||
return true;
|
||||
|
||||
verifyModule(M);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SplitSwitchesTransform::runOnModule(Module &M) {
|
||||
|
||||
if (getenv("AFL_QUIET") == NULL)
|
||||
llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n";
|
||||
llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n";
|
||||
splitSwitches(M);
|
||||
verifyModule(M);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
static void registerSplitSwitchesTransPass(const PassManagerBuilder &,
|
||||
legacy::PassManagerBase &PM) {
|
||||
legacy::PassManagerBase &PM) {
|
||||
|
||||
auto p = new SplitSwitchesTransform();
|
||||
PM.add(p);
|
||||
@ -324,3 +391,4 @@ static RegisterStandardPasses RegisterSplitSwitchesTransPass(
|
||||
|
||||
static RegisterStandardPasses RegisterSplitSwitchesTransPass0(
|
||||
PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass);
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
=========================================================
|
||||
High-performance binary-only instrumentation for afl-fuzz
|
||||
=========================================================
|
||||
# High-performance binary-only instrumentation for afl-fuzz
|
||||
|
||||
(See ../docs/README for the general instruction manual.)
|
||||
|
||||
1) Introduction
|
||||
---------------
|
||||
## 1) Introduction
|
||||
|
||||
The code in this directory allows you to build a standalone feature that
|
||||
leverages the QEMU "user emulation" mode and allows callers to obtain
|
||||
@ -16,14 +13,15 @@ with afl-gcc.
|
||||
The usual performance cost is 2-5x, which is considerably better than
|
||||
seen so far in experiments with tools such as DynamoRIO and PIN.
|
||||
|
||||
The idea and much of the implementation comes from Andrew Griffiths.
|
||||
The idea and much of the initial implementation comes from Andrew Griffiths.
|
||||
The actual implementation on QEMU 3 (shipped with afl++) is from
|
||||
Andrea Fioraldi. Special thanks to abiondo that re-enabled TCG chaining.
|
||||
|
||||
2) How to use
|
||||
-------------
|
||||
## 2) How to use
|
||||
|
||||
The feature is implemented with a fairly simple patch to QEMU 2.10.0. The
|
||||
simplest way to build it is to run ./build_qemu_support.sh. The script will
|
||||
download, configure, and compile the QEMU binary for you.
|
||||
The feature is implemented with a patch to QEMU 3.1.0. The simplest way
|
||||
to build it is to run ./build_qemu_support.sh. The script will download,
|
||||
configure, and compile the QEMU binary for you.
|
||||
|
||||
QEMU is a big project, so this will take a while, and you may have to
|
||||
resolve a couple of dependencies (most notably, you will definitely need
|
||||
@ -46,8 +44,26 @@ Note: if you want the QEMU helper to be installed on your system for all
|
||||
users, you need to build it before issuing 'make install' in the parent
|
||||
directory.
|
||||
|
||||
3) Notes on linking
|
||||
-------------------
|
||||
## 3) Options
|
||||
|
||||
There is ./libcompcov/ which implements laf-intel (splitting memcmp,
|
||||
strncmp, etc. to make these conditions easier solvable by afl-fuzz).
|
||||
Highly recommended.
|
||||
|
||||
The option that enables QEMU CompareCoverage is AFL_COMPCOV_LEVEL.
|
||||
AFL_COMPCOV_LEVEL=1 is to instrument comparisons with only immediate
|
||||
values / read-only memory. AFL_COMPCOV_LEVEL=2 instruments all
|
||||
comparison instructions and memory comparison functions when libcompcov
|
||||
is preloaded. Comparison instructions are currently instrumented only
|
||||
on the x86 and x86_64 targets.
|
||||
|
||||
Another option is the environment variable AFL_ENTRYPOINT which allows
|
||||
move the forkserver to a different part, e.g. just before the file is
|
||||
opened (e.g. way after command line parsing and config file loading, etc)
|
||||
which can be a huge speed improvement. Note that the specified address
|
||||
must be an address of a basic block.
|
||||
|
||||
## 4) Notes on linking
|
||||
|
||||
The feature is supported only on Linux. Supporting BSD may amount to porting
|
||||
the changes made to linux-user/elfload.c and applying them to
|
||||
@ -68,8 +84,7 @@ practice, this means two things:
|
||||
Setting AFL_INST_LIBS=1 can be used to circumvent the .text detection logic
|
||||
and instrument every basic block encountered.
|
||||
|
||||
4) Benchmarking
|
||||
---------------
|
||||
## 5) Benchmarking
|
||||
|
||||
If you want to compare the performance of the QEMU instrumentation with that of
|
||||
afl-gcc compiled code against the same target, you need to build the
|
||||
@ -84,8 +99,7 @@ Comparative measurements of execution speed or instrumentation coverage will be
|
||||
fairly meaningless if the optimization levels or instrumentation scopes don't
|
||||
match.
|
||||
|
||||
5) Gotchas, feedback, bugs
|
||||
--------------------------
|
||||
## 6) Gotchas, feedback, bugs
|
||||
|
||||
If you need to fix up checksums or do other cleanup on mutated test cases, see
|
||||
experimental/post_library/ for a viable solution.
|
||||
@ -106,20 +120,18 @@ with -march=core2, can help.
|
||||
Beyond that, this is an early-stage mechanism, so fields reports are welcome.
|
||||
You can send them to <afl-users@googlegroups.com>.
|
||||
|
||||
6) Alternatives: static rewriting
|
||||
---------------------------------
|
||||
## 7) Alternatives: static rewriting
|
||||
|
||||
Statically rewriting binaries just once, instead of attempting to translate
|
||||
them at run time, can be a faster alternative. That said, static rewriting is
|
||||
fraught with peril, because it depends on being able to properly and fully model
|
||||
program control flow without actually executing each and every code path.
|
||||
|
||||
If you want to experiment with this mode of operation, there is a module
|
||||
contributed by Aleksandar Nikolich:
|
||||
The best implementation is this one:
|
||||
|
||||
https://github.com/vanhauser-thc/afl-dyninst
|
||||
https://groups.google.com/forum/#!topic/afl-users/HlSQdbOTlpg
|
||||
|
||||
At this point, the author reports the possibility of hiccups with stripped
|
||||
binaries. That said, if we can get it to be comparably reliable to QEMU, we may
|
||||
decide to switch to this mode, but I had no time to play with it yet.
|
||||
The issue however is Dyninst which is not rewriting the binaries so that
|
||||
they run stable. a lot of crashes happen, especially in C++ programs that
|
||||
use throw/catch. Try it first, and if it works for you be happy as it is
|
||||
2-3x as fast as qemu_mode.
|
@ -3,10 +3,17 @@
|
||||
# american fuzzy lop - QEMU build script
|
||||
# --------------------------------------
|
||||
#
|
||||
# Written by Andrew Griffiths <agriffiths@google.com> and
|
||||
# Michal Zalewski <lcamtuf@google.com>
|
||||
# Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||
# Michal Zalewski <lcamtuf@google.com>
|
||||
#
|
||||
# TCG instrumentation and block chaining support by Andrea Biondo
|
||||
# <andrea.biondo965@gmail.com>
|
||||
#
|
||||
# QEMU 3.1.0 port, TCG thread-safety, CompareCoverage and NeverZero
|
||||
# counters by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
#
|
||||
# Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
# Copyright 2019 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.
|
||||
@ -105,7 +112,8 @@ if [ "$CKSUM" = "$QEMU_SHA384" ]; then
|
||||
|
||||
else
|
||||
|
||||
echo "[-] Error: signature mismatch on $ARCHIVE (perhaps download error?)."
|
||||
echo "[-] Error: signature mismatch on $ARCHIVE (perhaps download error?), removing archive ..."
|
||||
rm -f "$ARCHIVE"
|
||||
exit 1
|
||||
|
||||
fi
|
||||
@ -193,6 +201,8 @@ if [ "$ORIG_CPU_TARGET" = "" ]; then
|
||||
echo "[+] Instrumentation tests passed. "
|
||||
echo "[+] All set, you can now use the -Q mode in afl-fuzz!"
|
||||
|
||||
cd qemu_mode || exit 1
|
||||
|
||||
else
|
||||
|
||||
echo "[!] Note: can't test instrumentation when CPU_TARGET set."
|
||||
@ -200,4 +210,9 @@ else
|
||||
|
||||
fi
|
||||
|
||||
echo "[+] Building libcompcov ..."
|
||||
make -C libcompcov
|
||||
echo "[+] libcompcov ready"
|
||||
echo "[+] All done for qemu_mode, enjoy!"
|
||||
|
||||
exit 0
|
||||
|
@ -18,25 +18,25 @@ HELPER_PATH = $(PREFIX)/lib/afl
|
||||
|
||||
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops
|
||||
CFLAGS ?= -O3 -funroll-loops -I ../../include/
|
||||
CFLAGS += -Wall -Wno-unused-result -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
|
||||
LDFLAGS += -ldl
|
||||
|
||||
all: libcompcov.so compcovtest
|
||||
|
||||
libcompcov.so: libcompcov.so.c ../../config.h
|
||||
$(CC) $(CFLAGS) -shared -fPIC $< -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) -shared -fPIC $< -o ../../$@ $(LDFLAGS)
|
||||
|
||||
.NOTPARALLEL: clean
|
||||
|
||||
clean:
|
||||
rm -f *.o *.so *~ a.out core core.[1-9][0-9]*
|
||||
rm -f libcompcov.so compcovtest
|
||||
rm -f ../../libcompcov.so compcovtest
|
||||
|
||||
compcovtest: compcovtest.cc
|
||||
$(CXX) $< -o $@
|
||||
|
||||
install: all
|
||||
install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH)
|
||||
install -m 755 ../../libcompcov.so $${DESTDIR}$(HELPER_PATH)
|
||||
install -m 644 README.compcov $${DESTDIR}$(HELPER_PATH)
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
================================================================
|
||||
strcmp() / memcmp() CompareCoverage library for AFLplusplus-QEMU
|
||||
================================================================
|
||||
# strcmp() / memcmp() CompareCoverage library for afl++ QEMU
|
||||
|
||||
Written by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
This Linux-only companion library allows you to instrument strcmp(), memcmp(),
|
||||
This Linux-only companion library allows you to instrument `strcmp()`, `memcmp()`,
|
||||
and related functions to log the CompareCoverage of these libcalls.
|
||||
|
||||
Use this with caution. While this can speedup a lot the bypass of hard
|
||||
@ -18,14 +16,20 @@ For optimized binaries this is an issue, those functions are often inlined
|
||||
and this module is not capable to log the coverage in this case.
|
||||
|
||||
If you have the source code of the fuzzing target you should nto use this
|
||||
library and QEMU but build ot with afl-clang-fast and the laf-intel options.
|
||||
library and QEMU but build it with afl-clang-fast and the laf-intel options.
|
||||
|
||||
To use this library make sure to preload it with AFL_PRELOAD.
|
||||
|
||||
```
|
||||
export AFL_PRELOAD=/path/to/libcompcov.so
|
||||
export AFL_QEMU_COMPCOV=1
|
||||
export AFL_COMPCOV_LEVEL=1
|
||||
|
||||
afl-fuzz -Q -i input -o output <your options> -- <target args>
|
||||
```
|
||||
|
||||
The AFL_COMPCOV_LEVEL tells to QEMU and libcompcov how to log comaprisons.
|
||||
Level 1 logs just comparison with immediates / read-only memory and level 2
|
||||
logs all the comparisons.
|
||||
|
||||
The library make use of https://github.com/ouadev/proc_maps_parser and so it is
|
||||
Linux specific. However this is not a strict dependency, other UNIX operating
|
@ -3,13 +3,13 @@
|
||||
// Author: Mateusz Jurczyk (mjurczyk@google.com)
|
||||
//
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@ -17,7 +17,8 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
// solution: echo -ne 'The quick brown fox jumps over the lazy dog\xbe\xba\xfe\xca\xbe\xba\xfe\xca\xde\xc0\xad\xde\xef\xbe' | ./compcovtest
|
||||
// solution: echo -ne 'The quick brown fox jumps over the lazy
|
||||
// dog\xbe\xba\xfe\xca\xbe\xba\xfe\xca\xde\xc0\xad\xde\xef\xbe' | ./compcovtest
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
@ -25,39 +26,40 @@
|
||||
#include <cstring>
|
||||
|
||||
int main() {
|
||||
char buffer[44] = { /* zero padding */ };
|
||||
|
||||
char buffer[44] = {/* zero padding */};
|
||||
fread(buffer, 1, sizeof(buffer) - 1, stdin);
|
||||
|
||||
if (memcmp(&buffer[0], "The quick brown fox ", 20) != 0 ||
|
||||
strncmp(&buffer[20], "jumps over ", 11) != 0 ||
|
||||
strcmp(&buffer[31], "the lazy dog") != 0) {
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
uint64_t x = 0;
|
||||
fread(&x, sizeof(x), 1, stdin);
|
||||
if (x != 0xCAFEBABECAFEBABE) {
|
||||
return 2;
|
||||
}
|
||||
if (x != 0xCAFEBABECAFEBABE) { return 2; }
|
||||
|
||||
uint32_t y = 0;
|
||||
fread(&y, sizeof(y), 1, stdin);
|
||||
if (y != 0xDEADC0DE) {
|
||||
return 3;
|
||||
}
|
||||
if (y != 0xDEADC0DE) { return 3; }
|
||||
|
||||
uint16_t z = 0;
|
||||
fread(&z, sizeof(z), 1, stdin);
|
||||
|
||||
switch (z) {
|
||||
case 0xBEEF:
|
||||
break;
|
||||
|
||||
case 0xBEEF: break;
|
||||
|
||||
default:
|
||||
return 4;
|
||||
default: return 4;
|
||||
|
||||
}
|
||||
|
||||
printf("Puzzle solved, congrats!\n");
|
||||
abort();
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
Written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2019 Andrea Fioraldi. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -27,23 +27,24 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/shm.h>
|
||||
|
||||
#include "../../types.h"
|
||||
#include "../../config.h"
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "pmparser.h"
|
||||
|
||||
#ifndef __linux__
|
||||
# error "Sorry, this library is Linux-specific for now!"
|
||||
#endif /* !__linux__ */
|
||||
#error "Sorry, this library is Linux-specific for now!"
|
||||
#endif /* !__linux__ */
|
||||
|
||||
/* Change this value to tune the compare coverage */
|
||||
|
||||
#define MAX_CMP_LENGTH 32
|
||||
|
||||
static void *__compcov_code_start,
|
||||
*__compcov_code_end;
|
||||
static void *__compcov_code_start, *__compcov_code_end;
|
||||
|
||||
static u8 *__compcov_afl_map;
|
||||
static u8* __compcov_afl_map;
|
||||
|
||||
static u32 __compcov_level;
|
||||
|
||||
static int (*__libc_strcmp)(const char*, const char*);
|
||||
static int (*__libc_strncmp)(const char*, const char*, size_t);
|
||||
@ -53,27 +54,54 @@ static int (*__libc_memcmp)(const void*, const void*, size_t);
|
||||
|
||||
static int debug_fd = -1;
|
||||
|
||||
#define MAX_MAPPINGS 1024
|
||||
|
||||
static struct mapping { void *st, *en; } __compcov_ro[MAX_MAPPINGS];
|
||||
|
||||
static u32 __compcov_ro_cnt;
|
||||
|
||||
/* Check an address against the list of read-only mappings. */
|
||||
|
||||
static u8 __compcov_is_ro(const void* ptr) {
|
||||
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < __compcov_ro_cnt; i++)
|
||||
if (ptr >= __compcov_ro[i].st && ptr <= __compcov_ro[i].en) return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static size_t __strlen2(const char* s1, const char* s2, size_t max_length) {
|
||||
|
||||
static size_t __strlen2(const char *s1, const char *s2, size_t max_length) {
|
||||
// from https://github.com/googleprojectzero/CompareCoverage
|
||||
|
||||
|
||||
size_t len = 0;
|
||||
for (; len < max_length && s1[len] != '\0' && s2[len] != '\0'; len++) { }
|
||||
for (; len < max_length && s1[len] != '\0' && s2[len] != '\0'; len++) {}
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Identify the binary boundaries in the memory mapping */
|
||||
|
||||
static void __compcov_load(void) {
|
||||
|
||||
|
||||
__libc_strcmp = dlsym(RTLD_NEXT, "strcmp");
|
||||
__libc_strncmp = dlsym(RTLD_NEXT, "strncmp");
|
||||
__libc_strcasecmp = dlsym(RTLD_NEXT, "strcasecmp");
|
||||
__libc_strncasecmp = dlsym(RTLD_NEXT, "strncasecmp");
|
||||
__libc_memcmp = dlsym(RTLD_NEXT, "memcmp");
|
||||
|
||||
char *id_str = getenv(SHM_ENV_VAR);
|
||||
int shm_id;
|
||||
|
||||
if (getenv("AFL_QEMU_COMPCOV")) { __compcov_level = 1; }
|
||||
if (getenv("AFL_COMPCOV_LEVEL")) {
|
||||
|
||||
__compcov_level = atoi(getenv("AFL_COMPCOV_LEVEL"));
|
||||
|
||||
}
|
||||
|
||||
char* id_str = getenv(SHM_ENV_VAR);
|
||||
int shm_id;
|
||||
|
||||
if (id_str) {
|
||||
|
||||
@ -81,55 +109,72 @@ static void __compcov_load(void) {
|
||||
__compcov_afl_map = shmat(shm_id, NULL, 0);
|
||||
|
||||
if (__compcov_afl_map == (void*)-1) exit(1);
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
__compcov_afl_map = calloc(1, MAP_SIZE);
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_INST_LIBS")) {
|
||||
|
||||
|
||||
__compcov_code_start = (void*)0;
|
||||
__compcov_code_end = (void*)-1;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
char* bin_name = getenv("AFL_COMPCOV_BINNAME");
|
||||
|
||||
procmaps_iterator* maps = pmparser_parse(-1);
|
||||
procmaps_struct* maps_tmp = NULL;
|
||||
procmaps_struct* maps_tmp = NULL;
|
||||
|
||||
while ((maps_tmp = pmparser_next(maps)) != NULL) {
|
||||
|
||||
|
||||
/* If AFL_COMPCOV_BINNAME is not set pick the first executable segment */
|
||||
if (!bin_name || strstr(maps_tmp->pathname, bin_name) != NULL) {
|
||||
|
||||
|
||||
if (maps_tmp->is_x) {
|
||||
if (!__compcov_code_start)
|
||||
__compcov_code_start = maps_tmp->addr_start;
|
||||
if (!__compcov_code_end)
|
||||
__compcov_code_end = maps_tmp->addr_end;
|
||||
|
||||
if (!__compcov_code_start) __compcov_code_start = maps_tmp->addr_start;
|
||||
if (!__compcov_code_end) __compcov_code_end = maps_tmp->addr_end;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ((maps_tmp->is_w && !maps_tmp->is_r) || __compcov_ro_cnt == MAX_MAPPINGS)
|
||||
continue;
|
||||
|
||||
__compcov_ro[__compcov_ro_cnt].st = maps_tmp->addr_start;
|
||||
__compcov_ro[__compcov_ro_cnt].en = maps_tmp->addr_end;
|
||||
|
||||
}
|
||||
|
||||
pmparser_free(maps);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void __compcov_trace(u64 cur_loc, const u8* v0, const u8* v1, size_t n) {
|
||||
|
||||
size_t i;
|
||||
|
||||
|
||||
if (debug_fd != 1) {
|
||||
|
||||
char debugbuf[4096];
|
||||
snprintf(debugbuf, sizeof(debugbuf), "0x%llx %s %s %lu\n", cur_loc, v0 == NULL ? "(null)" : (char*)v0, v1 == NULL ? "(null)" : (char*)v1, n);
|
||||
snprintf(debugbuf, sizeof(debugbuf), "0x%llx %s %s %lu\n", cur_loc,
|
||||
v0 == NULL ? "(null)" : (char*)v0,
|
||||
v1 == NULL ? "(null)" : (char*)v1, n);
|
||||
write(debug_fd, debugbuf, strlen(debugbuf));
|
||||
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < n && v0[i] == v1[i]; ++i) {
|
||||
|
||||
__compcov_afl_map[cur_loc +i]++;
|
||||
|
||||
__compcov_afl_map[cur_loc + i]++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Check an address against the list of read-only mappings. */
|
||||
@ -137,8 +182,8 @@ static void __compcov_trace(u64 cur_loc, const u8* v0, const u8* v1, size_t n) {
|
||||
static u8 __compcov_is_in_bound(const void* ptr) {
|
||||
|
||||
return ptr >= __compcov_code_start && ptr < __compcov_code_end;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Replacements for strcmp(), memcmp(), and so on. Note that these will be used
|
||||
only if the target is compiled with -fno-builtins and linked dynamically. */
|
||||
@ -148,122 +193,145 @@ static u8 __compcov_is_in_bound(const void* ptr) {
|
||||
int strcmp(const char* str1, const char* str2) {
|
||||
|
||||
void* retaddr = __builtin_return_address(0);
|
||||
|
||||
if (__compcov_is_in_bound(retaddr)) {
|
||||
|
||||
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
|
||||
|
||||
if (__compcov_is_in_bound(retaddr) &&
|
||||
!(__compcov_level < 2 && !__compcov_is_ro(str1) &&
|
||||
!__compcov_is_ro(str2))) {
|
||||
|
||||
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH + 1);
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
|
||||
__compcov_trace(cur_loc, str1, str2, n);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return __libc_strcmp(str1, str2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#undef strncmp
|
||||
|
||||
int strncmp(const char* str1, const char* str2, size_t len) {
|
||||
|
||||
void* retaddr = __builtin_return_address(0);
|
||||
|
||||
if (__compcov_is_in_bound(retaddr)) {
|
||||
|
||||
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
|
||||
if (__compcov_is_in_bound(retaddr) &&
|
||||
!(__compcov_level < 2 && !__compcov_is_ro(str1) &&
|
||||
!__compcov_is_ro(str2))) {
|
||||
|
||||
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH + 1);
|
||||
n = MIN(n, len);
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
__compcov_trace(cur_loc, str1, str2, n);
|
||||
}
|
||||
}
|
||||
|
||||
return __libc_strncmp(str1, str2, len);
|
||||
}
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
__compcov_trace(cur_loc, str1, str2, n);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return __libc_strncmp(str1, str2, len);
|
||||
|
||||
}
|
||||
|
||||
#undef strcasecmp
|
||||
|
||||
int strcasecmp(const char* str1, const char* str2) {
|
||||
|
||||
void* retaddr = __builtin_return_address(0);
|
||||
|
||||
if (__compcov_is_in_bound(retaddr)) {
|
||||
|
||||
if (__compcov_is_in_bound(retaddr) &&
|
||||
!(__compcov_level < 2 && !__compcov_is_ro(str1) &&
|
||||
!__compcov_is_ro(str2))) {
|
||||
|
||||
/* Fallback to strcmp, maybe improve in future */
|
||||
|
||||
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
|
||||
|
||||
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH + 1);
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
|
||||
__compcov_trace(cur_loc, str1, str2, n);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return __libc_strcasecmp(str1, str2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#undef strncasecmp
|
||||
|
||||
int strncasecmp(const char* str1, const char* str2, size_t len) {
|
||||
|
||||
void* retaddr = __builtin_return_address(0);
|
||||
|
||||
if (__compcov_is_in_bound(retaddr)) {
|
||||
|
||||
if (__compcov_is_in_bound(retaddr) &&
|
||||
!(__compcov_level < 2 && !__compcov_is_ro(str1) &&
|
||||
!__compcov_is_ro(str2))) {
|
||||
|
||||
/* Fallback to strncmp, maybe improve in future */
|
||||
|
||||
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
|
||||
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH + 1);
|
||||
n = MIN(n, len);
|
||||
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
|
||||
__compcov_trace(cur_loc, str1, str2, n);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return __libc_strncasecmp(str1, str2, len);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#undef memcmp
|
||||
|
||||
int memcmp(const void* mem1, const void* mem2, size_t len) {
|
||||
|
||||
void* retaddr = __builtin_return_address(0);
|
||||
|
||||
if (__compcov_is_in_bound(retaddr)) {
|
||||
|
||||
if (__compcov_is_in_bound(retaddr) &&
|
||||
!(__compcov_level < 2 && !__compcov_is_ro(mem1) &&
|
||||
!__compcov_is_ro(mem2))) {
|
||||
|
||||
size_t n = len;
|
||||
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
|
||||
__compcov_trace(cur_loc, mem1, mem2, n);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return __libc_memcmp(mem1, mem2, len);
|
||||
|
||||
}
|
||||
|
||||
/* Init code to open init the library. */
|
||||
@ -271,9 +339,10 @@ int memcmp(const void* mem1, const void* mem2, size_t len) {
|
||||
__attribute__((constructor)) void __compcov_init(void) {
|
||||
|
||||
if (getenv("AFL_QEMU_COMPCOV_DEBUG") != NULL)
|
||||
debug_fd = open("compcov.debug", O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644);
|
||||
debug_fd =
|
||||
open("compcov.debug", O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644);
|
||||
|
||||
__compcov_load();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,43 +24,49 @@ implied warranty.
|
||||
#include <errno.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
//maximum line length in a procmaps file
|
||||
#define PROCMAPS_LINE_MAX_LENGTH (PATH_MAX + 100)
|
||||
// maximum line length in a procmaps file
|
||||
#define PROCMAPS_LINE_MAX_LENGTH (PATH_MAX + 100)
|
||||
/**
|
||||
* procmaps_struct
|
||||
* @desc hold all the information about an area in the process's VM
|
||||
*/
|
||||
typedef struct procmaps_struct{
|
||||
void* addr_start; //< start address of the area
|
||||
void* addr_end; //< end address
|
||||
unsigned long length; //< size of the range
|
||||
typedef struct procmaps_struct {
|
||||
|
||||
char perm[5]; //< permissions rwxp
|
||||
short is_r; //< rewrote of perm with short flags
|
||||
short is_w;
|
||||
short is_x;
|
||||
short is_p;
|
||||
void* addr_start; //< start address of the area
|
||||
void* addr_end; //< end address
|
||||
unsigned long length; //< size of the range
|
||||
|
||||
long offset; //< offset
|
||||
char dev[12]; //< dev major:minor
|
||||
int inode; //< inode of the file that backs the area
|
||||
char perm[5]; //< permissions rwxp
|
||||
short is_r; //< rewrote of perm with short flags
|
||||
short is_w;
|
||||
short is_x;
|
||||
short is_p;
|
||||
|
||||
long offset; //< offset
|
||||
char dev[12]; //< dev major:minor
|
||||
int inode; //< inode of the file that backs the area
|
||||
|
||||
char pathname[600]; //< the path of the file that backs the area
|
||||
// chained list
|
||||
struct procmaps_struct* next; //<handler of the chinaed list
|
||||
|
||||
char pathname[600]; //< the path of the file that backs the area
|
||||
//chained list
|
||||
struct procmaps_struct* next; //<handler of the chinaed list
|
||||
} procmaps_struct;
|
||||
|
||||
/**
|
||||
* procmaps_iterator
|
||||
* @desc holds iterating information
|
||||
*/
|
||||
typedef struct procmaps_iterator{
|
||||
procmaps_struct* head;
|
||||
procmaps_struct* current;
|
||||
typedef struct procmaps_iterator {
|
||||
|
||||
procmaps_struct* head;
|
||||
procmaps_struct* current;
|
||||
|
||||
} procmaps_iterator;
|
||||
|
||||
/**
|
||||
* pmparser_parse
|
||||
* @param pid the process id whose memory map to be parser. the current process if pid<0
|
||||
* @param pid the process id whose memory map to be parser. the current process
|
||||
* if pid<0
|
||||
* @return an iterator over all the nodes
|
||||
*/
|
||||
procmaps_iterator* pmparser_parse(int pid);
|
||||
@ -83,198 +89,238 @@ void pmparser_free(procmaps_iterator* p_procmaps_it);
|
||||
* _pmparser_split_line
|
||||
* @description internal usage
|
||||
*/
|
||||
void _pmparser_split_line(char*buf,char*addr1,char*addr2,char*perm, char* offset, char* device,char*inode,char* pathname);
|
||||
void _pmparser_split_line(char* buf, char* addr1, char* addr2, char* perm,
|
||||
char* offset, char* device, char* inode,
|
||||
char* pathname);
|
||||
|
||||
/**
|
||||
* pmparser_print
|
||||
* @param map the head of the list
|
||||
* @order the order of the area to print, -1 to print everything
|
||||
*/
|
||||
void pmparser_print(procmaps_struct* map,int order);
|
||||
|
||||
void pmparser_print(procmaps_struct* map, int order);
|
||||
|
||||
/**
|
||||
* gobal variables
|
||||
*/
|
||||
//procmaps_struct* g_last_head=NULL;
|
||||
//procmaps_struct* g_current=NULL;
|
||||
// procmaps_struct* g_last_head=NULL;
|
||||
// procmaps_struct* g_current=NULL;
|
||||
|
||||
procmaps_iterator* pmparser_parse(int pid) {
|
||||
|
||||
procmaps_iterator* pmparser_parse(int pid){
|
||||
procmaps_iterator* maps_it = malloc(sizeof(procmaps_iterator));
|
||||
char maps_path[500];
|
||||
if(pid>=0 ){
|
||||
sprintf(maps_path,"/proc/%d/maps",pid);
|
||||
}else{
|
||||
sprintf(maps_path,"/proc/self/maps");
|
||||
}
|
||||
FILE* file=fopen(maps_path,"r");
|
||||
if(!file){
|
||||
fprintf(stderr,"pmparser : cannot open the memory maps, %s\n",strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
int ind=0;char buf[PROCMAPS_LINE_MAX_LENGTH];
|
||||
//int c;
|
||||
procmaps_struct* list_maps=NULL;
|
||||
procmaps_struct* tmp;
|
||||
procmaps_struct* current_node=list_maps;
|
||||
char addr1[20],addr2[20], perm[8], offset[20], dev[10],inode[30],pathname[PATH_MAX];
|
||||
while( !feof(file) ){
|
||||
fgets(buf,PROCMAPS_LINE_MAX_LENGTH,file);
|
||||
//allocate a node
|
||||
tmp=(procmaps_struct*)malloc(sizeof(procmaps_struct));
|
||||
//fill the node
|
||||
_pmparser_split_line(buf,addr1,addr2,perm,offset, dev,inode,pathname);
|
||||
//printf("#%s",buf);
|
||||
//printf("%s-%s %s %s %s %s\t%s\n",addr1,addr2,perm,offset,dev,inode,pathname);
|
||||
//addr_start & addr_end
|
||||
//unsigned long l_addr_start;
|
||||
sscanf(addr1,"%lx",(long unsigned *)&tmp->addr_start );
|
||||
sscanf(addr2,"%lx",(long unsigned *)&tmp->addr_end );
|
||||
//size
|
||||
tmp->length=(unsigned long)(tmp->addr_end-tmp->addr_start);
|
||||
//perm
|
||||
strcpy(tmp->perm,perm);
|
||||
tmp->is_r=(perm[0]=='r');
|
||||
tmp->is_w=(perm[1]=='w');
|
||||
tmp->is_x=(perm[2]=='x');
|
||||
tmp->is_p=(perm[3]=='p');
|
||||
procmaps_iterator* maps_it = malloc(sizeof(procmaps_iterator));
|
||||
char maps_path[500];
|
||||
if (pid >= 0) {
|
||||
|
||||
//offset
|
||||
sscanf(offset,"%lx",&tmp->offset );
|
||||
//device
|
||||
strcpy(tmp->dev,dev);
|
||||
//inode
|
||||
tmp->inode=atoi(inode);
|
||||
//pathname
|
||||
strcpy(tmp->pathname,pathname);
|
||||
tmp->next=NULL;
|
||||
//attach the node
|
||||
if(ind==0){
|
||||
list_maps=tmp;
|
||||
list_maps->next=NULL;
|
||||
current_node=list_maps;
|
||||
}
|
||||
current_node->next=tmp;
|
||||
current_node=tmp;
|
||||
ind++;
|
||||
//printf("%s",buf);
|
||||
}
|
||||
sprintf(maps_path, "/proc/%d/maps", pid);
|
||||
|
||||
//close file
|
||||
fclose(file);
|
||||
} else {
|
||||
|
||||
sprintf(maps_path, "/proc/self/maps");
|
||||
|
||||
//g_last_head=list_maps;
|
||||
maps_it->head = list_maps;
|
||||
maps_it->current = list_maps;
|
||||
return maps_it;
|
||||
}
|
||||
}
|
||||
|
||||
FILE* file = fopen(maps_path, "r");
|
||||
if (!file) {
|
||||
|
||||
procmaps_struct* pmparser_next(procmaps_iterator* p_procmaps_it){
|
||||
if(p_procmaps_it->current == NULL)
|
||||
return NULL;
|
||||
procmaps_struct* p_current = p_procmaps_it->current;
|
||||
p_procmaps_it->current = p_procmaps_it->current->next;
|
||||
return p_current;
|
||||
/*
|
||||
if(g_current==NULL){
|
||||
g_current=g_last_head;
|
||||
}else
|
||||
g_current=g_current->next;
|
||||
fprintf(stderr, "pmparser : cannot open the memory maps, %s\n",
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
|
||||
return g_current;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
int ind = 0;
|
||||
char buf[PROCMAPS_LINE_MAX_LENGTH];
|
||||
// int c;
|
||||
procmaps_struct* list_maps = NULL;
|
||||
procmaps_struct* tmp;
|
||||
procmaps_struct* current_node = list_maps;
|
||||
char addr1[20], addr2[20], perm[8], offset[20], dev[10], inode[30],
|
||||
pathname[PATH_MAX];
|
||||
while (!feof(file)) {
|
||||
|
||||
fgets(buf, PROCMAPS_LINE_MAX_LENGTH, file);
|
||||
// allocate a node
|
||||
tmp = (procmaps_struct*)malloc(sizeof(procmaps_struct));
|
||||
// fill the node
|
||||
_pmparser_split_line(buf, addr1, addr2, perm, offset, dev, inode, pathname);
|
||||
// printf("#%s",buf);
|
||||
// printf("%s-%s %s %s %s
|
||||
// %s\t%s\n",addr1,addr2,perm,offset,dev,inode,pathname); addr_start &
|
||||
// addr_end unsigned long l_addr_start;
|
||||
sscanf(addr1, "%lx", (long unsigned*)&tmp->addr_start);
|
||||
sscanf(addr2, "%lx", (long unsigned*)&tmp->addr_end);
|
||||
// size
|
||||
tmp->length = (unsigned long)(tmp->addr_end - tmp->addr_start);
|
||||
// perm
|
||||
strcpy(tmp->perm, perm);
|
||||
tmp->is_r = (perm[0] == 'r');
|
||||
tmp->is_w = (perm[1] == 'w');
|
||||
tmp->is_x = (perm[2] == 'x');
|
||||
tmp->is_p = (perm[3] == 'p');
|
||||
|
||||
void pmparser_free(procmaps_iterator* p_procmaps_it){
|
||||
procmaps_struct* maps_list = p_procmaps_it->head;
|
||||
if(maps_list==NULL) return ;
|
||||
procmaps_struct* act=maps_list;
|
||||
procmaps_struct* nxt=act->next;
|
||||
while(act!=NULL){
|
||||
free(act);
|
||||
act=nxt;
|
||||
if(nxt!=NULL)
|
||||
nxt=nxt->next;
|
||||
}
|
||||
// offset
|
||||
sscanf(offset, "%lx", &tmp->offset);
|
||||
// device
|
||||
strcpy(tmp->dev, dev);
|
||||
// inode
|
||||
tmp->inode = atoi(inode);
|
||||
// pathname
|
||||
strcpy(tmp->pathname, pathname);
|
||||
tmp->next = NULL;
|
||||
// attach the node
|
||||
if (ind == 0) {
|
||||
|
||||
list_maps = tmp;
|
||||
list_maps->next = NULL;
|
||||
current_node = list_maps;
|
||||
|
||||
}
|
||||
|
||||
current_node->next = tmp;
|
||||
current_node = tmp;
|
||||
ind++;
|
||||
// printf("%s",buf);
|
||||
|
||||
}
|
||||
|
||||
// close file
|
||||
fclose(file);
|
||||
|
||||
// g_last_head=list_maps;
|
||||
maps_it->head = list_maps;
|
||||
maps_it->current = list_maps;
|
||||
return maps_it;
|
||||
|
||||
}
|
||||
|
||||
procmaps_struct* pmparser_next(procmaps_iterator* p_procmaps_it) {
|
||||
|
||||
void _pmparser_split_line(
|
||||
char*buf,char*addr1,char*addr2,
|
||||
char*perm,char* offset,char* device,char*inode,
|
||||
char* pathname){
|
||||
//
|
||||
int orig=0;
|
||||
int i=0;
|
||||
//addr1
|
||||
while(buf[i]!='-'){
|
||||
addr1[i-orig]=buf[i];
|
||||
i++;
|
||||
}
|
||||
addr1[i]='\0';
|
||||
i++;
|
||||
//addr2
|
||||
orig=i;
|
||||
while(buf[i]!='\t' && buf[i]!=' '){
|
||||
addr2[i-orig]=buf[i];
|
||||
i++;
|
||||
}
|
||||
addr2[i-orig]='\0';
|
||||
if (p_procmaps_it->current == NULL) return NULL;
|
||||
procmaps_struct* p_current = p_procmaps_it->current;
|
||||
p_procmaps_it->current = p_procmaps_it->current->next;
|
||||
return p_current;
|
||||
/*
|
||||
if(g_current==NULL){
|
||||
|
||||
//perm
|
||||
while(buf[i]=='\t' || buf[i]==' ')
|
||||
i++;
|
||||
orig=i;
|
||||
while(buf[i]!='\t' && buf[i]!=' '){
|
||||
perm[i-orig]=buf[i];
|
||||
i++;
|
||||
}
|
||||
perm[i-orig]='\0';
|
||||
//offset
|
||||
while(buf[i]=='\t' || buf[i]==' ')
|
||||
i++;
|
||||
orig=i;
|
||||
while(buf[i]!='\t' && buf[i]!=' '){
|
||||
offset[i-orig]=buf[i];
|
||||
i++;
|
||||
}
|
||||
offset[i-orig]='\0';
|
||||
//dev
|
||||
while(buf[i]=='\t' || buf[i]==' ')
|
||||
i++;
|
||||
orig=i;
|
||||
while(buf[i]!='\t' && buf[i]!=' '){
|
||||
device[i-orig]=buf[i];
|
||||
i++;
|
||||
}
|
||||
device[i-orig]='\0';
|
||||
//inode
|
||||
while(buf[i]=='\t' || buf[i]==' ')
|
||||
i++;
|
||||
orig=i;
|
||||
while(buf[i]!='\t' && buf[i]!=' '){
|
||||
inode[i-orig]=buf[i];
|
||||
i++;
|
||||
}
|
||||
inode[i-orig]='\0';
|
||||
//pathname
|
||||
pathname[0]='\0';
|
||||
while(buf[i]=='\t' || buf[i]==' ')
|
||||
i++;
|
||||
orig=i;
|
||||
while(buf[i]!='\t' && buf[i]!=' ' && buf[i]!='\n'){
|
||||
pathname[i-orig]=buf[i];
|
||||
i++;
|
||||
}
|
||||
pathname[i-orig]='\0';
|
||||
g_current=g_last_head;
|
||||
|
||||
}else
|
||||
|
||||
g_current=g_current->next;
|
||||
|
||||
return g_current;
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
void pmparser_free(procmaps_iterator* p_procmaps_it) {
|
||||
|
||||
procmaps_struct* maps_list = p_procmaps_it->head;
|
||||
if (maps_list == NULL) return;
|
||||
procmaps_struct* act = maps_list;
|
||||
procmaps_struct* nxt = act->next;
|
||||
while (act != NULL) {
|
||||
|
||||
free(act);
|
||||
act = nxt;
|
||||
if (nxt != NULL) nxt = nxt->next;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void _pmparser_split_line(char* buf, char* addr1, char* addr2, char* perm,
|
||||
char* offset, char* device, char* inode,
|
||||
char* pathname) {
|
||||
|
||||
//
|
||||
int orig = 0;
|
||||
int i = 0;
|
||||
// addr1
|
||||
while (buf[i] != '-') {
|
||||
|
||||
addr1[i - orig] = buf[i];
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
addr1[i] = '\0';
|
||||
i++;
|
||||
// addr2
|
||||
orig = i;
|
||||
while (buf[i] != '\t' && buf[i] != ' ') {
|
||||
|
||||
addr2[i - orig] = buf[i];
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
addr2[i - orig] = '\0';
|
||||
|
||||
// perm
|
||||
while (buf[i] == '\t' || buf[i] == ' ')
|
||||
i++;
|
||||
orig = i;
|
||||
while (buf[i] != '\t' && buf[i] != ' ') {
|
||||
|
||||
perm[i - orig] = buf[i];
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
perm[i - orig] = '\0';
|
||||
// offset
|
||||
while (buf[i] == '\t' || buf[i] == ' ')
|
||||
i++;
|
||||
orig = i;
|
||||
while (buf[i] != '\t' && buf[i] != ' ') {
|
||||
|
||||
offset[i - orig] = buf[i];
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
offset[i - orig] = '\0';
|
||||
// dev
|
||||
while (buf[i] == '\t' || buf[i] == ' ')
|
||||
i++;
|
||||
orig = i;
|
||||
while (buf[i] != '\t' && buf[i] != ' ') {
|
||||
|
||||
device[i - orig] = buf[i];
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
device[i - orig] = '\0';
|
||||
// inode
|
||||
while (buf[i] == '\t' || buf[i] == ' ')
|
||||
i++;
|
||||
orig = i;
|
||||
while (buf[i] != '\t' && buf[i] != ' ') {
|
||||
|
||||
inode[i - orig] = buf[i];
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
inode[i - orig] = '\0';
|
||||
// pathname
|
||||
pathname[0] = '\0';
|
||||
while (buf[i] == '\t' || buf[i] == ' ')
|
||||
i++;
|
||||
orig = i;
|
||||
while (buf[i] != '\t' && buf[i] != ' ' && buf[i] != '\n') {
|
||||
|
||||
pathname[i - orig] = buf[i];
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
pathname[i - orig] = '\0';
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
49
qemu_mode/patches/afl-qemu-common.h
Normal file
49
qemu_mode/patches/afl-qemu-common.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
american fuzzy lop++ - high-performance binary-only instrumentation
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
TCG instrumentation and block chaining support by Andrea Biondo
|
||||
<andrea.biondo965@gmail.com>
|
||||
|
||||
QEMU 3.1.0 port, TCG thread-safety, CompareCoverage and NeverZero
|
||||
counters by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This code is a shim patched into the separately-distributed source
|
||||
code of QEMU 3.1.0. It leverages the built-in QEMU tracing functionality
|
||||
to implement AFL-style instrumentation and to take care of the remaining
|
||||
parts of the AFL fork server logic.
|
||||
|
||||
The resulting QEMU binary is essentially a standalone instrumentation
|
||||
tool; for an example of how to leverage it for other purposes, you can
|
||||
have a look at afl-showmap.c.
|
||||
|
||||
*/
|
||||
|
||||
#include "../../config.h"
|
||||
|
||||
/* NeverZero */
|
||||
|
||||
#if (defined(__x86_64__) || defined(__i386__)) && defined(AFL_QEMU_NOT_ZERO)
|
||||
#define INC_AFL_AREA(loc) \
|
||||
asm volatile( \
|
||||
"incb (%0, %1, 1)\n" \
|
||||
"adcb $0, (%0, %1, 1)\n" \
|
||||
: /* no out */ \
|
||||
: "r"(afl_area_ptr), "r"(loc) \
|
||||
: "memory", "eax")
|
||||
#else
|
||||
#define INC_AFL_AREA(loc) afl_area_ptr[loc]++
|
||||
#endif
|
||||
|
@ -1,19 +1,18 @@
|
||||
/*
|
||||
american fuzzy lop - high-performance binary-only instrumentation
|
||||
-----------------------------------------------------------------
|
||||
american fuzzy lop++ - high-performance binary-only instrumentation
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Idea & design very much by Andrew Griffiths.
|
||||
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
TCG instrumentation and block chaining support by Andrea Biondo
|
||||
<andrea.biondo965@gmail.com>
|
||||
|
||||
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
||||
<andreafioraldi@gmail.com>
|
||||
QEMU 3.1.0 port, TCG thread-safety, CompareCoverage and NeverZero
|
||||
counters by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -43,11 +42,16 @@
|
||||
_start and does the usual forkserver stuff, not very different from
|
||||
regular instrumentation injected via afl-as.h. */
|
||||
|
||||
#define AFL_QEMU_CPU_SNIPPET2 do { \
|
||||
if(itb->pc == afl_entry_point) { \
|
||||
afl_setup(); \
|
||||
afl_forkserver(cpu); \
|
||||
} \
|
||||
#define AFL_QEMU_CPU_SNIPPET2 \
|
||||
do { \
|
||||
\
|
||||
if (itb->pc == afl_entry_point) { \
|
||||
\
|
||||
afl_setup(); \
|
||||
afl_forkserver(cpu); \
|
||||
\
|
||||
} \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* We use one additional file descriptor to relay "needs translation"
|
||||
@ -57,60 +61,71 @@
|
||||
|
||||
/* This is equivalent to afl-as.h: */
|
||||
|
||||
static unsigned char dummy[MAP_SIZE]; /* costs MAP_SIZE but saves a few instructions */
|
||||
unsigned char *afl_area_ptr = dummy; /* Exported for afl_gen_trace */
|
||||
static unsigned char
|
||||
dummy[MAP_SIZE]; /* costs MAP_SIZE but saves a few instructions */
|
||||
unsigned char *afl_area_ptr = dummy; /* Exported for afl_gen_trace */
|
||||
|
||||
/* Exported variables populated by the code patched into elfload.c: */
|
||||
|
||||
abi_ulong afl_entry_point, /* ELF entry point (_start) */
|
||||
afl_start_code, /* .text start pointer */
|
||||
afl_end_code; /* .text end pointer */
|
||||
abi_ulong afl_entry_point, /* ELF entry point (_start) */
|
||||
afl_start_code, /* .text start pointer */
|
||||
afl_end_code; /* .text end pointer */
|
||||
|
||||
u8 afl_enable_compcov;
|
||||
u8 afl_compcov_level;
|
||||
|
||||
/* Set in the child process in forkserver mode: */
|
||||
|
||||
static int forkserver_installed = 0;
|
||||
static int forkserver_installed = 0;
|
||||
static unsigned char afl_fork_child;
|
||||
unsigned int afl_forksrv_pid;
|
||||
unsigned int afl_forksrv_pid;
|
||||
|
||||
/* Instrumentation ratio: */
|
||||
|
||||
unsigned int afl_inst_rms = MAP_SIZE; /* Exported for afl_gen_trace */
|
||||
unsigned int afl_inst_rms = MAP_SIZE; /* Exported for afl_gen_trace */
|
||||
|
||||
/* Function declarations. */
|
||||
|
||||
static void afl_setup(void);
|
||||
static void afl_forkserver(CPUState*);
|
||||
static void afl_forkserver(CPUState *);
|
||||
|
||||
static void afl_wait_tsl(CPUState*, int);
|
||||
static void afl_request_tsl(target_ulong, target_ulong, uint32_t, uint32_t, TranslationBlock*, int);
|
||||
static void afl_wait_tsl(CPUState *, int);
|
||||
static void afl_request_tsl(target_ulong, target_ulong, uint32_t, uint32_t,
|
||||
TranslationBlock *, int);
|
||||
|
||||
/* Data structures passed around by the translate handlers: */
|
||||
|
||||
struct afl_tb {
|
||||
|
||||
target_ulong pc;
|
||||
target_ulong cs_base;
|
||||
uint32_t flags;
|
||||
uint32_t cf_mask;
|
||||
uint32_t flags;
|
||||
uint32_t cf_mask;
|
||||
|
||||
};
|
||||
|
||||
struct afl_tsl {
|
||||
|
||||
struct afl_tb tb;
|
||||
char is_chain;
|
||||
char is_chain;
|
||||
|
||||
};
|
||||
|
||||
struct afl_chain {
|
||||
|
||||
struct afl_tb last_tb;
|
||||
uint32_t cf_mask;
|
||||
int tb_exit;
|
||||
uint32_t cf_mask;
|
||||
int tb_exit;
|
||||
|
||||
};
|
||||
|
||||
/* Some forward decls: */
|
||||
|
||||
TranslationBlock *tb_htable_lookup(CPUState*, target_ulong, target_ulong, uint32_t, uint32_t);
|
||||
static inline TranslationBlock *tb_find(CPUState*, TranslationBlock*, int, uint32_t);
|
||||
static inline void tb_add_jump(TranslationBlock *tb, int n, TranslationBlock *tb_next);
|
||||
TranslationBlock *tb_htable_lookup(CPUState *, target_ulong, target_ulong,
|
||||
uint32_t, uint32_t);
|
||||
static inline TranslationBlock *tb_find(CPUState *, TranslationBlock *, int,
|
||||
uint32_t);
|
||||
static inline void tb_add_jump(TranslationBlock *tb, int n,
|
||||
TranslationBlock *tb_next);
|
||||
|
||||
/*************************
|
||||
* ACTUAL IMPLEMENTATION *
|
||||
@ -120,8 +135,7 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, TranslationBlock *tb
|
||||
|
||||
static void afl_setup(void) {
|
||||
|
||||
char *id_str = getenv(SHM_ENV_VAR),
|
||||
*inst_r = getenv("AFL_INST_RATIO");
|
||||
char *id_str = getenv(SHM_ENV_VAR), *inst_r = getenv("AFL_INST_RATIO");
|
||||
|
||||
int shm_id;
|
||||
|
||||
@ -143,7 +157,7 @@ static void afl_setup(void) {
|
||||
shm_id = atoi(id_str);
|
||||
afl_area_ptr = shmat(shm_id, NULL, 0);
|
||||
|
||||
if (afl_area_ptr == (void*)-1) exit(1);
|
||||
if (afl_area_ptr == (void *)-1) exit(1);
|
||||
|
||||
/* With AFL_INST_RATIO set to a low value, we want to touch the bitmap
|
||||
so that the parent doesn't give up on us. */
|
||||
@ -155,13 +169,16 @@ static void afl_setup(void) {
|
||||
if (getenv("AFL_INST_LIBS")) {
|
||||
|
||||
afl_start_code = 0;
|
||||
afl_end_code = (abi_ulong)-1;
|
||||
afl_end_code = (abi_ulong)-1;
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_QEMU_COMPCOV")) {
|
||||
|
||||
afl_enable_compcov = 1;
|
||||
/* Maintain for compatibility */
|
||||
if (getenv("AFL_QEMU_COMPCOV")) { afl_compcov_level = 1; }
|
||||
if (getenv("AFL_COMPCOV_LEVEL")) {
|
||||
|
||||
afl_compcov_level = atoi(getenv("AFL_COMPCOV_LEVEL"));
|
||||
|
||||
}
|
||||
|
||||
/* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm
|
||||
@ -172,17 +189,15 @@ static void afl_setup(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Fork server logic, invoked once we hit _start. */
|
||||
|
||||
static void afl_forkserver(CPUState *cpu) {
|
||||
|
||||
static unsigned char tmp[4];
|
||||
|
||||
if (forkserver_installed == 1)
|
||||
return;
|
||||
if (forkserver_installed == 1) return;
|
||||
forkserver_installed = 1;
|
||||
//if (!afl_area_ptr) return; // not necessary because of fixed dummy buffer
|
||||
// if (!afl_area_ptr) return; // not necessary because of fixed dummy buffer
|
||||
|
||||
/* Tell the parent that we're alive. If the parent doesn't want
|
||||
to talk, assume that we're not running in forkserver mode. */
|
||||
@ -196,7 +211,7 @@ static void afl_forkserver(CPUState *cpu) {
|
||||
while (1) {
|
||||
|
||||
pid_t child_pid;
|
||||
int status, t_fd[2];
|
||||
int status, t_fd[2];
|
||||
|
||||
/* Whoops, parent dead? */
|
||||
|
||||
@ -242,59 +257,60 @@ static void afl_forkserver(CPUState *cpu) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* This code is invoked whenever QEMU decides that it doesn't have a
|
||||
translation of a particular block and needs to compute it, or when it
|
||||
decides to chain two TBs together. When this happens, we tell the parent to
|
||||
mirror the operation, so that the next fork() has a cached copy. */
|
||||
|
||||
static void afl_request_tsl(target_ulong pc, target_ulong cb, uint32_t flags, uint32_t cf_mask,
|
||||
TranslationBlock *last_tb, int tb_exit) {
|
||||
static void afl_request_tsl(target_ulong pc, target_ulong cb, uint32_t flags,
|
||||
uint32_t cf_mask, TranslationBlock *last_tb,
|
||||
int tb_exit) {
|
||||
|
||||
struct afl_tsl t;
|
||||
struct afl_tsl t;
|
||||
struct afl_chain c;
|
||||
|
||||
if (!afl_fork_child) return;
|
||||
|
||||
t.tb.pc = pc;
|
||||
t.tb.pc = pc;
|
||||
t.tb.cs_base = cb;
|
||||
t.tb.flags = flags;
|
||||
t.tb.flags = flags;
|
||||
t.tb.cf_mask = cf_mask;
|
||||
t.is_chain = (last_tb != NULL);
|
||||
t.is_chain = (last_tb != NULL);
|
||||
|
||||
if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
||||
return;
|
||||
|
||||
if (t.is_chain) {
|
||||
c.last_tb.pc = last_tb->pc;
|
||||
|
||||
c.last_tb.pc = last_tb->pc;
|
||||
c.last_tb.cs_base = last_tb->cs_base;
|
||||
c.last_tb.flags = last_tb->flags;
|
||||
c.cf_mask = cf_mask;
|
||||
c.tb_exit = tb_exit;
|
||||
c.last_tb.flags = last_tb->flags;
|
||||
c.cf_mask = cf_mask;
|
||||
c.tb_exit = tb_exit;
|
||||
|
||||
if (write(TSL_FD, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain))
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Check if an address is valid in the current mapping */
|
||||
|
||||
static inline int is_valid_addr(target_ulong addr) {
|
||||
|
||||
int l, flags;
|
||||
target_ulong page;
|
||||
void * p;
|
||||
|
||||
page = addr & TARGET_PAGE_MASK;
|
||||
l = (page + TARGET_PAGE_SIZE) - addr;
|
||||
|
||||
flags = page_get_flags(page);
|
||||
if (!(flags & PAGE_VALID) || !(flags & PAGE_READ))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
int l, flags;
|
||||
target_ulong page;
|
||||
void * p;
|
||||
|
||||
page = addr & TARGET_PAGE_MASK;
|
||||
l = (page + TARGET_PAGE_SIZE) - addr;
|
||||
|
||||
flags = page_get_flags(page);
|
||||
if (!(flags & PAGE_VALID) || !(flags & PAGE_READ)) return 0;
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* This is the other side of the same channel. Since timeouts are handled by
|
||||
@ -302,8 +318,8 @@ static inline int is_valid_addr(target_ulong addr) {
|
||||
|
||||
static void afl_wait_tsl(CPUState *cpu, int fd) {
|
||||
|
||||
struct afl_tsl t;
|
||||
struct afl_chain c;
|
||||
struct afl_tsl t;
|
||||
struct afl_chain c;
|
||||
TranslationBlock *tb, *last_tb;
|
||||
|
||||
while (1) {
|
||||
@ -312,30 +328,33 @@ static void afl_wait_tsl(CPUState *cpu, int fd) {
|
||||
|
||||
/* Broken pipe means it's time to return to the fork server routine. */
|
||||
|
||||
if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
||||
break;
|
||||
if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) break;
|
||||
|
||||
tb = tb_htable_lookup(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, t.tb.cf_mask);
|
||||
|
||||
if(!tb) {
|
||||
|
||||
if (!tb) {
|
||||
|
||||
/* The child may request to transate a block of memory that is not
|
||||
mapped in the parent (e.g. jitted code or dlopened code).
|
||||
This causes a SIGSEV in gen_intermediate_code() and associated
|
||||
subroutines. We simply avoid caching of such blocks. */
|
||||
|
||||
if (is_valid_addr(t.tb.pc)) {
|
||||
|
||||
|
||||
mmap_lock();
|
||||
tb = tb_gen_code(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, 0);
|
||||
tb = tb_gen_code(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, t.tb.cf_mask);
|
||||
mmap_unlock();
|
||||
|
||||
} else {
|
||||
|
||||
invalid_pc = 1;
|
||||
|
||||
invalid_pc = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (t.is_chain) {
|
||||
|
||||
if (read(fd, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain))
|
||||
break;
|
||||
|
||||
@ -343,10 +362,10 @@ static void afl_wait_tsl(CPUState *cpu, int fd) {
|
||||
|
||||
last_tb = tb_htable_lookup(cpu, c.last_tb.pc, c.last_tb.cs_base,
|
||||
c.last_tb.flags, c.cf_mask);
|
||||
if (last_tb) {
|
||||
tb_add_jump(last_tb, c.tb_exit, tb);
|
||||
}
|
||||
if (last_tb) { tb_add_jump(last_tb, c.tb_exit, tb); }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -354,3 +373,4 @@ static void afl_wait_tsl(CPUState *cpu, int fd) {
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,18 @@
|
||||
/*
|
||||
american fuzzy lop - high-performance binary-only instrumentation
|
||||
-----------------------------------------------------------------
|
||||
american fuzzy lop++ - high-performance binary-only instrumentation
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Idea & design very much by Andrew Griffiths.
|
||||
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
TCG instrumentation and block chaining support by Andrea Biondo
|
||||
<andrea.biondo965@gmail.com>
|
||||
|
||||
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
||||
<andreafioraldi@gmail.com>
|
||||
|
||||
QEMU 3.1.0 port, TCG thread-safety, CompareCoverage and NeverZero
|
||||
counters by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -32,15 +31,15 @@
|
||||
|
||||
*/
|
||||
|
||||
#include "../../config.h"
|
||||
#include "afl-qemu-common.h"
|
||||
#include "tcg.h"
|
||||
#include "tcg-op.h"
|
||||
|
||||
/* Declared in afl-qemu-cpu-inl.h */
|
||||
extern unsigned char *afl_area_ptr;
|
||||
extern unsigned int afl_inst_rms;
|
||||
extern abi_ulong afl_start_code, afl_end_code;
|
||||
extern u8 afl_enable_compcov;
|
||||
extern unsigned int afl_inst_rms;
|
||||
extern abi_ulong afl_start_code, afl_end_code;
|
||||
extern u8 afl_compcov_level;
|
||||
|
||||
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc,
|
||||
TCGv_i64 arg1, TCGv_i64 arg2);
|
||||
@ -48,78 +47,93 @@ void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc,
|
||||
static void afl_compcov_log_16(target_ulong cur_loc, target_ulong arg1,
|
||||
target_ulong arg2) {
|
||||
|
||||
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
||||
afl_area_ptr[cur_loc]++;
|
||||
}
|
||||
if ((arg1 & 0xff) == (arg2 & 0xff)) { INC_AFL_AREA(cur_loc); }
|
||||
|
||||
}
|
||||
|
||||
static void afl_compcov_log_32(target_ulong cur_loc, target_ulong arg1,
|
||||
target_ulong arg2) {
|
||||
|
||||
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
||||
afl_area_ptr[cur_loc]++;
|
||||
|
||||
INC_AFL_AREA(cur_loc);
|
||||
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
|
||||
afl_area_ptr[cur_loc +1]++;
|
||||
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
|
||||
afl_area_ptr[cur_loc +2]++;
|
||||
}
|
||||
|
||||
INC_AFL_AREA(cur_loc + 1);
|
||||
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) { INC_AFL_AREA(cur_loc + 2); }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void afl_compcov_log_64(target_ulong cur_loc, target_ulong arg1,
|
||||
target_ulong arg2) {
|
||||
|
||||
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
||||
afl_area_ptr[cur_loc]++;
|
||||
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
|
||||
afl_area_ptr[cur_loc +1]++;
|
||||
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
|
||||
afl_area_ptr[cur_loc +2]++;
|
||||
if ((arg1 & 0xffffffff) == (arg2 & 0xffffffff)) {
|
||||
afl_area_ptr[cur_loc +3]++;
|
||||
if ((arg1 & 0xffffffffff) == (arg2 & 0xffffffffff)) {
|
||||
afl_area_ptr[cur_loc +4]++;
|
||||
if ((arg1 & 0xffffffffffff) == (arg2 & 0xffffffffffff)) {
|
||||
afl_area_ptr[cur_loc +5]++;
|
||||
if ((arg1 & 0xffffffffffffff) == (arg2 & 0xffffffffffffff)) {
|
||||
afl_area_ptr[cur_loc +6]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INC_AFL_AREA(cur_loc);
|
||||
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
|
||||
|
||||
INC_AFL_AREA(cur_loc + 1);
|
||||
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
|
||||
|
||||
INC_AFL_AREA(cur_loc + 2);
|
||||
if ((arg1 & 0xffffffff) == (arg2 & 0xffffffff)) {
|
||||
|
||||
INC_AFL_AREA(cur_loc + 3);
|
||||
if ((arg1 & 0xffffffffff) == (arg2 & 0xffffffffff)) {
|
||||
|
||||
INC_AFL_AREA(cur_loc + 4);
|
||||
if ((arg1 & 0xffffffffffff) == (arg2 & 0xffffffffffff)) {
|
||||
|
||||
INC_AFL_AREA(cur_loc + 5);
|
||||
if ((arg1 & 0xffffffffffffff) == (arg2 & 0xffffffffffffff)) {
|
||||
|
||||
INC_AFL_AREA(cur_loc + 6);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void afl_gen_compcov(target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2,
|
||||
TCGMemOp ot) {
|
||||
TCGMemOp ot, int is_imm) {
|
||||
|
||||
void *func;
|
||||
|
||||
if (!afl_enable_compcov || cur_loc > afl_end_code || cur_loc < afl_start_code)
|
||||
|
||||
if (!afl_compcov_level || cur_loc > afl_end_code || cur_loc < afl_start_code)
|
||||
return;
|
||||
|
||||
if (!is_imm && afl_compcov_level < 2) return;
|
||||
|
||||
switch (ot) {
|
||||
case MO_64:
|
||||
func = &afl_compcov_log_64;
|
||||
break;
|
||||
case MO_32:
|
||||
func = &afl_compcov_log_32;
|
||||
break;
|
||||
case MO_16:
|
||||
func = &afl_compcov_log_16;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
case MO_64: func = &afl_compcov_log_64; break;
|
||||
case MO_32: func = &afl_compcov_log_32; break;
|
||||
case MO_16: func = &afl_compcov_log_16; break;
|
||||
default: return;
|
||||
|
||||
}
|
||||
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 7;
|
||||
|
||||
if (cur_loc >= afl_inst_rms) return;
|
||||
|
||||
|
||||
tcg_gen_afl_compcov_log_call(func, cur_loc, arg1, arg2);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,18 @@
|
||||
/*
|
||||
american fuzzy lop - high-performance binary-only instrumentation
|
||||
-----------------------------------------------------------------
|
||||
american fuzzy lop++ - high-performance binary-only instrumentation
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Idea & design very much by Andrew Griffiths.
|
||||
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
TCG instrumentation and block chaining support by Andrea Biondo
|
||||
<andrea.biondo965@gmail.com>
|
||||
|
||||
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
||||
<andreafioraldi@gmail.com>
|
||||
QEMU 3.1.0 port, TCG thread-safety, CompareCoverage and NeverZero
|
||||
counters by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -32,275 +31,343 @@
|
||||
|
||||
*/
|
||||
|
||||
void afl_maybe_log(void* cur_loc);
|
||||
void afl_maybe_log(void *cur_loc);
|
||||
|
||||
/* Note: we convert the 64 bit args to 32 bit and do some alignment
|
||||
and endian swap. Maybe it would be better to do the alignment
|
||||
and endian swap in tcg_reg_alloc_call(). */
|
||||
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
|
||||
{
|
||||
int real_args, pi;
|
||||
unsigned sizemask, flags;
|
||||
TCGOp *op;
|
||||
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc) {
|
||||
|
||||
TCGTemp *arg = tcgv_i64_temp( tcg_const_tl(cur_loc) );
|
||||
int real_args, pi;
|
||||
unsigned sizemask, flags;
|
||||
TCGOp * op;
|
||||
|
||||
flags = 0;
|
||||
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1);
|
||||
TCGTemp *arg = tcgv_i64_temp(tcg_const_tl(cur_loc));
|
||||
|
||||
#if defined(__sparc__) && !defined(__arch64__) \
|
||||
&& !defined(CONFIG_TCG_INTERPRETER)
|
||||
/* We have 64-bit values in one register, but need to pass as two
|
||||
separate parameters. Split them. */
|
||||
int orig_sizemask = sizemask;
|
||||
TCGv_i64 retl, reth;
|
||||
TCGTemp *split_args[MAX_OPC_PARAM];
|
||||
flags = 0;
|
||||
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1);
|
||||
|
||||
retl = NULL;
|
||||
reth = NULL;
|
||||
if (sizemask != 0) {
|
||||
real_args = 0;
|
||||
int is_64bit = sizemask & (1 << 2);
|
||||
if (is_64bit) {
|
||||
TCGv_i64 orig = temp_tcgv_i64(arg);
|
||||
TCGv_i32 h = tcg_temp_new_i32();
|
||||
TCGv_i32 l = tcg_temp_new_i32();
|
||||
tcg_gen_extr_i64_i32(l, h, orig);
|
||||
split_args[real_args++] = tcgv_i32_temp(h);
|
||||
split_args[real_args++] = tcgv_i32_temp(l);
|
||||
} else {
|
||||
split_args[real_args++] = arg;
|
||||
}
|
||||
nargs = real_args;
|
||||
args = split_args;
|
||||
sizemask = 0;
|
||||
}
|
||||
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
|
||||
int is_64bit = sizemask & (1 << 2);
|
||||
int is_signed = sizemask & (2 << 2);
|
||||
if (!is_64bit) {
|
||||
TCGv_i64 temp = tcg_temp_new_i64();
|
||||
TCGv_i64 orig = temp_tcgv_i64(arg);
|
||||
if (is_signed) {
|
||||
tcg_gen_ext32s_i64(temp, orig);
|
||||
} else {
|
||||
tcg_gen_ext32u_i64(temp, orig);
|
||||
}
|
||||
arg = tcgv_i64_temp(temp);
|
||||
}
|
||||
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||
#if defined(__sparc__) && !defined(__arch64__) && \
|
||||
!defined(CONFIG_TCG_INTERPRETER)
|
||||
/* We have 64-bit values in one register, but need to pass as two
|
||||
separate parameters. Split them. */
|
||||
int orig_sizemask = sizemask;
|
||||
TCGv_i64 retl, reth;
|
||||
TCGTemp *split_args[MAX_OPC_PARAM];
|
||||
|
||||
op = tcg_emit_op(INDEX_op_call);
|
||||
|
||||
pi = 0;
|
||||
|
||||
TCGOP_CALLO(op) = 0;
|
||||
retl = NULL;
|
||||
reth = NULL;
|
||||
if (sizemask != 0) {
|
||||
|
||||
real_args = 0;
|
||||
int is_64bit = sizemask & (1 << 2);
|
||||
if (TCG_TARGET_REG_BITS < 64 && is_64bit) {
|
||||
#ifdef TCG_TARGET_CALL_ALIGN_ARGS
|
||||
/* some targets want aligned 64 bit args */
|
||||
if (real_args & 1) {
|
||||
op->args[pi++] = TCG_CALL_DUMMY_ARG;
|
||||
real_args++;
|
||||
}
|
||||
#endif
|
||||
/* If stack grows up, then we will be placing successive
|
||||
arguments at lower addresses, which means we need to
|
||||
reverse the order compared to how we would normally
|
||||
treat either big or little-endian. For those arguments
|
||||
that will wind up in registers, this still works for
|
||||
HPPA (the only current STACK_GROWSUP target) since the
|
||||
argument registers are *also* allocated in decreasing
|
||||
order. If another such target is added, this logic may
|
||||
have to get more complicated to differentiate between
|
||||
stack arguments and register arguments. */
|
||||
#if defined(HOST_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP)
|
||||
op->args[pi++] = temp_arg(arg + 1);
|
||||
op->args[pi++] = temp_arg(arg);
|
||||
#else
|
||||
op->args[pi++] = temp_arg(arg);
|
||||
op->args[pi++] = temp_arg(arg + 1);
|
||||
#endif
|
||||
real_args += 2;
|
||||
if (is_64bit) {
|
||||
|
||||
TCGv_i64 orig = temp_tcgv_i64(arg);
|
||||
TCGv_i32 h = tcg_temp_new_i32();
|
||||
TCGv_i32 l = tcg_temp_new_i32();
|
||||
tcg_gen_extr_i64_i32(l, h, orig);
|
||||
split_args[real_args++] = tcgv_i32_temp(h);
|
||||
split_args[real_args++] = tcgv_i32_temp(l);
|
||||
|
||||
} else {
|
||||
|
||||
split_args[real_args++] = arg;
|
||||
|
||||
}
|
||||
|
||||
nargs = real_args;
|
||||
args = split_args;
|
||||
sizemask = 0;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
|
||||
int is_64bit = sizemask & (1 << 2);
|
||||
int is_signed = sizemask & (2 << 2);
|
||||
if (!is_64bit) {
|
||||
|
||||
TCGv_i64 temp = tcg_temp_new_i64();
|
||||
TCGv_i64 orig = temp_tcgv_i64(arg);
|
||||
if (is_signed) {
|
||||
|
||||
tcg_gen_ext32s_i64(temp, orig);
|
||||
|
||||
} else {
|
||||
|
||||
tcg_gen_ext32u_i64(temp, orig);
|
||||
|
||||
}
|
||||
|
||||
arg = tcgv_i64_temp(temp);
|
||||
|
||||
}
|
||||
|
||||
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||
|
||||
op = tcg_emit_op(INDEX_op_call);
|
||||
|
||||
pi = 0;
|
||||
|
||||
TCGOP_CALLO(op) = 0;
|
||||
|
||||
real_args = 0;
|
||||
int is_64bit = sizemask & (1 << 2);
|
||||
if (TCG_TARGET_REG_BITS < 64 && is_64bit) {
|
||||
|
||||
#ifdef TCG_TARGET_CALL_ALIGN_ARGS
|
||||
/* some targets want aligned 64 bit args */
|
||||
if (real_args & 1) {
|
||||
|
||||
op->args[pi++] = TCG_CALL_DUMMY_ARG;
|
||||
real_args++;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
/* If stack grows up, then we will be placing successive
|
||||
arguments at lower addresses, which means we need to
|
||||
reverse the order compared to how we would normally
|
||||
treat either big or little-endian. For those arguments
|
||||
that will wind up in registers, this still works for
|
||||
HPPA (the only current STACK_GROWSUP target) since the
|
||||
argument registers are *also* allocated in decreasing
|
||||
order. If another such target is added, this logic may
|
||||
have to get more complicated to differentiate between
|
||||
stack arguments and register arguments. */
|
||||
#if defined(HOST_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP)
|
||||
op->args[pi++] = temp_arg(arg + 1);
|
||||
op->args[pi++] = temp_arg(arg);
|
||||
#else
|
||||
op->args[pi++] = temp_arg(arg);
|
||||
op->args[pi++] = temp_arg(arg + 1);
|
||||
#endif
|
||||
real_args += 2;
|
||||
|
||||
}
|
||||
|
||||
op->args[pi++] = temp_arg(arg);
|
||||
real_args++;
|
||||
|
||||
op->args[pi++] = (uintptr_t)&afl_maybe_log;
|
||||
op->args[pi++] = flags;
|
||||
TCGOP_CALLI(op) = real_args;
|
||||
|
||||
/* Make sure the fields didn't overflow. */
|
||||
tcg_debug_assert(TCGOP_CALLI(op) == real_args);
|
||||
tcg_debug_assert(pi <= ARRAY_SIZE(op->args));
|
||||
|
||||
#if defined(__sparc__) && !defined(__arch64__) && \
|
||||
!defined(CONFIG_TCG_INTERPRETER)
|
||||
/* Free all of the parts we allocated above. */
|
||||
real_args = 0;
|
||||
int is_64bit = orig_sizemask & (1 << 2);
|
||||
if (is_64bit) {
|
||||
|
||||
tcg_temp_free_internal(args[real_args++]);
|
||||
tcg_temp_free_internal(args[real_args++]);
|
||||
|
||||
} else {
|
||||
|
||||
real_args++;
|
||||
|
||||
op->args[pi++] = (uintptr_t)&afl_maybe_log;
|
||||
op->args[pi++] = flags;
|
||||
TCGOP_CALLI(op) = real_args;
|
||||
}
|
||||
|
||||
/* Make sure the fields didn't overflow. */
|
||||
tcg_debug_assert(TCGOP_CALLI(op) == real_args);
|
||||
tcg_debug_assert(pi <= ARRAY_SIZE(op->args));
|
||||
if (orig_sizemask & 1) {
|
||||
|
||||
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
|
||||
Note that describing these as TCGv_i64 eliminates an unnecessary
|
||||
zero-extension that tcg_gen_concat_i32_i64 would create. */
|
||||
tcg_gen_concat32_i64(temp_tcgv_i64(NULL), retl, reth);
|
||||
tcg_temp_free_i64(retl);
|
||||
tcg_temp_free_i64(reth);
|
||||
|
||||
}
|
||||
|
||||
#if defined(__sparc__) && !defined(__arch64__) \
|
||||
&& !defined(CONFIG_TCG_INTERPRETER)
|
||||
/* Free all of the parts we allocated above. */
|
||||
real_args = 0;
|
||||
int is_64bit = orig_sizemask & (1 << 2);
|
||||
if (is_64bit) {
|
||||
tcg_temp_free_internal(args[real_args++]);
|
||||
tcg_temp_free_internal(args[real_args++]);
|
||||
} else {
|
||||
real_args++;
|
||||
}
|
||||
if (orig_sizemask & 1) {
|
||||
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
|
||||
Note that describing these as TCGv_i64 eliminates an unnecessary
|
||||
zero-extension that tcg_gen_concat_i32_i64 would create. */
|
||||
tcg_gen_concat32_i64(temp_tcgv_i64(NULL), retl, reth);
|
||||
tcg_temp_free_i64(retl);
|
||||
tcg_temp_free_i64(reth);
|
||||
}
|
||||
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
|
||||
int is_64bit = sizemask & (1 << 2);
|
||||
int is_64bit = sizemask & (1 << 2);
|
||||
if (!is_64bit) { tcg_temp_free_internal(arg); }
|
||||
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||
|
||||
}
|
||||
|
||||
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc,
|
||||
TCGv_i64 arg1, TCGv_i64 arg2) {
|
||||
|
||||
int i, real_args, nb_rets, pi;
|
||||
unsigned sizemask, flags;
|
||||
TCGOp * op;
|
||||
|
||||
const int nargs = 3;
|
||||
TCGTemp *args[3] = {tcgv_i64_temp(tcg_const_tl(cur_loc)), tcgv_i64_temp(arg1),
|
||||
tcgv_i64_temp(arg2)};
|
||||
|
||||
flags = 0;
|
||||
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1) | dh_sizemask(i64, 2) |
|
||||
dh_sizemask(i64, 3);
|
||||
|
||||
#if defined(__sparc__) && !defined(__arch64__) && \
|
||||
!defined(CONFIG_TCG_INTERPRETER)
|
||||
/* We have 64-bit values in one register, but need to pass as two
|
||||
separate parameters. Split them. */
|
||||
int orig_sizemask = sizemask;
|
||||
int orig_nargs = nargs;
|
||||
TCGv_i64 retl, reth;
|
||||
TCGTemp *split_args[MAX_OPC_PARAM];
|
||||
|
||||
retl = NULL;
|
||||
reth = NULL;
|
||||
if (sizemask != 0) {
|
||||
|
||||
for (i = real_args = 0; i < nargs; ++i) {
|
||||
|
||||
int is_64bit = sizemask & (1 << (i + 1) * 2);
|
||||
if (is_64bit) {
|
||||
|
||||
TCGv_i64 orig = temp_tcgv_i64(args[i]);
|
||||
TCGv_i32 h = tcg_temp_new_i32();
|
||||
TCGv_i32 l = tcg_temp_new_i32();
|
||||
tcg_gen_extr_i64_i32(l, h, orig);
|
||||
split_args[real_args++] = tcgv_i32_temp(h);
|
||||
split_args[real_args++] = tcgv_i32_temp(l);
|
||||
|
||||
} else {
|
||||
|
||||
split_args[real_args++] = args[i];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
nargs = real_args;
|
||||
args = split_args;
|
||||
sizemask = 0;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
|
||||
for (i = 0; i < nargs; ++i) {
|
||||
|
||||
int is_64bit = sizemask & (1 << (i + 1) * 2);
|
||||
int is_signed = sizemask & (2 << (i + 1) * 2);
|
||||
if (!is_64bit) {
|
||||
tcg_temp_free_internal(arg);
|
||||
|
||||
TCGv_i64 temp = tcg_temp_new_i64();
|
||||
TCGv_i64 orig = temp_tcgv_i64(args[i]);
|
||||
if (is_signed) {
|
||||
|
||||
tcg_gen_ext32s_i64(temp, orig);
|
||||
|
||||
} else {
|
||||
|
||||
tcg_gen_ext32u_i64(temp, orig);
|
||||
|
||||
}
|
||||
|
||||
args[i] = tcgv_i64_temp(temp);
|
||||
|
||||
}
|
||||
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||
}
|
||||
|
||||
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2)
|
||||
{
|
||||
int i, real_args, nb_rets, pi;
|
||||
unsigned sizemask, flags;
|
||||
TCGOp *op;
|
||||
}
|
||||
|
||||
const int nargs = 3;
|
||||
TCGTemp *args[3] = { tcgv_i64_temp( tcg_const_tl(cur_loc) ),
|
||||
tcgv_i64_temp(arg1),
|
||||
tcgv_i64_temp(arg2) };
|
||||
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||
|
||||
flags = 0;
|
||||
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1) |
|
||||
dh_sizemask(i64, 2) | dh_sizemask(i64, 3);
|
||||
op = tcg_emit_op(INDEX_op_call);
|
||||
|
||||
#if defined(__sparc__) && !defined(__arch64__) \
|
||||
&& !defined(CONFIG_TCG_INTERPRETER)
|
||||
/* We have 64-bit values in one register, but need to pass as two
|
||||
separate parameters. Split them. */
|
||||
int orig_sizemask = sizemask;
|
||||
int orig_nargs = nargs;
|
||||
TCGv_i64 retl, reth;
|
||||
TCGTemp *split_args[MAX_OPC_PARAM];
|
||||
pi = 0;
|
||||
nb_rets = 0;
|
||||
TCGOP_CALLO(op) = nb_rets;
|
||||
|
||||
retl = NULL;
|
||||
reth = NULL;
|
||||
if (sizemask != 0) {
|
||||
for (i = real_args = 0; i < nargs; ++i) {
|
||||
int is_64bit = sizemask & (1 << (i+1)*2);
|
||||
if (is_64bit) {
|
||||
TCGv_i64 orig = temp_tcgv_i64(args[i]);
|
||||
TCGv_i32 h = tcg_temp_new_i32();
|
||||
TCGv_i32 l = tcg_temp_new_i32();
|
||||
tcg_gen_extr_i64_i32(l, h, orig);
|
||||
split_args[real_args++] = tcgv_i32_temp(h);
|
||||
split_args[real_args++] = tcgv_i32_temp(l);
|
||||
} else {
|
||||
split_args[real_args++] = args[i];
|
||||
}
|
||||
}
|
||||
nargs = real_args;
|
||||
args = split_args;
|
||||
sizemask = 0;
|
||||
}
|
||||
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
|
||||
for (i = 0; i < nargs; ++i) {
|
||||
int is_64bit = sizemask & (1 << (i+1)*2);
|
||||
int is_signed = sizemask & (2 << (i+1)*2);
|
||||
if (!is_64bit) {
|
||||
TCGv_i64 temp = tcg_temp_new_i64();
|
||||
TCGv_i64 orig = temp_tcgv_i64(args[i]);
|
||||
if (is_signed) {
|
||||
tcg_gen_ext32s_i64(temp, orig);
|
||||
} else {
|
||||
tcg_gen_ext32u_i64(temp, orig);
|
||||
}
|
||||
args[i] = tcgv_i64_temp(temp);
|
||||
}
|
||||
}
|
||||
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||
real_args = 0;
|
||||
for (i = 0; i < nargs; i++) {
|
||||
|
||||
op = tcg_emit_op(INDEX_op_call);
|
||||
int is_64bit = sizemask & (1 << (i + 1) * 2);
|
||||
if (TCG_TARGET_REG_BITS < 64 && is_64bit) {
|
||||
|
||||
pi = 0;
|
||||
nb_rets = 0;
|
||||
TCGOP_CALLO(op) = nb_rets;
|
||||
|
||||
real_args = 0;
|
||||
for (i = 0; i < nargs; i++) {
|
||||
int is_64bit = sizemask & (1 << (i+1)*2);
|
||||
if (TCG_TARGET_REG_BITS < 64 && is_64bit) {
|
||||
#ifdef TCG_TARGET_CALL_ALIGN_ARGS
|
||||
/* some targets want aligned 64 bit args */
|
||||
if (real_args & 1) {
|
||||
op->args[pi++] = TCG_CALL_DUMMY_ARG;
|
||||
real_args++;
|
||||
}
|
||||
#endif
|
||||
/* If stack grows up, then we will be placing successive
|
||||
arguments at lower addresses, which means we need to
|
||||
reverse the order compared to how we would normally
|
||||
treat either big or little-endian. For those arguments
|
||||
that will wind up in registers, this still works for
|
||||
HPPA (the only current STACK_GROWSUP target) since the
|
||||
argument registers are *also* allocated in decreasing
|
||||
order. If another such target is added, this logic may
|
||||
have to get more complicated to differentiate between
|
||||
stack arguments and register arguments. */
|
||||
#if defined(HOST_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP)
|
||||
op->args[pi++] = temp_arg(args[i] + 1);
|
||||
op->args[pi++] = temp_arg(args[i]);
|
||||
#else
|
||||
op->args[pi++] = temp_arg(args[i]);
|
||||
op->args[pi++] = temp_arg(args[i] + 1);
|
||||
#endif
|
||||
real_args += 2;
|
||||
continue;
|
||||
}
|
||||
/* some targets want aligned 64 bit args */
|
||||
if (real_args & 1) {
|
||||
|
||||
op->args[pi++] = temp_arg(args[i]);
|
||||
op->args[pi++] = TCG_CALL_DUMMY_ARG;
|
||||
real_args++;
|
||||
}
|
||||
op->args[pi++] = (uintptr_t)func;
|
||||
op->args[pi++] = flags;
|
||||
TCGOP_CALLI(op) = real_args;
|
||||
|
||||
/* Make sure the fields didn't overflow. */
|
||||
tcg_debug_assert(TCGOP_CALLI(op) == real_args);
|
||||
tcg_debug_assert(pi <= ARRAY_SIZE(op->args));
|
||||
}
|
||||
|
||||
#endif
|
||||
/* If stack grows up, then we will be placing successive
|
||||
arguments at lower addresses, which means we need to
|
||||
reverse the order compared to how we would normally
|
||||
treat either big or little-endian. For those arguments
|
||||
that will wind up in registers, this still works for
|
||||
HPPA (the only current STACK_GROWSUP target) since the
|
||||
argument registers are *also* allocated in decreasing
|
||||
order. If another such target is added, this logic may
|
||||
have to get more complicated to differentiate between
|
||||
stack arguments and register arguments. */
|
||||
#if defined(HOST_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP)
|
||||
op->args[pi++] = temp_arg(args[i] + 1);
|
||||
op->args[pi++] = temp_arg(args[i]);
|
||||
#else
|
||||
op->args[pi++] = temp_arg(args[i]);
|
||||
op->args[pi++] = temp_arg(args[i] + 1);
|
||||
#endif
|
||||
real_args += 2;
|
||||
continue;
|
||||
|
||||
#if defined(__sparc__) && !defined(__arch64__) \
|
||||
&& !defined(CONFIG_TCG_INTERPRETER)
|
||||
/* Free all of the parts we allocated above. */
|
||||
for (i = real_args = 0; i < orig_nargs; ++i) {
|
||||
int is_64bit = orig_sizemask & (1 << (i+1)*2);
|
||||
if (is_64bit) {
|
||||
tcg_temp_free_internal(args[real_args++]);
|
||||
tcg_temp_free_internal(args[real_args++]);
|
||||
} else {
|
||||
real_args++;
|
||||
}
|
||||
}
|
||||
if (orig_sizemask & 1) {
|
||||
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
|
||||
Note that describing these as TCGv_i64 eliminates an unnecessary
|
||||
zero-extension that tcg_gen_concat_i32_i64 would create. */
|
||||
tcg_gen_concat32_i64(temp_tcgv_i64(NULL), retl, reth);
|
||||
tcg_temp_free_i64(retl);
|
||||
tcg_temp_free_i64(reth);
|
||||
|
||||
op->args[pi++] = temp_arg(args[i]);
|
||||
real_args++;
|
||||
|
||||
}
|
||||
|
||||
op->args[pi++] = (uintptr_t)func;
|
||||
op->args[pi++] = flags;
|
||||
TCGOP_CALLI(op) = real_args;
|
||||
|
||||
/* Make sure the fields didn't overflow. */
|
||||
tcg_debug_assert(TCGOP_CALLI(op) == real_args);
|
||||
tcg_debug_assert(pi <= ARRAY_SIZE(op->args));
|
||||
|
||||
#if defined(__sparc__) && !defined(__arch64__) && \
|
||||
!defined(CONFIG_TCG_INTERPRETER)
|
||||
/* Free all of the parts we allocated above. */
|
||||
for (i = real_args = 0; i < orig_nargs; ++i) {
|
||||
|
||||
int is_64bit = orig_sizemask & (1 << (i + 1) * 2);
|
||||
if (is_64bit) {
|
||||
|
||||
tcg_temp_free_internal(args[real_args++]);
|
||||
tcg_temp_free_internal(args[real_args++]);
|
||||
|
||||
} else {
|
||||
|
||||
real_args++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (orig_sizemask & 1) {
|
||||
|
||||
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
|
||||
Note that describing these as TCGv_i64 eliminates an unnecessary
|
||||
zero-extension that tcg_gen_concat_i32_i64 would create. */
|
||||
tcg_gen_concat32_i64(temp_tcgv_i64(NULL), retl, reth);
|
||||
tcg_temp_free_i64(retl);
|
||||
tcg_temp_free_i64(reth);
|
||||
|
||||
}
|
||||
|
||||
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
|
||||
for (i = 0; i < nargs; ++i) {
|
||||
int is_64bit = sizemask & (1 << (i+1)*2);
|
||||
if (!is_64bit) {
|
||||
tcg_temp_free_internal(args[i]);
|
||||
}
|
||||
}
|
||||
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||
for (i = 0; i < nargs; ++i) {
|
||||
|
||||
int is_64bit = sizemask & (1 << (i + 1) * 2);
|
||||
if (!is_64bit) { tcg_temp_free_internal(args[i]); }
|
||||
|
||||
}
|
||||
|
||||
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,18 @@
|
||||
/*
|
||||
american fuzzy lop - high-performance binary-only instrumentation
|
||||
-----------------------------------------------------------------
|
||||
american fuzzy lop++ - high-performance binary-only instrumentation
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Idea & design very much by Andrew Griffiths.
|
||||
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
TCG instrumentation and block chaining support by Andrea Biondo
|
||||
<andrea.biondo965@gmail.com>
|
||||
|
||||
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
||||
<andreafioraldi@gmail.com>
|
||||
QEMU 3.1.0 port, TCG thread-safety, CompareCoverage and NeverZero
|
||||
counters by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -32,21 +31,24 @@
|
||||
|
||||
*/
|
||||
|
||||
#include "../../config.h"
|
||||
#include "afl-qemu-common.h"
|
||||
#include "tcg-op.h"
|
||||
|
||||
/* Declared in afl-qemu-cpu-inl.h */
|
||||
extern unsigned char *afl_area_ptr;
|
||||
extern unsigned int afl_inst_rms;
|
||||
extern abi_ulong afl_start_code, afl_end_code;
|
||||
extern unsigned int afl_inst_rms;
|
||||
extern abi_ulong afl_start_code, afl_end_code;
|
||||
|
||||
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc);
|
||||
|
||||
void afl_maybe_log(target_ulong cur_loc) {
|
||||
void afl_maybe_log(target_ulong cur_loc) {
|
||||
|
||||
static __thread abi_ulong prev_loc;
|
||||
|
||||
afl_area_ptr[cur_loc ^ prev_loc]++;
|
||||
register uintptr_t afl_idx = cur_loc ^ prev_loc;
|
||||
|
||||
INC_AFL_AREA(afl_idx);
|
||||
|
||||
prev_loc = cur_loc >> 1;
|
||||
|
||||
}
|
||||
@ -57,14 +59,16 @@ static void afl_gen_trace(target_ulong cur_loc) {
|
||||
/* Optimize for cur_loc > afl_end_code, which is the most likely case on
|
||||
Linux systems. */
|
||||
|
||||
if (cur_loc > afl_end_code || cur_loc < afl_start_code /*|| !afl_area_ptr*/) // not needed because of static dummy buffer
|
||||
if (cur_loc > afl_end_code ||
|
||||
cur_loc < afl_start_code /*|| !afl_area_ptr*/) // not needed because of
|
||||
// static dummy buffer
|
||||
return;
|
||||
|
||||
/* Looks like QEMU always maps to fixed locations, so ASLR is not a
|
||||
concern. Phew. But instruction addresses may be aligned. Let's mangle
|
||||
the value to get something quasi-uniform. */
|
||||
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
/* Implement probabilistic instrumentation by looking at scrambled block
|
||||
@ -73,5 +77,6 @@ static void afl_gen_trace(target_ulong cur_loc) {
|
||||
if (cur_loc >= afl_inst_rms) return;
|
||||
|
||||
tcg_gen_afl_maybe_log_call(cur_loc);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -15,11 +15,11 @@ index 0dd5fbe4..b95d341e 100644
|
||||
tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0,
|
||||
s1->mem_index, ot | MO_LE);
|
||||
tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1);
|
||||
+ afl_gen_compcov(s1->pc, s1->cc_srcT, s1->T1, ot);
|
||||
+ afl_gen_compcov(s1->pc, s1->cc_srcT, s1->T1, ot, d == OR_EAX);
|
||||
} else {
|
||||
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
|
||||
tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1);
|
||||
+ afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot);
|
||||
+ afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot, d == OR_EAX);
|
||||
gen_op_st_rm_T0_A0(s1, ot, d);
|
||||
}
|
||||
gen_op_update2_cc(s1);
|
||||
@ -27,7 +27,7 @@ index 0dd5fbe4..b95d341e 100644
|
||||
tcg_gen_mov_tl(cpu_cc_src, s1->T1);
|
||||
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
|
||||
tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1);
|
||||
+ afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot);
|
||||
+ afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot, d == OR_EAX);
|
||||
set_cc_op(s1, CC_OP_SUBB + ot);
|
||||
break;
|
||||
}
|
||||
|
@ -2,9 +2,10 @@ diff --git a/linux-user/syscall.c b/linux-user/syscall.c
|
||||
index 280137da..8c0e749f 100644
|
||||
--- a/linux-user/syscall.c
|
||||
+++ b/linux-user/syscall.c
|
||||
@@ -112,6 +112,8 @@
|
||||
@@ -112,6 +112,9 @@
|
||||
#include "qemu.h"
|
||||
#include "fd-trans.h"
|
||||
+#include <linux/sockios.h>
|
||||
|
||||
+extern unsigned int afl_forksrv_pid;
|
||||
+
|
||||
@ -32,4 +33,4 @@ index 280137da..8c0e749f 100644
|
||||
+ }
|
||||
|
||||
#ifdef TARGET_NR_set_robust_list
|
||||
case TARGET_NR_set_robust_list:
|
||||
case TARGET_NR_set_robust_list:
|
@ -1,6 +0,0 @@
|
||||
#ifndef __SHAREDMEM_H
|
||||
#define __SHAREDMEM_H
|
||||
|
||||
void setup_shm(unsigned char dumb_mode);
|
||||
void remove_shm(void);
|
||||
#endif
|
22
src/README.src
Normal file
22
src/README.src
Normal file
@ -0,0 +1,22 @@
|
||||
Quick explanation about the files here:
|
||||
|
||||
afl-analyze.c - afl-analyze binary tool
|
||||
afl-as.c - afl-as binary tool
|
||||
afl-gotcpu.c - afl-gotcpu binary tool
|
||||
afl-showmap.c - afl-showmap binary tool
|
||||
afl-tmin.c - afl-tmin binary tool
|
||||
afl-fuzz.c - afl-fuzz binary tool (just main() and usage())
|
||||
afl-fuzz-bitmap.c - afl-fuzz bitmap handling
|
||||
afl-fuzz-extras.c - afl-fuzz the *extra* function calls
|
||||
afl-fuzz-globals.c - afl-fuzz global variables
|
||||
afl-fuzz-init.c - afl-fuzz initialization
|
||||
afl-fuzz-misc.c - afl-fuzz misc functions
|
||||
afl-fuzz-one.c - afl-fuzz fuzzer_one big loop, this is where the mutation is happening
|
||||
afl-fuzz-python.c - afl-fuzz the python mutator extension
|
||||
afl-fuzz-queue.c - afl-fuzz handling the queue
|
||||
afl-fuzz-run.c - afl-fuzz running the target
|
||||
afl-fuzz-stats.c - afl-fuzz writing the statistics file
|
||||
afl-gcc.c - afl-gcc binary tool (deprecated)
|
||||
afl-common.c - common functions, used by afl-analyze, afl-fuzz, afl-showmap and afl-tmin
|
||||
afl-forkserver.c - forkserver implementation, used by afl-fuzz and afl-tmin
|
||||
afl-sharedmem.c - sharedmem implementation, used by afl-fuzz and afl-tmin
|
@ -1,10 +1,15 @@
|
||||
/*
|
||||
american fuzzy lop - file format analyzer
|
||||
-----------------------------------------
|
||||
american fuzzy lop++ - file format analyzer
|
||||
-------------------------------------------
|
||||
|
||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -21,13 +26,16 @@
|
||||
|
||||
#define AFL_MAIN
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "alloc-inl.h"
|
||||
#include "hash.h"
|
||||
#include "sharedmem.h"
|
||||
#include "afl-common.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
@ -47,61 +55,59 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
static s32 child_pid; /* PID of the tested program */
|
||||
static s32 child_pid; /* PID of the tested program */
|
||||
|
||||
u8* trace_bits; /* SHM with instrumentation bitmap */
|
||||
u8* trace_bits; /* SHM with instrumentation bitmap */
|
||||
|
||||
static u8 *in_file, /* Analyzer input test case */
|
||||
*prog_in, /* Targeted program input file */
|
||||
*target_path, /* Path to target binary */
|
||||
*doc_path; /* Path to docs */
|
||||
static u8 *in_file, /* Analyzer input test case */
|
||||
*prog_in, /* Targeted program input file */
|
||||
*target_path, /* Path to target binary */
|
||||
*doc_path; /* Path to docs */
|
||||
|
||||
static u8 *in_data; /* Input data for analysis */
|
||||
static u8* in_data; /* Input data for analysis */
|
||||
|
||||
static u32 in_len, /* Input data length */
|
||||
orig_cksum, /* Original checksum */
|
||||
total_execs, /* Total number of execs */
|
||||
exec_hangs, /* Total number of hangs */
|
||||
exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */
|
||||
static u32 in_len, /* Input data length */
|
||||
orig_cksum, /* Original checksum */
|
||||
total_execs, /* Total number of execs */
|
||||
exec_hangs, /* Total number of hangs */
|
||||
exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */
|
||||
|
||||
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
||||
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
||||
|
||||
static s32 dev_null_fd = -1; /* FD to /dev/null */
|
||||
static s32 dev_null_fd = -1; /* FD to /dev/null */
|
||||
|
||||
static u8 edges_only, /* Ignore hit counts? */
|
||||
use_hex_offsets, /* Show hex offsets? */
|
||||
use_stdin = 1; /* Use stdin for program input? */
|
||||
|
||||
static volatile u8
|
||||
stop_soon, /* Ctrl-C pressed? */
|
||||
child_timed_out; /* Child timed out? */
|
||||
static u8 edges_only, /* Ignore hit counts? */
|
||||
use_hex_offsets, /* Show hex offsets? */
|
||||
use_stdin = 1; /* Use stdin for program input? */
|
||||
|
||||
static volatile u8 stop_soon, /* Ctrl-C pressed? */
|
||||
child_timed_out; /* Child timed out? */
|
||||
|
||||
/* Constants used for describing byte behavior. */
|
||||
|
||||
#define RESP_NONE 0x00 /* Changing byte is a no-op. */
|
||||
#define RESP_MINOR 0x01 /* Some changes have no effect. */
|
||||
#define RESP_VARIABLE 0x02 /* Changes produce variable paths. */
|
||||
#define RESP_FIXED 0x03 /* Changes produce fixed patterns. */
|
||||
#define RESP_NONE 0x00 /* Changing byte is a no-op. */
|
||||
#define RESP_MINOR 0x01 /* Some changes have no effect. */
|
||||
#define RESP_VARIABLE 0x02 /* Changes produce variable paths. */
|
||||
#define RESP_FIXED 0x03 /* Changes produce fixed patterns. */
|
||||
|
||||
#define RESP_LEN 0x04 /* Potential length field */
|
||||
#define RESP_CKSUM 0x05 /* Potential checksum */
|
||||
#define RESP_SUSPECT 0x06 /* Potential "suspect" blob */
|
||||
#define RESP_LEN 0x04 /* Potential length field */
|
||||
#define RESP_CKSUM 0x05 /* Potential checksum */
|
||||
#define RESP_SUSPECT 0x06 /* Potential "suspect" blob */
|
||||
|
||||
|
||||
/* Classify tuple counts. This is a slow & naive version, but good enough here. */
|
||||
/* Classify tuple counts. This is a slow & naive version, but good enough here.
|
||||
*/
|
||||
|
||||
static u8 count_class_lookup[256] = {
|
||||
|
||||
[0] = 0,
|
||||
[1] = 1,
|
||||
[2] = 2,
|
||||
[3] = 4,
|
||||
[4 ... 7] = 8,
|
||||
[8 ... 15] = 16,
|
||||
[16 ... 31] = 32,
|
||||
[32 ... 127] = 64,
|
||||
[128 ... 255] = 128
|
||||
[0] = 0,
|
||||
[1] = 1,
|
||||
[2] = 2,
|
||||
[3] = 4,
|
||||
[4 ... 7] = 8,
|
||||
[8 ... 15] = 16,
|
||||
[16 ... 31] = 32,
|
||||
[32 ... 127] = 64,
|
||||
[128 ... 255] = 128
|
||||
|
||||
};
|
||||
|
||||
@ -112,61 +118,62 @@ static void classify_counts(u8* mem) {
|
||||
if (edges_only) {
|
||||
|
||||
while (i--) {
|
||||
|
||||
if (*mem) *mem = 1;
|
||||
mem++;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
while (i--) {
|
||||
|
||||
*mem = count_class_lookup[*mem];
|
||||
mem++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* See if any bytes are set in the bitmap. */
|
||||
|
||||
static inline u8 anything_set(void) {
|
||||
|
||||
u32* ptr = (u32*)trace_bits;
|
||||
u32 i = (MAP_SIZE >> 2);
|
||||
u32 i = (MAP_SIZE >> 2);
|
||||
|
||||
while (i--) if (*(ptr++)) return 1;
|
||||
while (i--)
|
||||
if (*(ptr++)) return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Get rid of temp files (atexit handler). */
|
||||
|
||||
static void at_exit_handler(void) {
|
||||
|
||||
unlink(prog_in); /* Ignore errors */
|
||||
unlink(prog_in); /* Ignore errors */
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Read initial file. */
|
||||
|
||||
static void read_initial_file(void) {
|
||||
|
||||
struct stat st;
|
||||
s32 fd = open(in_file, O_RDONLY);
|
||||
s32 fd = open(in_file, O_RDONLY);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to open '%s'", in_file);
|
||||
|
||||
if (fstat(fd, &st) || !st.st_size)
|
||||
FATAL("Zero-sized input file.");
|
||||
if (fstat(fd, &st) || !st.st_size) FATAL("Zero-sized input file.");
|
||||
|
||||
if (st.st_size >= TMIN_MAX_FILE)
|
||||
FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024);
|
||||
|
||||
in_len = st.st_size;
|
||||
in_len = st.st_size;
|
||||
in_data = ck_alloc_nozero(in_len);
|
||||
|
||||
ck_read(fd, in_data, in_len, in_file);
|
||||
@ -177,14 +184,13 @@ static void read_initial_file(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Write output file. */
|
||||
|
||||
static s32 write_to_file(u8* path, u8* mem, u32 len) {
|
||||
|
||||
s32 ret;
|
||||
|
||||
unlink(path); /* Ignore errors */
|
||||
unlink(path); /* Ignore errors */
|
||||
|
||||
ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
|
||||
@ -198,7 +204,6 @@ static s32 write_to_file(u8* path, u8* mem, u32 len) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Handle timeout signal. */
|
||||
|
||||
static void handle_timeout(int sig) {
|
||||
@ -208,14 +213,13 @@ static void handle_timeout(int sig) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Execute target application. Returns exec checksum, or 0 if program
|
||||
times out. */
|
||||
|
||||
static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
||||
|
||||
static struct itimerval it;
|
||||
int status = 0;
|
||||
int status = 0;
|
||||
|
||||
s32 prog_in_fd;
|
||||
u32 cksum;
|
||||
@ -234,8 +238,7 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
||||
struct rlimit r;
|
||||
|
||||
if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 ||
|
||||
dup2(dev_null_fd, 1) < 0 ||
|
||||
dup2(dev_null_fd, 2) < 0) {
|
||||
dup2(dev_null_fd, 1) < 0 || dup2(dev_null_fd, 2) < 0) {
|
||||
|
||||
*(u32*)trace_bits = EXEC_FAIL_SIG;
|
||||
PFATAL("dup2() failed");
|
||||
@ -251,18 +254,18 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
||||
|
||||
#ifdef RLIMIT_AS
|
||||
|
||||
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
||||
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
||||
|
||||
#else
|
||||
|
||||
setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
|
||||
setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
|
||||
|
||||
#endif /* ^RLIMIT_AS */
|
||||
#endif /* ^RLIMIT_AS */
|
||||
|
||||
}
|
||||
|
||||
r.rlim_max = r.rlim_cur = 0;
|
||||
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
|
||||
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
|
||||
|
||||
execv(target_path, argv);
|
||||
|
||||
@ -300,8 +303,10 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
||||
total_execs++;
|
||||
|
||||
if (stop_soon) {
|
||||
|
||||
SAYF(cRST cLRD "\n+++ Analysis aborted by user +++\n" cRST);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* Always discard inputs that time out. */
|
||||
@ -332,7 +337,6 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_COLOR
|
||||
|
||||
/* Helper function to display a human-readable character. */
|
||||
@ -350,23 +354,24 @@ static void show_char(u8 val) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Show the legend */
|
||||
|
||||
static void show_legend(void) {
|
||||
|
||||
SAYF(" " cLGR bgGRA " 01 " cRST " - no-op block "
|
||||
cBLK bgLGN " 01 " cRST " - suspected length field\n"
|
||||
" " cBRI bgGRA " 01 " cRST " - superficial content "
|
||||
cBLK bgYEL " 01 " cRST " - suspected cksum or magic int\n"
|
||||
" " cBLK bgCYA " 01 " cRST " - critical stream "
|
||||
cBLK bgLRD " 01 " cRST " - suspected checksummed block\n"
|
||||
SAYF(" " cLGR bgGRA " 01 " cRST " - no-op block " cBLK bgLGN
|
||||
" 01 " cRST
|
||||
" - suspected length field\n"
|
||||
" " cBRI bgGRA " 01 " cRST " - superficial content " cBLK bgYEL
|
||||
" 01 " cRST
|
||||
" - suspected cksum or magic int\n"
|
||||
" " cBLK bgCYA " 01 " cRST " - critical stream " cBLK bgLRD
|
||||
" 01 " cRST
|
||||
" - suspected checksummed block\n"
|
||||
" " cBLK bgMGN " 01 " cRST " - \"magic value\" section\n\n");
|
||||
|
||||
}
|
||||
|
||||
#endif /* USE_COLOR */
|
||||
|
||||
#endif /* USE_COLOR */
|
||||
|
||||
/* Interpret and report a pattern in the input file. */
|
||||
|
||||
@ -380,9 +385,9 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) {
|
||||
u32 rlen = 1, off;
|
||||
#else
|
||||
u32 rlen = 1;
|
||||
#endif /* ^USE_COLOR */
|
||||
#endif /* ^USE_COLOR */
|
||||
|
||||
u8 rtype = b_data[i] & 0x0f;
|
||||
u8 rtype = b_data[i] & 0x0f;
|
||||
|
||||
/* Look ahead to determine the length of run. */
|
||||
|
||||
@ -401,51 +406,61 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) {
|
||||
|
||||
case 2: {
|
||||
|
||||
u16 val = *(u16*)(in_data + i);
|
||||
u16 val = *(u16*)(in_data + i);
|
||||
|
||||
/* Small integers may be length fields. */
|
||||
/* Small integers may be length fields. */
|
||||
|
||||
if (val && (val <= in_len || SWAP16(val) <= in_len)) {
|
||||
rtype = RESP_LEN;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Uniform integers may be checksums. */
|
||||
|
||||
if (val && abs(in_data[i] - in_data[i + 1]) > 32) {
|
||||
rtype = RESP_CKSUM;
|
||||
break;
|
||||
}
|
||||
if (val && (val <= in_len || SWAP16(val) <= in_len)) {
|
||||
|
||||
rtype = RESP_LEN;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
/* Uniform integers may be checksums. */
|
||||
|
||||
if (val && abs(in_data[i] - in_data[i + 1]) > 32) {
|
||||
|
||||
rtype = RESP_CKSUM;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 4: {
|
||||
|
||||
u32 val = *(u32*)(in_data + i);
|
||||
u32 val = *(u32*)(in_data + i);
|
||||
|
||||
/* Small integers may be length fields. */
|
||||
/* Small integers may be length fields. */
|
||||
|
||||
if (val && (val <= in_len || SWAP32(val) <= in_len)) {
|
||||
rtype = RESP_LEN;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Uniform integers may be checksums. */
|
||||
|
||||
if (val && (in_data[i] >> 7 != in_data[i + 1] >> 7 ||
|
||||
in_data[i] >> 7 != in_data[i + 2] >> 7 ||
|
||||
in_data[i] >> 7 != in_data[i + 3] >> 7)) {
|
||||
rtype = RESP_CKSUM;
|
||||
break;
|
||||
}
|
||||
if (val && (val <= in_len || SWAP32(val) <= in_len)) {
|
||||
|
||||
rtype = RESP_LEN;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 1: case 3: case 5 ... MAX_AUTO_EXTRA - 1: break;
|
||||
/* Uniform integers may be checksums. */
|
||||
|
||||
if (val && (in_data[i] >> 7 != in_data[i + 1] >> 7 ||
|
||||
in_data[i] >> 7 != in_data[i + 2] >> 7 ||
|
||||
in_data[i] >> 7 != in_data[i + 3] >> 7)) {
|
||||
|
||||
rtype = RESP_CKSUM;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
case 5 ... MAX_AUTO_EXTRA - 1: break;
|
||||
|
||||
default: rtype = RESP_SUSPECT;
|
||||
|
||||
@ -474,19 +489,22 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) {
|
||||
|
||||
switch (rtype) {
|
||||
|
||||
case RESP_NONE: SAYF(cLGR bgGRA); break;
|
||||
case RESP_MINOR: SAYF(cBRI bgGRA); break;
|
||||
case RESP_NONE: SAYF(cLGR bgGRA); break;
|
||||
case RESP_MINOR: SAYF(cBRI bgGRA); break;
|
||||
case RESP_VARIABLE: SAYF(cBLK bgCYA); break;
|
||||
case RESP_FIXED: SAYF(cBLK bgMGN); break;
|
||||
case RESP_LEN: SAYF(cBLK bgLGN); break;
|
||||
case RESP_CKSUM: SAYF(cBLK bgYEL); break;
|
||||
case RESP_SUSPECT: SAYF(cBLK bgLRD); break;
|
||||
case RESP_FIXED: SAYF(cBLK bgMGN); break;
|
||||
case RESP_LEN: SAYF(cBLK bgLGN); break;
|
||||
case RESP_CKSUM: SAYF(cBLK bgYEL); break;
|
||||
case RESP_SUSPECT: SAYF(cBLK bgLRD); break;
|
||||
|
||||
}
|
||||
|
||||
show_char(in_data[i + off]);
|
||||
|
||||
if (off != rlen - 1 && (i + off + 1) % 16) SAYF(" "); else SAYF(cRST " ");
|
||||
if (off != rlen - 1 && (i + off + 1) % 16)
|
||||
SAYF(" ");
|
||||
else
|
||||
SAYF(cRST " ");
|
||||
|
||||
}
|
||||
|
||||
@ -499,17 +517,17 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) {
|
||||
|
||||
switch (rtype) {
|
||||
|
||||
case RESP_NONE: SAYF("no-op block\n"); break;
|
||||
case RESP_MINOR: SAYF("superficial content\n"); break;
|
||||
case RESP_NONE: SAYF("no-op block\n"); break;
|
||||
case RESP_MINOR: SAYF("superficial content\n"); break;
|
||||
case RESP_VARIABLE: SAYF("critical stream\n"); break;
|
||||
case RESP_FIXED: SAYF("\"magic value\" section\n"); break;
|
||||
case RESP_LEN: SAYF("suspected length field\n"); break;
|
||||
case RESP_CKSUM: SAYF("suspected cksum or magic int\n"); break;
|
||||
case RESP_SUSPECT: SAYF("suspected checksummed block\n"); break;
|
||||
case RESP_FIXED: SAYF("\"magic value\" section\n"); break;
|
||||
case RESP_LEN: SAYF("suspected length field\n"); break;
|
||||
case RESP_CKSUM: SAYF("suspected cksum or magic int\n"); break;
|
||||
case RESP_SUSPECT: SAYF("suspected checksummed block\n"); break;
|
||||
|
||||
}
|
||||
|
||||
#endif /* ^USE_COLOR */
|
||||
#endif /* ^USE_COLOR */
|
||||
|
||||
i += rlen - 1;
|
||||
|
||||
@ -517,12 +535,10 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) {
|
||||
|
||||
#ifdef USE_COLOR
|
||||
SAYF(cRST "\n");
|
||||
#endif /* USE_COLOR */
|
||||
#endif /* USE_COLOR */
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Actually analyze! */
|
||||
|
||||
static void analyze(char** argv) {
|
||||
@ -533,13 +549,13 @@ static void analyze(char** argv) {
|
||||
u8* b_data = ck_alloc(in_len + 1);
|
||||
u8 seq_byte = 0;
|
||||
|
||||
b_data[in_len] = 0xff; /* Intentional terminator. */
|
||||
b_data[in_len] = 0xff; /* Intentional terminator. */
|
||||
|
||||
ACTF("Analyzing input file (this may take a while)...\n");
|
||||
|
||||
#ifdef USE_COLOR
|
||||
show_legend();
|
||||
#endif /* USE_COLOR */
|
||||
#endif /* USE_COLOR */
|
||||
|
||||
for (i = 0; i < in_len; i++) {
|
||||
|
||||
@ -584,12 +600,15 @@ static void analyze(char** argv) {
|
||||
|
||||
b_data[i] = RESP_FIXED;
|
||||
|
||||
} else b_data[i] = RESP_VARIABLE;
|
||||
} else
|
||||
|
||||
b_data[i] = RESP_VARIABLE;
|
||||
|
||||
/* When all checksums change, flip most significant bit of b_data. */
|
||||
|
||||
if (prev_xff != xor_ff && prev_x01 != xor_01 &&
|
||||
prev_s10 != sub_10 && prev_a10 != add_10) seq_byte ^= 0x80;
|
||||
if (prev_xff != xor_ff && prev_x01 != xor_01 && prev_s10 != sub_10 &&
|
||||
prev_a10 != add_10)
|
||||
seq_byte ^= 0x80;
|
||||
|
||||
b_data[i] |= seq_byte;
|
||||
|
||||
@ -598,7 +617,7 @@ static void analyze(char** argv) {
|
||||
prev_s10 = sub_10;
|
||||
prev_a10 = add_10;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
dump_hex(in_data, in_len, b_data);
|
||||
|
||||
@ -615,8 +634,6 @@ static void analyze(char** argv) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Handle Ctrl-C and the like. */
|
||||
|
||||
static void handle_stop_sig(int sig) {
|
||||
@ -627,7 +644,6 @@ static void handle_stop_sig(int sig) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Do basic preparations - persistent fds, filenames, etc. */
|
||||
|
||||
static void set_up_environment(void) {
|
||||
@ -671,18 +687,20 @@ static void set_up_environment(void) {
|
||||
if (x) {
|
||||
|
||||
if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR)))
|
||||
FATAL("Custom MSAN_OPTIONS set without exit_code="
|
||||
STRINGIFY(MSAN_ERROR) " - please fix!");
|
||||
FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(
|
||||
MSAN_ERROR) " - please fix!");
|
||||
|
||||
if (!strstr(x, "symbolize=0"))
|
||||
FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");
|
||||
|
||||
}
|
||||
|
||||
setenv("ASAN_OPTIONS", "abort_on_error=1:"
|
||||
"detect_leaks=0:"
|
||||
"symbolize=0:"
|
||||
"allocator_may_return_null=1", 0);
|
||||
setenv("ASAN_OPTIONS",
|
||||
"abort_on_error=1:"
|
||||
"detect_leaks=0:"
|
||||
"symbolize=0:"
|
||||
"allocator_may_return_null=1",
|
||||
0);
|
||||
|
||||
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
||||
"symbolize=0:"
|
||||
@ -691,21 +709,22 @@ static void set_up_environment(void) {
|
||||
"msan_track_origins=0", 0);
|
||||
|
||||
if (getenv("AFL_PRELOAD")) {
|
||||
|
||||
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
||||
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Setup signal handlers, duh. */
|
||||
|
||||
static void setup_signal_handlers(void) {
|
||||
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = NULL;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = NULL;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_sigaction = NULL;
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
@ -724,43 +743,42 @@ static void setup_signal_handlers(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Display usage hints. */
|
||||
|
||||
static void usage(u8* argv0) {
|
||||
|
||||
SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
|
||||
SAYF(
|
||||
"\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
|
||||
|
||||
"Required parameters:\n\n"
|
||||
"Required parameters:\n\n"
|
||||
|
||||
" -i file - input test case to be analyzed by the tool\n"
|
||||
" -i file - input test case to be analyzed by the tool\n"
|
||||
|
||||
"Execution control settings:\n\n"
|
||||
"Execution control settings:\n\n"
|
||||
|
||||
" -f file - input file read by the tested program (stdin)\n"
|
||||
" -t msec - timeout for each run (%u ms)\n"
|
||||
" -m megs - memory limit for child process (%u MB)\n"
|
||||
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||
" -U - use unicorn-based instrumentation (Unicorn mode)\n\n"
|
||||
" -f file - input file read by the tested program (stdin)\n"
|
||||
" -t msec - timeout for each run (%d ms)\n"
|
||||
" -m megs - memory limit for child process (%d MB)\n"
|
||||
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||
" -U - use unicorn-based instrumentation (Unicorn mode)\n\n"
|
||||
|
||||
"Analysis settings:\n\n"
|
||||
"Analysis settings:\n\n"
|
||||
|
||||
" -e - look for edge coverage only, ignore hit counts\n\n"
|
||||
" -e - look for edge coverage only, ignore hit counts\n\n"
|
||||
|
||||
"For additional tips, please consult %s/README.\n\n",
|
||||
"For additional tips, please consult %s/README.\n\n",
|
||||
|
||||
argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path);
|
||||
argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path);
|
||||
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Find binary. */
|
||||
|
||||
static void find_binary(u8* fname) {
|
||||
|
||||
u8* env_path = 0;
|
||||
u8* env_path = 0;
|
||||
struct stat st;
|
||||
|
||||
if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
|
||||
@ -783,7 +801,9 @@ static void find_binary(u8* fname) {
|
||||
memcpy(cur_elem, env_path, delim - env_path);
|
||||
delim++;
|
||||
|
||||
} else cur_elem = ck_strdup(env_path);
|
||||
} else
|
||||
|
||||
cur_elem = ck_strdup(env_path);
|
||||
|
||||
env_path = delim;
|
||||
|
||||
@ -795,7 +815,8 @@ static void find_binary(u8* fname) {
|
||||
ck_free(cur_elem);
|
||||
|
||||
if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
|
||||
(st.st_mode & 0111) && st.st_size >= 4) break;
|
||||
(st.st_mode & 0111) && st.st_size >= 4)
|
||||
break;
|
||||
|
||||
ck_free(target_path);
|
||||
target_path = 0;
|
||||
@ -808,13 +829,12 @@ static void find_binary(u8* fname) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Fix up argv for QEMU. */
|
||||
|
||||
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
||||
|
||||
char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
|
||||
u8 *tmp, *cp, *rsl, *own_copy;
|
||||
u8 * tmp, *cp, *rsl, *own_copy;
|
||||
|
||||
memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc);
|
||||
|
||||
@ -829,8 +849,7 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
||||
|
||||
cp = alloc_printf("%s/afl-qemu-trace", tmp);
|
||||
|
||||
if (access(cp, X_OK))
|
||||
FATAL("Unable to find '%s'", tmp);
|
||||
if (access(cp, X_OK)) FATAL("Unable to find '%s'", tmp);
|
||||
|
||||
target_path = new_argv[0] = cp;
|
||||
return new_argv;
|
||||
@ -854,7 +873,9 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
||||
|
||||
}
|
||||
|
||||
} else ck_free(own_copy);
|
||||
} else
|
||||
|
||||
ck_free(own_copy);
|
||||
|
||||
if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {
|
||||
|
||||
@ -879,7 +900,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
SAYF(cCYA "afl-analyze" VERSION cRST " by <lcamtuf@google.com>\n");
|
||||
|
||||
while ((opt = getopt(argc,argv,"+i:f:m:t:eQU")) > 0)
|
||||
while ((opt = getopt(argc, argv, "+i:f:m:t:eQUh")) > 0)
|
||||
|
||||
switch (opt) {
|
||||
|
||||
@ -893,7 +914,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (prog_in) FATAL("Multiple -f options not supported");
|
||||
use_stdin = 0;
|
||||
prog_in = optarg;
|
||||
prog_in = optarg;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
@ -904,40 +925,41 @@ int main(int argc, char** argv) {
|
||||
|
||||
case 'm': {
|
||||
|
||||
u8 suffix = 'M';
|
||||
u8 suffix = 'M';
|
||||
|
||||
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
||||
mem_limit_given = 1;
|
||||
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
||||
mem_limit_given = 1;
|
||||
|
||||
if (!strcmp(optarg, "none")) {
|
||||
if (!strcmp(optarg, "none")) {
|
||||
|
||||
mem_limit = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
|
||||
optarg[0] == '-') FATAL("Bad syntax used for -m");
|
||||
|
||||
switch (suffix) {
|
||||
|
||||
case 'T': mem_limit *= 1024 * 1024; break;
|
||||
case 'G': mem_limit *= 1024; break;
|
||||
case 'k': mem_limit /= 1024; break;
|
||||
case 'M': break;
|
||||
|
||||
default: FATAL("Unsupported suffix or bad syntax for -m");
|
||||
|
||||
}
|
||||
|
||||
if (mem_limit < 5) FATAL("Dangerously low value of -m");
|
||||
|
||||
if (sizeof(rlim_t) == 4 && mem_limit > 2000)
|
||||
FATAL("Value of -m out of range on 32-bit systems");
|
||||
mem_limit = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
|
||||
optarg[0] == '-')
|
||||
FATAL("Bad syntax used for -m");
|
||||
|
||||
switch (suffix) {
|
||||
|
||||
case 'T': mem_limit *= 1024 * 1024; break;
|
||||
case 'G': mem_limit *= 1024; break;
|
||||
case 'k': mem_limit /= 1024; break;
|
||||
case 'M': break;
|
||||
|
||||
default: FATAL("Unsupported suffix or bad syntax for -m");
|
||||
|
||||
}
|
||||
|
||||
if (mem_limit < 5) FATAL("Dangerously low value of -m");
|
||||
|
||||
if (sizeof(rlim_t) == 4 && mem_limit > 2000)
|
||||
FATAL("Value of -m out of range on 32-bit systems");
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 't':
|
||||
|
||||
@ -966,10 +988,13 @@ int main(int argc, char** argv) {
|
||||
|
||||
unicorn_mode = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
break;
|
||||
|
||||
default: usage(argv[0]);
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,15 @@
|
||||
/*
|
||||
american fuzzy lop - wrapper for GNU as
|
||||
---------------------------------------
|
||||
american fuzzy lop++ - wrapper for GNU as
|
||||
-----------------------------------------
|
||||
|
||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Copyright 2013, 2014, 2015 Google Inc. All rights reserved.
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -48,38 +53,37 @@
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static u8** as_params; /* Parameters passed to the real 'as' */
|
||||
static u8** as_params; /* Parameters passed to the real 'as' */
|
||||
|
||||
static u8* input_file; /* Originally specified input file */
|
||||
static u8* modified_file; /* Instrumented file for the real 'as' */
|
||||
static u8* input_file; /* Originally specified input file */
|
||||
static u8* modified_file; /* Instrumented file for the real 'as' */
|
||||
|
||||
static u8 be_quiet, /* Quiet mode (no stderr output) */
|
||||
clang_mode, /* Running in clang mode? */
|
||||
pass_thru, /* Just pass data through? */
|
||||
just_version, /* Just show version? */
|
||||
sanitizer; /* Using ASAN / MSAN */
|
||||
static u8 be_quiet, /* Quiet mode (no stderr output) */
|
||||
clang_mode, /* Running in clang mode? */
|
||||
pass_thru, /* Just pass data through? */
|
||||
just_version, /* Just show version? */
|
||||
sanitizer; /* Using ASAN / MSAN */
|
||||
|
||||
static u32 inst_ratio = 100, /* Instrumentation probability (%) */
|
||||
as_par_cnt = 1; /* Number of params to 'as' */
|
||||
static u32 inst_ratio = 100, /* Instrumentation probability (%) */
|
||||
as_par_cnt = 1; /* Number of params to 'as' */
|
||||
|
||||
/* If we don't find --32 or --64 in the command line, default to
|
||||
/* If we don't find --32 or --64 in the command line, default to
|
||||
instrumentation for whichever mode we were compiled with. This is not
|
||||
perfect, but should do the trick for almost all use cases. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
static u8 use_64bit = 1;
|
||||
static u8 use_64bit = 1;
|
||||
|
||||
#else
|
||||
|
||||
static u8 use_64bit = 0;
|
||||
static u8 use_64bit = 0;
|
||||
|
||||
#ifdef __APPLE__
|
||||
# error "Sorry, 32-bit Apple platforms are not supported."
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#endif /* ^__x86_64__ */
|
||||
#error "Sorry, 32-bit Apple platforms are not supported."
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
/* Examine and modify parameters to pass to 'as'. Note that the file name
|
||||
is always the last parameter passed by GCC, so we exploit this property
|
||||
@ -116,7 +120,7 @@ static void edit_params(int argc, char** argv) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
/* Although this is not documented, GCC also uses TEMP and TMP when TMPDIR
|
||||
is not set. We need to check these non-standard variables to properly
|
||||
@ -134,8 +138,10 @@ static void edit_params(int argc, char** argv) {
|
||||
|
||||
for (i = 1; i < argc - 1; i++) {
|
||||
|
||||
if (!strcmp(argv[i], "--64")) use_64bit = 1;
|
||||
else if (!strcmp(argv[i], "--32")) use_64bit = 0;
|
||||
if (!strcmp(argv[i], "--64"))
|
||||
use_64bit = 1;
|
||||
else if (!strcmp(argv[i], "--32"))
|
||||
use_64bit = 0;
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
@ -143,7 +149,8 @@ static void edit_params(int argc, char** argv) {
|
||||
|
||||
if (!strcmp(argv[i], "-arch") && i + 1 < argc) {
|
||||
|
||||
if (!strcmp(argv[i + 1], "x86_64")) use_64bit = 1;
|
||||
if (!strcmp(argv[i + 1], "x86_64"))
|
||||
use_64bit = 1;
|
||||
else if (!strcmp(argv[i + 1], "i386"))
|
||||
FATAL("Sorry, 32-bit Apple platforms are not supported.");
|
||||
|
||||
@ -155,7 +162,7 @@ static void edit_params(int argc, char** argv) {
|
||||
if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q")))
|
||||
continue;
|
||||
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
as_params[as_par_cnt++] = argv[i];
|
||||
|
||||
@ -174,20 +181,24 @@ static void edit_params(int argc, char** argv) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
input_file = argv[argc - 1];
|
||||
|
||||
if (input_file[0] == '-') {
|
||||
|
||||
if (!strcmp(input_file + 1, "-version")) {
|
||||
|
||||
just_version = 1;
|
||||
modified_file = input_file;
|
||||
goto wrap_things_up;
|
||||
|
||||
}
|
||||
|
||||
if (input_file[1]) FATAL("Incorrect use (not called through afl-gcc?)");
|
||||
else input_file = NULL;
|
||||
if (input_file[1])
|
||||
FATAL("Incorrect use (not called through afl-gcc?)");
|
||||
else
|
||||
input_file = NULL;
|
||||
|
||||
} else {
|
||||
|
||||
@ -197,22 +208,21 @@ static void edit_params(int argc, char** argv) {
|
||||
NSS. */
|
||||
|
||||
if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) &&
|
||||
strncmp(input_file, "/var/tmp/", 9) &&
|
||||
strncmp(input_file, "/tmp/", 5)) pass_thru = 1;
|
||||
strncmp(input_file, "/var/tmp/", 9) && strncmp(input_file, "/tmp/", 5))
|
||||
pass_thru = 1;
|
||||
|
||||
}
|
||||
|
||||
modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(),
|
||||
(u32)time(NULL));
|
||||
modified_file =
|
||||
alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(), (u32)time(NULL));
|
||||
|
||||
wrap_things_up:
|
||||
|
||||
as_params[as_par_cnt++] = modified_file;
|
||||
as_params[as_par_cnt] = NULL;
|
||||
as_params[as_par_cnt] = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Process input file, generate modified_file. Insert instrumentation in all
|
||||
the appropriate places. */
|
||||
|
||||
@ -222,24 +232,26 @@ static void add_instrumentation(void) {
|
||||
|
||||
FILE* inf;
|
||||
FILE* outf;
|
||||
s32 outfd;
|
||||
u32 ins_lines = 0;
|
||||
s32 outfd;
|
||||
u32 ins_lines = 0;
|
||||
|
||||
u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0,
|
||||
skip_intel = 0, skip_app = 0, instrument_next = 0;
|
||||
u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0, skip_intel = 0,
|
||||
skip_app = 0, instrument_next = 0;
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
u8* colon_pos;
|
||||
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
if (input_file) {
|
||||
|
||||
inf = fopen(input_file, "r");
|
||||
if (!inf) PFATAL("Unable to read '%s'", input_file);
|
||||
|
||||
} else inf = stdin;
|
||||
} else
|
||||
|
||||
inf = stdin;
|
||||
|
||||
outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600);
|
||||
|
||||
@ -247,7 +259,7 @@ static void add_instrumentation(void) {
|
||||
|
||||
outf = fdopen(outfd, "w");
|
||||
|
||||
if (!outf) PFATAL("fdopen() failed");
|
||||
if (!outf) PFATAL("fdopen() failed");
|
||||
|
||||
while (fgets(line, MAX_LINE, inf)) {
|
||||
|
||||
@ -284,22 +296,26 @@ static void add_instrumentation(void) {
|
||||
around them, so we use that as a signal. */
|
||||
|
||||
if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) &&
|
||||
isdigit(line[10]) && line[11] == '\n') skip_next_label = 1;
|
||||
isdigit(line[10]) && line[11] == '\n')
|
||||
skip_next_label = 1;
|
||||
|
||||
if (!strncmp(line + 2, "text\n", 5) ||
|
||||
!strncmp(line + 2, "section\t.text", 13) ||
|
||||
!strncmp(line + 2, "section\t__TEXT,__text", 21) ||
|
||||
!strncmp(line + 2, "section __TEXT,__text", 21)) {
|
||||
|
||||
instr_ok = 1;
|
||||
continue;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (!strncmp(line + 2, "section\t", 8) ||
|
||||
!strncmp(line + 2, "section ", 8) ||
|
||||
!strncmp(line + 2, "bss\n", 4) ||
|
||||
!strncmp(line + 2, "section ", 8) || !strncmp(line + 2, "bss\n", 4) ||
|
||||
!strncmp(line + 2, "data\n", 5)) {
|
||||
|
||||
instr_ok = 0;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -354,8 +370,9 @@ static void add_instrumentation(void) {
|
||||
|
||||
*/
|
||||
|
||||
if (skip_intel || skip_app || skip_csect || !instr_ok ||
|
||||
line[0] == '#' || line[0] == ' ') continue;
|
||||
if (skip_intel || skip_app || skip_csect || !instr_ok || line[0] == '#' ||
|
||||
line[0] == ' ')
|
||||
continue;
|
||||
|
||||
/* Conditional branch instruction (jnz, etc). We append the instrumentation
|
||||
right after the branch (to instrument the not-taken path) and at the
|
||||
@ -396,7 +413,7 @@ static void add_instrumentation(void) {
|
||||
|
||||
if (line[0] == '.') {
|
||||
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
/* .L0: or LBB0_0: style jump destination */
|
||||
|
||||
@ -404,17 +421,18 @@ static void add_instrumentation(void) {
|
||||
|
||||
/* Apple: L<num> / LBB<num> */
|
||||
|
||||
if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
|
||||
&& R(100) < inst_ratio) {
|
||||
if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3))) &&
|
||||
R(100) < inst_ratio) {
|
||||
|
||||
#else
|
||||
|
||||
/* Apple: .L<num> / .LBB<num> */
|
||||
|
||||
if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3)))
|
||||
&& R(100) < inst_ratio) {
|
||||
if ((isdigit(line[2]) ||
|
||||
(clang_mode && !strncmp(line + 1, "LBB", 3))) &&
|
||||
R(100) < inst_ratio) {
|
||||
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
/* An optimization is possible here by adding the code only if the
|
||||
label is mentioned in the code in contexts other than call / jmp.
|
||||
@ -427,7 +445,10 @@ static void add_instrumentation(void) {
|
||||
.Lfunc_begin0-style exception handling calculations (a problem on
|
||||
MacOS X). */
|
||||
|
||||
if (!skip_next_label) instrument_next = 1; else skip_next_label = 0;
|
||||
if (!skip_next_label)
|
||||
instrument_next = 1;
|
||||
else
|
||||
skip_next_label = 0;
|
||||
|
||||
}
|
||||
|
||||
@ -436,34 +457,34 @@ static void add_instrumentation(void) {
|
||||
/* Function label (always instrumented, deferred mode). */
|
||||
|
||||
instrument_next = 1;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ins_lines)
|
||||
fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
|
||||
if (ins_lines) fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
|
||||
|
||||
if (input_file) fclose(inf);
|
||||
fclose(outf);
|
||||
|
||||
if (!be_quiet) {
|
||||
|
||||
if (!ins_lines) WARNF("No instrumentation targets found%s.",
|
||||
pass_thru ? " (pass-thru mode)" : "");
|
||||
else OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).",
|
||||
ins_lines, use_64bit ? "64" : "32",
|
||||
getenv("AFL_HARDEN") ? "hardened" :
|
||||
(sanitizer ? "ASAN/MSAN" : "non-hardened"),
|
||||
inst_ratio);
|
||||
|
||||
if (!ins_lines)
|
||||
WARNF("No instrumentation targets found%s.",
|
||||
pass_thru ? " (pass-thru mode)" : "");
|
||||
else
|
||||
OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).", ins_lines,
|
||||
use_64bit ? "64" : "32",
|
||||
getenv("AFL_HARDEN") ? "hardened"
|
||||
: (sanitizer ? "ASAN/MSAN" : "non-hardened"),
|
||||
inst_ratio);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Main entry point */
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@ -473,7 +494,7 @@ int main(int argc, char** argv) {
|
||||
int status;
|
||||
u8* inst_ratio_str = getenv("AFL_INST_RATIO");
|
||||
|
||||
struct timeval tv;
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
clang_mode = !!getenv(CLANG_ENV_VAR);
|
||||
@ -481,19 +502,26 @@ int main(int argc, char** argv) {
|
||||
if (isatty(2) && !getenv("AFL_QUIET")) {
|
||||
|
||||
SAYF(cCYA "afl-as" VERSION cRST " by <lcamtuf@google.com>\n");
|
||||
|
||||
} else be_quiet = 1;
|
||||
|
||||
} else
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
if (argc < 2) {
|
||||
|
||||
SAYF("\n"
|
||||
"This is a helper application for afl-fuzz. It is a wrapper around GNU 'as',\n"
|
||||
"executed by the toolchain whenever using afl-gcc or afl-clang. You probably\n"
|
||||
"don't want to run this program directly.\n\n"
|
||||
SAYF(
|
||||
"\n"
|
||||
"This is a helper application for afl-fuzz. It is a wrapper around GNU "
|
||||
"'as',\n"
|
||||
"executed by the toolchain whenever using afl-gcc or afl-clang. You "
|
||||
"probably\n"
|
||||
"don't want to run this program directly.\n\n"
|
||||
|
||||
"Rarely, when dealing with extremely complex projects, it may be advisable to\n"
|
||||
"set AFL_INST_RATIO to a value less than 100 in order to reduce the odds of\n"
|
||||
"instrumenting every discovered branch.\n\n");
|
||||
"Rarely, when dealing with extremely complex projects, it may be "
|
||||
"advisable to\n"
|
||||
"set AFL_INST_RATIO to a value less than 100 in order to reduce the "
|
||||
"odds of\n"
|
||||
"instrumenting every discovered branch.\n\n");
|
||||
|
||||
exit(1);
|
||||
|
||||
@ -509,7 +537,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (inst_ratio_str) {
|
||||
|
||||
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100)
|
||||
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100)
|
||||
FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)");
|
||||
|
||||
}
|
||||
@ -524,9 +552,10 @@ int main(int argc, char** argv) {
|
||||
that... */
|
||||
|
||||
if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) {
|
||||
|
||||
sanitizer = 1;
|
||||
if (!getenv("AFL_INST_RATIO"))
|
||||
inst_ratio /= 3;
|
||||
if (!getenv("AFL_INST_RATIO")) inst_ratio /= 3;
|
||||
|
||||
}
|
||||
|
||||
if (!just_version) add_instrumentation();
|
97
src/afl-common.c
Normal file
97
src/afl-common.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
american fuzzy lop++ - common routines
|
||||
--------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
Gather some functions common to multiple executables
|
||||
|
||||
- detect_file_args
|
||||
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "alloc-inl.h"
|
||||
|
||||
/* Detect @@ in args. */
|
||||
#ifndef __glibc__
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
void detect_file_args(char** argv, u8* prog_in) {
|
||||
|
||||
u32 i = 0;
|
||||
#ifdef __GLIBC__
|
||||
u8* cwd = getcwd(NULL, 0); /* non portable glibc extension */
|
||||
#else
|
||||
u8* cwd;
|
||||
char* buf;
|
||||
long size = pathconf(".", _PC_PATH_MAX);
|
||||
if ((buf = (char*)malloc((size_t)size)) != NULL) {
|
||||
|
||||
cwd = getcwd(buf, (size_t)size); /* portable version */
|
||||
|
||||
} else {
|
||||
|
||||
PFATAL("getcwd() failed");
|
||||
cwd = 0; /* for dumb compilers */
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (!cwd) PFATAL("getcwd() failed");
|
||||
|
||||
while (argv[i]) {
|
||||
|
||||
u8* aa_loc = strstr(argv[i], "@@");
|
||||
|
||||
if (aa_loc) {
|
||||
|
||||
u8 *aa_subst, *n_arg;
|
||||
|
||||
if (!prog_in) FATAL("@@ syntax is not supported by this tool.");
|
||||
|
||||
/* Be sure that we're always using fully-qualified paths. */
|
||||
|
||||
if (prog_in[0] == '/')
|
||||
aa_subst = prog_in;
|
||||
else
|
||||
aa_subst = alloc_printf("%s/%s", cwd, prog_in);
|
||||
|
||||
/* Construct a replacement argv value. */
|
||||
|
||||
*aa_loc = 0;
|
||||
n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);
|
||||
argv[i] = n_arg;
|
||||
*aa_loc = '@';
|
||||
|
||||
if (prog_in[0] != '/') ck_free(aa_subst);
|
||||
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
free(cwd); /* not tracked */
|
||||
|
||||
}
|
||||
|
456
src/afl-forkserver.c
Normal file
456
src/afl-forkserver.c
Normal file
@ -0,0 +1,456 @@
|
||||
/*
|
||||
american fuzzy lop++ - forkserver code
|
||||
--------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
Shared code that implements a forkserver. This is used by the fuzzer
|
||||
as well the other components like afl-tmin.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "forkserver.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
/* a program that includes afl-forkserver needs to define these */
|
||||
extern u8 uses_asan;
|
||||
extern u8 *trace_bits;
|
||||
extern s32 forksrv_pid, child_pid, fsrv_ctl_fd, fsrv_st_fd;
|
||||
extern s32 out_fd, out_dir_fd, dev_urandom_fd,
|
||||
dev_null_fd; /* initialize these with -1 */
|
||||
extern u32 exec_tmout;
|
||||
extern u64 mem_limit;
|
||||
extern u8 * out_file, *target_path, *doc_path;
|
||||
extern FILE *plot_file;
|
||||
|
||||
/* we need this internally but can be defined and read extern in the main source
|
||||
*/
|
||||
u8 child_timed_out;
|
||||
|
||||
/* Describe integer as memory size. */
|
||||
|
||||
u8 *forkserver_DMS(u64 val) {
|
||||
|
||||
static u8 tmp[12][16];
|
||||
static u8 cur;
|
||||
|
||||
#define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast) \
|
||||
do { \
|
||||
\
|
||||
if (val < (_divisor) * (_limit_mult)) { \
|
||||
\
|
||||
sprintf(tmp[cur], _fmt, ((_cast)val) / (_divisor)); \
|
||||
return tmp[cur]; \
|
||||
\
|
||||
} \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
cur = (cur + 1) % 12;
|
||||
|
||||
/* 0-9999 */
|
||||
CHK_FORMAT(1, 10000, "%llu B", u64);
|
||||
|
||||
/* 10.0k - 99.9k */
|
||||
CHK_FORMAT(1024, 99.95, "%0.01f kB", double);
|
||||
|
||||
/* 100k - 999k */
|
||||
CHK_FORMAT(1024, 1000, "%llu kB", u64);
|
||||
|
||||
/* 1.00M - 9.99M */
|
||||
CHK_FORMAT(1024 * 1024, 9.995, "%0.02f MB", double);
|
||||
|
||||
/* 10.0M - 99.9M */
|
||||
CHK_FORMAT(1024 * 1024, 99.95, "%0.01f MB", double);
|
||||
|
||||
/* 100M - 999M */
|
||||
CHK_FORMAT(1024 * 1024, 1000, "%llu MB", u64);
|
||||
|
||||
/* 1.00G - 9.99G */
|
||||
CHK_FORMAT(1024LL * 1024 * 1024, 9.995, "%0.02f GB", double);
|
||||
|
||||
/* 10.0G - 99.9G */
|
||||
CHK_FORMAT(1024LL * 1024 * 1024, 99.95, "%0.01f GB", double);
|
||||
|
||||
/* 100G - 999G */
|
||||
CHK_FORMAT(1024LL * 1024 * 1024, 1000, "%llu GB", u64);
|
||||
|
||||
/* 1.00T - 9.99G */
|
||||
CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 9.995, "%0.02f TB", double);
|
||||
|
||||
/* 10.0T - 99.9T */
|
||||
CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 99.95, "%0.01f TB", double);
|
||||
|
||||
#undef CHK_FORMAT
|
||||
|
||||
/* 100T+ */
|
||||
strcpy(tmp[cur], "infty");
|
||||
return tmp[cur];
|
||||
|
||||
}
|
||||
|
||||
/* the timeout handler */
|
||||
|
||||
void handle_timeout(int sig) {
|
||||
|
||||
if (child_pid > 0) {
|
||||
|
||||
child_timed_out = 1;
|
||||
kill(child_pid, SIGKILL);
|
||||
|
||||
} else if (child_pid == -1 && forksrv_pid > 0) {
|
||||
|
||||
child_timed_out = 1;
|
||||
kill(forksrv_pid, SIGKILL);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Spin up fork server (instrumented mode only). The idea is explained here:
|
||||
|
||||
http://lcamtuf.blogspot.com/2014/10/fuzzing-binaries-without-execve.html
|
||||
|
||||
In essence, the instrumentation allows us to skip execve(), and just keep
|
||||
cloning a stopped child. So, we just execute once, and then send commands
|
||||
through a pipe. The other part of this logic is in afl-as.h / llvm_mode */
|
||||
|
||||
void init_forkserver(char **argv) {
|
||||
|
||||
static struct itimerval it;
|
||||
int st_pipe[2], ctl_pipe[2];
|
||||
int status;
|
||||
s32 rlen;
|
||||
|
||||
ACTF("Spinning up the fork server...");
|
||||
|
||||
if (pipe(st_pipe) || pipe(ctl_pipe)) PFATAL("pipe() failed");
|
||||
|
||||
child_timed_out = 0;
|
||||
forksrv_pid = fork();
|
||||
|
||||
if (forksrv_pid < 0) PFATAL("fork() failed");
|
||||
|
||||
if (!forksrv_pid) {
|
||||
|
||||
/* CHILD PROCESS */
|
||||
|
||||
struct rlimit r;
|
||||
|
||||
/* Umpf. On OpenBSD, the default fd limit for root users is set to
|
||||
soft 128. Let's try to fix that... */
|
||||
|
||||
if (!getrlimit(RLIMIT_NOFILE, &r) && r.rlim_cur < FORKSRV_FD + 2) {
|
||||
|
||||
r.rlim_cur = FORKSRV_FD + 2;
|
||||
setrlimit(RLIMIT_NOFILE, &r); /* Ignore errors */
|
||||
|
||||
}
|
||||
|
||||
if (mem_limit) {
|
||||
|
||||
r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;
|
||||
|
||||
#ifdef RLIMIT_AS
|
||||
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
||||
#else
|
||||
/* This takes care of OpenBSD, which doesn't have RLIMIT_AS, but
|
||||
according to reliable sources, RLIMIT_DATA covers anonymous
|
||||
maps - so we should be getting good protection against OOM bugs. */
|
||||
|
||||
setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
|
||||
#endif /* ^RLIMIT_AS */
|
||||
|
||||
}
|
||||
|
||||
/* Dumping cores is slow and can lead to anomalies if SIGKILL is delivered
|
||||
before the dump is complete. */
|
||||
|
||||
// r.rlim_max = r.rlim_cur = 0;
|
||||
// setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
|
||||
|
||||
/* Isolate the process and configure standard descriptors. If out_file is
|
||||
specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */
|
||||
|
||||
setsid();
|
||||
|
||||
if (!getenv("AFL_DEBUG_CHILD_OUTPUT")) {
|
||||
|
||||
dup2(dev_null_fd, 1);
|
||||
dup2(dev_null_fd, 2);
|
||||
|
||||
}
|
||||
|
||||
if (out_file) {
|
||||
|
||||
dup2(dev_null_fd, 0);
|
||||
|
||||
} else {
|
||||
|
||||
dup2(out_fd, 0);
|
||||
close(out_fd);
|
||||
|
||||
}
|
||||
|
||||
/* Set up control and status pipes, close the unneeded original fds. */
|
||||
|
||||
if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed");
|
||||
if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed");
|
||||
|
||||
close(ctl_pipe[0]);
|
||||
close(ctl_pipe[1]);
|
||||
close(st_pipe[0]);
|
||||
close(st_pipe[1]);
|
||||
|
||||
close(out_dir_fd);
|
||||
close(dev_null_fd);
|
||||
close(dev_urandom_fd);
|
||||
close(plot_file == NULL ? -1 : fileno(plot_file));
|
||||
|
||||
/* This should improve performance a bit, since it stops the linker from
|
||||
doing extra work post-fork(). */
|
||||
|
||||
if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0);
|
||||
|
||||
/* Set sane defaults for ASAN if nothing else specified. */
|
||||
|
||||
setenv("ASAN_OPTIONS",
|
||||
"abort_on_error=1:"
|
||||
"detect_leaks=0:"
|
||||
"symbolize=0:"
|
||||
"allocator_may_return_null=1",
|
||||
0);
|
||||
|
||||
/* MSAN is tricky, because it doesn't support abort_on_error=1 at this
|
||||
point. So, we do this in a very hacky way. */
|
||||
|
||||
setenv("MSAN_OPTIONS",
|
||||
"exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
||||
"symbolize=0:"
|
||||
"abort_on_error=1:"
|
||||
"allocator_may_return_null=1:"
|
||||
"msan_track_origins=0",
|
||||
0);
|
||||
|
||||
execv(target_path, argv);
|
||||
|
||||
/* Use a distinctive bitmap signature to tell the parent about execv()
|
||||
falling through. */
|
||||
|
||||
*(u32 *)trace_bits = EXEC_FAIL_SIG;
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
||||
/* PARENT PROCESS */
|
||||
|
||||
/* Close the unneeded endpoints. */
|
||||
|
||||
close(ctl_pipe[0]);
|
||||
close(st_pipe[1]);
|
||||
|
||||
fsrv_ctl_fd = ctl_pipe[1];
|
||||
fsrv_st_fd = st_pipe[0];
|
||||
|
||||
/* Wait for the fork server to come up, but don't wait too long. */
|
||||
|
||||
if (exec_tmout) {
|
||||
|
||||
it.it_value.tv_sec = ((exec_tmout * FORK_WAIT_MULT) / 1000);
|
||||
it.it_value.tv_usec = ((exec_tmout * FORK_WAIT_MULT) % 1000) * 1000;
|
||||
|
||||
}
|
||||
|
||||
setitimer(ITIMER_REAL, &it, NULL);
|
||||
|
||||
rlen = read(fsrv_st_fd, &status, 4);
|
||||
|
||||
it.it_value.tv_sec = 0;
|
||||
it.it_value.tv_usec = 0;
|
||||
|
||||
setitimer(ITIMER_REAL, &it, NULL);
|
||||
|
||||
/* If we have a four-byte "hello" message from the server, we're all set.
|
||||
Otherwise, try to figure out what went wrong. */
|
||||
|
||||
if (rlen == 4) {
|
||||
|
||||
OKF("All right - fork server is up.");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (child_timed_out)
|
||||
FATAL("Timeout while initializing fork server (adjusting -t may help)");
|
||||
|
||||
if (waitpid(forksrv_pid, &status, 0) <= 0) PFATAL("waitpid() failed");
|
||||
|
||||
if (WIFSIGNALED(status)) {
|
||||
|
||||
if (mem_limit && mem_limit < 500 && uses_asan) {
|
||||
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
"Whoops, the target binary crashed suddenly, "
|
||||
"before receiving any input\n"
|
||||
" from the fuzzer! Since it seems to be built with ASAN and you "
|
||||
"have a\n"
|
||||
" restrictive memory limit configured, this is expected; please "
|
||||
"read\n"
|
||||
" %s/notes_for_asan.txt for help.\n",
|
||||
doc_path);
|
||||
|
||||
} else if (!mem_limit) {
|
||||
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
"Whoops, the target binary crashed suddenly, "
|
||||
"before receiving any input\n"
|
||||
" from the fuzzer! There are several probable explanations:\n\n"
|
||||
|
||||
" - The binary is just buggy and explodes entirely on its own. "
|
||||
"If so, you\n"
|
||||
" need to fix the underlying problem or find a better "
|
||||
"replacement.\n\n"
|
||||
|
||||
MSG_FORK_ON_APPLE
|
||||
|
||||
" - Less likely, there is a horrible bug in the fuzzer. If other "
|
||||
"options\n"
|
||||
" fail, poke <afl-users@googlegroups.com> for troubleshooting "
|
||||
"tips.\n");
|
||||
|
||||
} else {
|
||||
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
"Whoops, the target binary crashed suddenly, "
|
||||
"before receiving any input\n"
|
||||
" from the fuzzer! There are several probable explanations:\n\n"
|
||||
|
||||
" - The current memory limit (%s) is too restrictive, causing "
|
||||
"the\n"
|
||||
" target to hit an OOM condition in the dynamic linker. Try "
|
||||
"bumping up\n"
|
||||
" the limit with the -m setting in the command line. A simple "
|
||||
"way confirm\n"
|
||||
" this diagnosis would be:\n\n"
|
||||
|
||||
MSG_ULIMIT_USAGE
|
||||
" /path/to/fuzzed_app )\n\n"
|
||||
|
||||
" Tip: you can use http://jwilk.net/software/recidivm to "
|
||||
"quickly\n"
|
||||
" estimate the required amount of virtual memory for the "
|
||||
"binary.\n\n"
|
||||
|
||||
" - The binary is just buggy and explodes entirely on its own. "
|
||||
"If so, you\n"
|
||||
" need to fix the underlying problem or find a better "
|
||||
"replacement.\n\n"
|
||||
|
||||
MSG_FORK_ON_APPLE
|
||||
|
||||
" - Less likely, there is a horrible bug in the fuzzer. If other "
|
||||
"options\n"
|
||||
" fail, poke <afl-users@googlegroups.com> for troubleshooting "
|
||||
"tips.\n",
|
||||
forkserver_DMS(mem_limit << 20), mem_limit - 1);
|
||||
|
||||
}
|
||||
|
||||
FATAL("Fork server crashed with signal %d", WTERMSIG(status));
|
||||
|
||||
}
|
||||
|
||||
if (*(u32 *)trace_bits == EXEC_FAIL_SIG)
|
||||
FATAL("Unable to execute target application ('%s')", argv[0]);
|
||||
|
||||
if (mem_limit && mem_limit < 500 && uses_asan) {
|
||||
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
"Hmm, looks like the target binary terminated "
|
||||
"before we could complete a\n"
|
||||
" handshake with the injected code. Since it seems to be built "
|
||||
"with ASAN and\n"
|
||||
" you have a restrictive memory limit configured, this is "
|
||||
"expected; please\n"
|
||||
" read %s/notes_for_asan.txt for help.\n",
|
||||
doc_path);
|
||||
|
||||
} else if (!mem_limit) {
|
||||
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
"Hmm, looks like the target binary terminated "
|
||||
"before we could complete a\n"
|
||||
" handshake with the injected code. Perhaps there is a horrible "
|
||||
"bug in the\n"
|
||||
" fuzzer. Poke <afl-users@googlegroups.com> for troubleshooting "
|
||||
"tips.\n");
|
||||
|
||||
} else {
|
||||
|
||||
SAYF(
|
||||
"\n" cLRD "[-] " cRST
|
||||
"Hmm, looks like the target binary terminated "
|
||||
"before we could complete a\n"
|
||||
" handshake with the injected code. There are %s probable "
|
||||
"explanations:\n\n"
|
||||
|
||||
"%s"
|
||||
" - The current memory limit (%s) is too restrictive, causing an "
|
||||
"OOM\n"
|
||||
" fault in the dynamic linker. This can be fixed with the -m "
|
||||
"option. A\n"
|
||||
" simple way to confirm the diagnosis may be:\n\n"
|
||||
|
||||
MSG_ULIMIT_USAGE
|
||||
" /path/to/fuzzed_app )\n\n"
|
||||
|
||||
" Tip: you can use http://jwilk.net/software/recidivm to quickly\n"
|
||||
" estimate the required amount of virtual memory for the "
|
||||
"binary.\n\n"
|
||||
|
||||
" - Less likely, there is a horrible bug in the fuzzer. If other "
|
||||
"options\n"
|
||||
" fail, poke <afl-users@googlegroups.com> for troubleshooting "
|
||||
"tips.\n",
|
||||
getenv(DEFER_ENV_VAR) ? "three" : "two",
|
||||
getenv(DEFER_ENV_VAR)
|
||||
? " - You are using deferred forkserver, but __AFL_INIT() is "
|
||||
"never\n"
|
||||
" reached before the program terminates.\n\n"
|
||||
: "",
|
||||
forkserver_DMS(mem_limit << 20), mem_limit - 1);
|
||||
|
||||
}
|
||||
|
||||
FATAL("Fork server handshake failed");
|
||||
|
||||
}
|
||||
|
711
src/afl-fuzz-bitmap.c
Normal file
711
src/afl-fuzz-bitmap.c
Normal file
@ -0,0 +1,711 @@
|
||||
/*
|
||||
american fuzzy lop++ - bitmap related routines
|
||||
----------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This is the real deal: the program takes an instrumented binary and
|
||||
attempts a variety of basic fuzzing tricks, paying close attention to
|
||||
how they affect the execution path.
|
||||
|
||||
*/
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* Write bitmap to file. The bitmap is useful mostly for the secret
|
||||
-B option, to focus a separate fuzzing session on a particular
|
||||
interesting input without rediscovering all the others. */
|
||||
|
||||
void write_bitmap(void) {
|
||||
|
||||
u8* fname;
|
||||
s32 fd;
|
||||
|
||||
if (!bitmap_changed) return;
|
||||
bitmap_changed = 0;
|
||||
|
||||
fname = alloc_printf("%s/fuzz_bitmap", out_dir);
|
||||
fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to open '%s'", fname);
|
||||
|
||||
ck_write(fd, virgin_bits, MAP_SIZE, fname);
|
||||
|
||||
close(fd);
|
||||
ck_free(fname);
|
||||
|
||||
}
|
||||
|
||||
/* Read bitmap from file. This is for the -B option again. */
|
||||
|
||||
void read_bitmap(u8* fname) {
|
||||
|
||||
s32 fd = open(fname, O_RDONLY);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to open '%s'", fname);
|
||||
|
||||
ck_read(fd, virgin_bits, MAP_SIZE, fname);
|
||||
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
/* Check if the current execution path brings anything new to the table.
|
||||
Update virgin bits to reflect the finds. Returns 1 if the only change is
|
||||
the hit-count for a particular tuple; 2 if there are new tuples seen.
|
||||
Updates the map, so subsequent calls will always return 0.
|
||||
|
||||
This function is called after every exec() on a fairly large buffer, so
|
||||
it needs to be fast. We do this in 32-bit and 64-bit flavors. */
|
||||
|
||||
u8 has_new_bits(u8* virgin_map) {
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
u64* current = (u64*)trace_bits;
|
||||
u64* virgin = (u64*)virgin_map;
|
||||
|
||||
u32 i = (MAP_SIZE >> 3);
|
||||
|
||||
#else
|
||||
|
||||
u32* current = (u32*)trace_bits;
|
||||
u32* virgin = (u32*)virgin_map;
|
||||
|
||||
u32 i = (MAP_SIZE >> 2);
|
||||
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
u8 ret = 0;
|
||||
|
||||
while (i--) {
|
||||
|
||||
/* Optimize for (*current & *virgin) == 0 - i.e., no bits in current bitmap
|
||||
that have not been already cleared from the virgin map - since this will
|
||||
almost always be the case. */
|
||||
|
||||
if (unlikely(*current) && unlikely(*current & *virgin)) {
|
||||
|
||||
if (likely(ret < 2)) {
|
||||
|
||||
u8* cur = (u8*)current;
|
||||
u8* vir = (u8*)virgin;
|
||||
|
||||
/* Looks like we have not found any new bytes yet; see if any non-zero
|
||||
bytes in current[] are pristine in virgin[]. */
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) ||
|
||||
(cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff) ||
|
||||
(cur[4] && vir[4] == 0xff) || (cur[5] && vir[5] == 0xff) ||
|
||||
(cur[6] && vir[6] == 0xff) || (cur[7] && vir[7] == 0xff))
|
||||
ret = 2;
|
||||
else
|
||||
ret = 1;
|
||||
|
||||
#else
|
||||
|
||||
if ((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) ||
|
||||
(cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff))
|
||||
ret = 2;
|
||||
else
|
||||
ret = 1;
|
||||
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
}
|
||||
|
||||
*virgin &= ~*current;
|
||||
|
||||
}
|
||||
|
||||
++current;
|
||||
++virgin;
|
||||
|
||||
}
|
||||
|
||||
if (ret && virgin_map == virgin_bits) bitmap_changed = 1;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* Count the number of bits set in the provided bitmap. Used for the status
|
||||
screen several times every second, does not have to be fast. */
|
||||
|
||||
u32 count_bits(u8* mem) {
|
||||
|
||||
u32* ptr = (u32*)mem;
|
||||
u32 i = (MAP_SIZE >> 2);
|
||||
u32 ret = 0;
|
||||
|
||||
while (i--) {
|
||||
|
||||
u32 v = *(ptr++);
|
||||
|
||||
/* This gets called on the inverse, virgin bitmap; optimize for sparse
|
||||
data. */
|
||||
|
||||
if (v == 0xffffffff) {
|
||||
|
||||
ret += 32;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
v -= ((v >> 1) & 0x55555555);
|
||||
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
|
||||
ret += (((v + (v >> 4)) & 0xF0F0F0F) * 0x01010101) >> 24;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
#define FF(_b) (0xff << ((_b) << 3))
|
||||
|
||||
/* Count the number of bytes set in the bitmap. Called fairly sporadically,
|
||||
mostly to update the status screen or calibrate and examine confirmed
|
||||
new paths. */
|
||||
|
||||
u32 count_bytes(u8* mem) {
|
||||
|
||||
u32* ptr = (u32*)mem;
|
||||
u32 i = (MAP_SIZE >> 2);
|
||||
u32 ret = 0;
|
||||
|
||||
while (i--) {
|
||||
|
||||
u32 v = *(ptr++);
|
||||
|
||||
if (!v) continue;
|
||||
if (v & FF(0)) ++ret;
|
||||
if (v & FF(1)) ++ret;
|
||||
if (v & FF(2)) ++ret;
|
||||
if (v & FF(3)) ++ret;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* Count the number of non-255 bytes set in the bitmap. Used strictly for the
|
||||
status screen, several calls per second or so. */
|
||||
|
||||
u32 count_non_255_bytes(u8* mem) {
|
||||
|
||||
u32* ptr = (u32*)mem;
|
||||
u32 i = (MAP_SIZE >> 2);
|
||||
u32 ret = 0;
|
||||
|
||||
while (i--) {
|
||||
|
||||
u32 v = *(ptr++);
|
||||
|
||||
/* This is called on the virgin bitmap, so optimize for the most likely
|
||||
case. */
|
||||
|
||||
if (v == 0xffffffff) continue;
|
||||
if ((v & FF(0)) != FF(0)) ++ret;
|
||||
if ((v & FF(1)) != FF(1)) ++ret;
|
||||
if ((v & FF(2)) != FF(2)) ++ret;
|
||||
if ((v & FF(3)) != FF(3)) ++ret;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* Destructively simplify trace by eliminating hit count information
|
||||
and replacing it with 0x80 or 0x01 depending on whether the tuple
|
||||
is hit or not. Called on every new crash or timeout, should be
|
||||
reasonably fast. */
|
||||
|
||||
const u8 simplify_lookup[256] = {
|
||||
|
||||
[0] = 1, [1 ... 255] = 128
|
||||
|
||||
};
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
void simplify_trace(u64* mem) {
|
||||
|
||||
u32 i = MAP_SIZE >> 3;
|
||||
|
||||
while (i--) {
|
||||
|
||||
/* Optimize for sparse bitmaps. */
|
||||
|
||||
if (unlikely(*mem)) {
|
||||
|
||||
u8* mem8 = (u8*)mem;
|
||||
|
||||
mem8[0] = simplify_lookup[mem8[0]];
|
||||
mem8[1] = simplify_lookup[mem8[1]];
|
||||
mem8[2] = simplify_lookup[mem8[2]];
|
||||
mem8[3] = simplify_lookup[mem8[3]];
|
||||
mem8[4] = simplify_lookup[mem8[4]];
|
||||
mem8[5] = simplify_lookup[mem8[5]];
|
||||
mem8[6] = simplify_lookup[mem8[6]];
|
||||
mem8[7] = simplify_lookup[mem8[7]];
|
||||
|
||||
} else
|
||||
|
||||
*mem = 0x0101010101010101ULL;
|
||||
|
||||
++mem;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void simplify_trace(u32* mem) {
|
||||
|
||||
u32 i = MAP_SIZE >> 2;
|
||||
|
||||
while (i--) {
|
||||
|
||||
/* Optimize for sparse bitmaps. */
|
||||
|
||||
if (unlikely(*mem)) {
|
||||
|
||||
u8* mem8 = (u8*)mem;
|
||||
|
||||
mem8[0] = simplify_lookup[mem8[0]];
|
||||
mem8[1] = simplify_lookup[mem8[1]];
|
||||
mem8[2] = simplify_lookup[mem8[2]];
|
||||
mem8[3] = simplify_lookup[mem8[3]];
|
||||
|
||||
} else
|
||||
|
||||
*mem = 0x01010101;
|
||||
|
||||
++mem;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
/* Destructively classify execution counts in a trace. This is used as a
|
||||
preprocessing step for any newly acquired traces. Called on every exec,
|
||||
must be fast. */
|
||||
|
||||
static const u8 count_class_lookup8[256] = {
|
||||
|
||||
[0] = 0,
|
||||
[1] = 1,
|
||||
[2] = 2,
|
||||
[3] = 4,
|
||||
[4 ... 7] = 8,
|
||||
[8 ... 15] = 16,
|
||||
[16 ... 31] = 32,
|
||||
[32 ... 127] = 64,
|
||||
[128 ... 255] = 128
|
||||
|
||||
};
|
||||
|
||||
static u16 count_class_lookup16[65536];
|
||||
|
||||
void init_count_class16(void) {
|
||||
|
||||
u32 b1, b2;
|
||||
|
||||
for (b1 = 0; b1 < 256; b1++)
|
||||
for (b2 = 0; b2 < 256; b2++)
|
||||
count_class_lookup16[(b1 << 8) + b2] =
|
||||
(count_class_lookup8[b1] << 8) | count_class_lookup8[b2];
|
||||
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
void classify_counts(u64* mem) {
|
||||
|
||||
u32 i = MAP_SIZE >> 3;
|
||||
|
||||
while (i--) {
|
||||
|
||||
/* Optimize for sparse bitmaps. */
|
||||
|
||||
if (unlikely(*mem)) {
|
||||
|
||||
u16* mem16 = (u16*)mem;
|
||||
|
||||
mem16[0] = count_class_lookup16[mem16[0]];
|
||||
mem16[1] = count_class_lookup16[mem16[1]];
|
||||
mem16[2] = count_class_lookup16[mem16[2]];
|
||||
mem16[3] = count_class_lookup16[mem16[3]];
|
||||
|
||||
}
|
||||
|
||||
++mem;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void classify_counts(u32* mem) {
|
||||
|
||||
u32 i = MAP_SIZE >> 2;
|
||||
|
||||
while (i--) {
|
||||
|
||||
/* Optimize for sparse bitmaps. */
|
||||
|
||||
if (unlikely(*mem)) {
|
||||
|
||||
u16* mem16 = (u16*)mem;
|
||||
|
||||
mem16[0] = count_class_lookup16[mem16[0]];
|
||||
mem16[1] = count_class_lookup16[mem16[1]];
|
||||
|
||||
}
|
||||
|
||||
++mem;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
/* Compact trace bytes into a smaller bitmap. We effectively just drop the
|
||||
count information here. This is called only sporadically, for some
|
||||
new paths. */
|
||||
|
||||
void minimize_bits(u8* dst, u8* src) {
|
||||
|
||||
u32 i = 0;
|
||||
|
||||
while (i < MAP_SIZE) {
|
||||
|
||||
if (*(src++)) dst[i >> 3] |= 1 << (i & 7);
|
||||
++i;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifndef SIMPLE_FILES
|
||||
|
||||
/* Construct a file name for a new test case, capturing the operation
|
||||
that led to its discovery. Uses a static buffer. */
|
||||
|
||||
u8* describe_op(u8 hnb) {
|
||||
|
||||
static u8 ret[256];
|
||||
|
||||
if (syncing_party) {
|
||||
|
||||
sprintf(ret, "sync:%s,src:%06u", syncing_party, syncing_case);
|
||||
|
||||
} else {
|
||||
|
||||
sprintf(ret, "src:%06u", current_entry);
|
||||
|
||||
sprintf(ret + strlen(ret), ",time:%llu", get_cur_time() - start_time);
|
||||
|
||||
if (splicing_with >= 0) sprintf(ret + strlen(ret), "+%06d", splicing_with);
|
||||
|
||||
sprintf(ret + strlen(ret), ",op:%s", stage_short);
|
||||
|
||||
if (stage_cur_byte >= 0) {
|
||||
|
||||
sprintf(ret + strlen(ret), ",pos:%d", stage_cur_byte);
|
||||
|
||||
if (stage_val_type != STAGE_VAL_NONE)
|
||||
sprintf(ret + strlen(ret), ",val:%s%+d",
|
||||
(stage_val_type == STAGE_VAL_BE) ? "be:" : "", stage_cur_val);
|
||||
|
||||
} else
|
||||
|
||||
sprintf(ret + strlen(ret), ",rep:%d", stage_cur_val);
|
||||
|
||||
}
|
||||
|
||||
if (hnb == 2) strcat(ret, ",+cov");
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
#endif /* !SIMPLE_FILES */
|
||||
|
||||
/* Write a message accompanying the crash directory :-) */
|
||||
|
||||
static void write_crash_readme(void) {
|
||||
|
||||
u8* fn = alloc_printf("%s/crashes/README.txt", out_dir);
|
||||
s32 fd;
|
||||
FILE* f;
|
||||
|
||||
fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
ck_free(fn);
|
||||
|
||||
/* Do not die on errors here - that would be impolite. */
|
||||
|
||||
if (fd < 0) return;
|
||||
|
||||
f = fdopen(fd, "w");
|
||||
|
||||
if (!f) {
|
||||
|
||||
close(fd);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
fprintf(
|
||||
f,
|
||||
"Command line used to find this crash:\n\n"
|
||||
|
||||
"%s\n\n"
|
||||
|
||||
"If you can't reproduce a bug outside of afl-fuzz, be sure to set the "
|
||||
"same\n"
|
||||
"memory limit. The limit used for this fuzzing session was %s.\n\n"
|
||||
|
||||
"Need a tool to minimize test cases before investigating the crashes or "
|
||||
"sending\n"
|
||||
"them to a vendor? Check out the afl-tmin that comes with the fuzzer!\n\n"
|
||||
|
||||
"Found any cool bugs in open-source tools using afl-fuzz? If yes, please "
|
||||
"drop\n"
|
||||
"an mail at <afl-users@googlegroups.com> once the issues are fixed\n\n"
|
||||
|
||||
" https://github.com/vanhauser-thc/AFLplusplus\n\n",
|
||||
|
||||
orig_cmdline, DMS(mem_limit << 20)); /* ignore errors */
|
||||
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
|
||||
/* Check if the result of an execve() during routine fuzzing is interesting,
|
||||
save or queue the input test case for further analysis if so. Returns 1 if
|
||||
entry is saved, 0 otherwise. */
|
||||
|
||||
u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) {
|
||||
|
||||
if (len == 0) return 0;
|
||||
|
||||
u8* fn = "";
|
||||
u8 hnb;
|
||||
s32 fd;
|
||||
u8 keeping = 0, res;
|
||||
|
||||
/* Update path frequency. */
|
||||
u32 cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
|
||||
|
||||
struct queue_entry* q = queue;
|
||||
while (q) {
|
||||
|
||||
if (q->exec_cksum == cksum) q->n_fuzz = q->n_fuzz + 1;
|
||||
|
||||
q = q->next;
|
||||
|
||||
}
|
||||
|
||||
if (fault == crash_mode) {
|
||||
|
||||
/* Keep only if there are new bits in the map, add to queue for
|
||||
future fuzzing, etc. */
|
||||
|
||||
if (!(hnb = has_new_bits(virgin_bits))) {
|
||||
|
||||
if (crash_mode) ++total_crashes;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
#ifndef SIMPLE_FILES
|
||||
|
||||
fn = alloc_printf("%s/queue/id:%06u,%s", out_dir, queued_paths,
|
||||
describe_op(hnb));
|
||||
|
||||
#else
|
||||
|
||||
fn = alloc_printf("%s/queue/id_%06u", out_dir, queued_paths);
|
||||
|
||||
#endif /* ^!SIMPLE_FILES */
|
||||
|
||||
add_to_queue(fn, len, 0);
|
||||
|
||||
if (hnb == 2) {
|
||||
|
||||
queue_top->has_new_cov = 1;
|
||||
++queued_with_cov;
|
||||
|
||||
}
|
||||
|
||||
queue_top->exec_cksum = cksum;
|
||||
|
||||
/* Try to calibrate inline; this also calls update_bitmap_score() when
|
||||
successful. */
|
||||
|
||||
res = calibrate_case(argv, queue_top, mem, queue_cycle - 1, 0);
|
||||
|
||||
if (res == FAULT_ERROR) FATAL("Unable to execute target application");
|
||||
|
||||
fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", fn);
|
||||
ck_write(fd, mem, len, fn);
|
||||
close(fd);
|
||||
|
||||
keeping = 1;
|
||||
|
||||
}
|
||||
|
||||
switch (fault) {
|
||||
|
||||
case FAULT_TMOUT:
|
||||
|
||||
/* Timeouts are not very interesting, but we're still obliged to keep
|
||||
a handful of samples. We use the presence of new bits in the
|
||||
hang-specific bitmap as a signal of uniqueness. In "dumb" mode, we
|
||||
just keep everything. */
|
||||
|
||||
++total_tmouts;
|
||||
|
||||
if (unique_hangs >= KEEP_UNIQUE_HANG) return keeping;
|
||||
|
||||
if (!dumb_mode) {
|
||||
|
||||
#ifdef __x86_64__
|
||||
simplify_trace((u64*)trace_bits);
|
||||
#else
|
||||
simplify_trace((u32*)trace_bits);
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
if (!has_new_bits(virgin_tmout)) return keeping;
|
||||
|
||||
}
|
||||
|
||||
++unique_tmouts;
|
||||
|
||||
/* Before saving, we make sure that it's a genuine hang by re-running
|
||||
the target with a more generous timeout (unless the default timeout
|
||||
is already generous). */
|
||||
|
||||
if (exec_tmout < hang_tmout) {
|
||||
|
||||
u8 new_fault;
|
||||
write_to_testcase(mem, len);
|
||||
new_fault = run_target(argv, hang_tmout);
|
||||
|
||||
/* A corner case that one user reported bumping into: increasing the
|
||||
timeout actually uncovers a crash. Make sure we don't discard it if
|
||||
so. */
|
||||
|
||||
if (!stop_soon && new_fault == FAULT_CRASH) goto keep_as_crash;
|
||||
|
||||
if (stop_soon || new_fault != FAULT_TMOUT) return keeping;
|
||||
|
||||
}
|
||||
|
||||
#ifndef SIMPLE_FILES
|
||||
|
||||
fn = alloc_printf("%s/hangs/id:%06llu,%s", out_dir, unique_hangs,
|
||||
describe_op(0));
|
||||
|
||||
#else
|
||||
|
||||
fn = alloc_printf("%s/hangs/id_%06llu", out_dir, unique_hangs);
|
||||
|
||||
#endif /* ^!SIMPLE_FILES */
|
||||
|
||||
++unique_hangs;
|
||||
|
||||
last_hang_time = get_cur_time();
|
||||
|
||||
break;
|
||||
|
||||
case FAULT_CRASH:
|
||||
|
||||
keep_as_crash:
|
||||
|
||||
/* This is handled in a manner roughly similar to timeouts,
|
||||
except for slightly different limits and no need to re-run test
|
||||
cases. */
|
||||
|
||||
++total_crashes;
|
||||
|
||||
if (unique_crashes >= KEEP_UNIQUE_CRASH) return keeping;
|
||||
|
||||
if (!dumb_mode) {
|
||||
|
||||
#ifdef __x86_64__
|
||||
simplify_trace((u64*)trace_bits);
|
||||
#else
|
||||
simplify_trace((u32*)trace_bits);
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
if (!has_new_bits(virgin_crash)) return keeping;
|
||||
|
||||
}
|
||||
|
||||
if (!unique_crashes) write_crash_readme();
|
||||
|
||||
#ifndef SIMPLE_FILES
|
||||
|
||||
fn = alloc_printf("%s/crashes/id:%06llu,sig:%02u,%s", out_dir,
|
||||
unique_crashes, kill_signal, describe_op(0));
|
||||
|
||||
#else
|
||||
|
||||
fn = alloc_printf("%s/crashes/id_%06llu_%02u", out_dir, unique_crashes,
|
||||
kill_signal);
|
||||
|
||||
#endif /* ^!SIMPLE_FILES */
|
||||
|
||||
++unique_crashes;
|
||||
|
||||
last_crash_time = get_cur_time();
|
||||
last_crash_execs = total_execs;
|
||||
|
||||
break;
|
||||
|
||||
case FAULT_ERROR: FATAL("Unable to execute target application");
|
||||
|
||||
default: return keeping;
|
||||
|
||||
}
|
||||
|
||||
/* If we're here, we apparently want to save the crash or hang
|
||||
test case, too. */
|
||||
|
||||
fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", fn);
|
||||
ck_write(fd, mem, len, fn);
|
||||
close(fd);
|
||||
|
||||
ck_free(fn);
|
||||
|
||||
return keeping;
|
||||
|
||||
}
|
||||
|
488
src/afl-fuzz-extras.c
Normal file
488
src/afl-fuzz-extras.c
Normal file
@ -0,0 +1,488 @@
|
||||
/*
|
||||
american fuzzy lop++ - extras relates routines
|
||||
----------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This is the real deal: the program takes an instrumented binary and
|
||||
attempts a variety of basic fuzzing tricks, paying close attention to
|
||||
how they affect the execution path.
|
||||
|
||||
*/
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* Helper function for load_extras. */
|
||||
|
||||
static int compare_extras_len(const void* p1, const void* p2) {
|
||||
|
||||
struct extra_data *e1 = (struct extra_data*)p1, *e2 = (struct extra_data*)p2;
|
||||
|
||||
return e1->len - e2->len;
|
||||
|
||||
}
|
||||
|
||||
static int compare_extras_use_d(const void* p1, const void* p2) {
|
||||
|
||||
struct extra_data *e1 = (struct extra_data*)p1, *e2 = (struct extra_data*)p2;
|
||||
|
||||
return e2->hit_cnt - e1->hit_cnt;
|
||||
|
||||
}
|
||||
|
||||
/* Read extras from a file, sort by size. */
|
||||
|
||||
void load_extras_file(u8* fname, u32* min_len, u32* max_len, u32 dict_level) {
|
||||
|
||||
FILE* f;
|
||||
u8 buf[MAX_LINE];
|
||||
u8* lptr;
|
||||
u32 cur_line = 0;
|
||||
|
||||
f = fopen(fname, "r");
|
||||
|
||||
if (!f) PFATAL("Unable to open '%s'", fname);
|
||||
|
||||
while ((lptr = fgets(buf, MAX_LINE, f))) {
|
||||
|
||||
u8 *rptr, *wptr;
|
||||
u32 klen = 0;
|
||||
|
||||
++cur_line;
|
||||
|
||||
/* Trim on left and right. */
|
||||
|
||||
while (isspace(*lptr))
|
||||
++lptr;
|
||||
|
||||
rptr = lptr + strlen(lptr) - 1;
|
||||
while (rptr >= lptr && isspace(*rptr))
|
||||
--rptr;
|
||||
++rptr;
|
||||
*rptr = 0;
|
||||
|
||||
/* Skip empty lines and comments. */
|
||||
|
||||
if (!*lptr || *lptr == '#') continue;
|
||||
|
||||
/* All other lines must end with '"', which we can consume. */
|
||||
|
||||
--rptr;
|
||||
|
||||
if (rptr < lptr || *rptr != '"')
|
||||
FATAL("Malformed name=\"value\" pair in line %u.", cur_line);
|
||||
|
||||
*rptr = 0;
|
||||
|
||||
/* Skip alphanumerics and dashes (label). */
|
||||
|
||||
while (isalnum(*lptr) || *lptr == '_')
|
||||
++lptr;
|
||||
|
||||
/* If @number follows, parse that. */
|
||||
|
||||
if (*lptr == '@') {
|
||||
|
||||
++lptr;
|
||||
if (atoi(lptr) > dict_level) continue;
|
||||
while (isdigit(*lptr))
|
||||
++lptr;
|
||||
|
||||
}
|
||||
|
||||
/* Skip whitespace and = signs. */
|
||||
|
||||
while (isspace(*lptr) || *lptr == '=')
|
||||
++lptr;
|
||||
|
||||
/* Consume opening '"'. */
|
||||
|
||||
if (*lptr != '"')
|
||||
FATAL("Malformed name=\"keyword\" pair in line %u.", cur_line);
|
||||
|
||||
++lptr;
|
||||
|
||||
if (!*lptr) FATAL("Empty keyword in line %u.", cur_line);
|
||||
|
||||
/* Okay, let's allocate memory and copy data between "...", handling
|
||||
\xNN escaping, \\, and \". */
|
||||
|
||||
extras =
|
||||
ck_realloc_block(extras, (extras_cnt + 1) * sizeof(struct extra_data));
|
||||
|
||||
wptr = extras[extras_cnt].data = ck_alloc(rptr - lptr);
|
||||
|
||||
while (*lptr) {
|
||||
|
||||
char* hexdigits = "0123456789abcdef";
|
||||
|
||||
switch (*lptr) {
|
||||
|
||||
case 1 ... 31:
|
||||
case 128 ... 255:
|
||||
FATAL("Non-printable characters in line %u.", cur_line);
|
||||
|
||||
case '\\':
|
||||
|
||||
++lptr;
|
||||
|
||||
if (*lptr == '\\' || *lptr == '"') {
|
||||
|
||||
*(wptr++) = *(lptr++);
|
||||
klen++;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (*lptr != 'x' || !isxdigit(lptr[1]) || !isxdigit(lptr[2]))
|
||||
FATAL("Invalid escaping (not \\xNN) in line %u.", cur_line);
|
||||
|
||||
*(wptr++) = ((strchr(hexdigits, tolower(lptr[1])) - hexdigits) << 4) |
|
||||
(strchr(hexdigits, tolower(lptr[2])) - hexdigits);
|
||||
|
||||
lptr += 3;
|
||||
++klen;
|
||||
|
||||
break;
|
||||
|
||||
default: *(wptr++) = *(lptr++); ++klen;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extras[extras_cnt].len = klen;
|
||||
|
||||
if (extras[extras_cnt].len > MAX_DICT_FILE)
|
||||
FATAL("Keyword too big in line %u (%s, limit is %s)", cur_line, DMS(klen),
|
||||
DMS(MAX_DICT_FILE));
|
||||
|
||||
if (*min_len > klen) *min_len = klen;
|
||||
if (*max_len < klen) *max_len = klen;
|
||||
|
||||
++extras_cnt;
|
||||
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
|
||||
/* Read extras from the extras directory and sort them by size. */
|
||||
|
||||
void load_extras(u8* dir) {
|
||||
|
||||
DIR* d;
|
||||
struct dirent* de;
|
||||
u32 min_len = MAX_DICT_FILE, max_len = 0, dict_level = 0;
|
||||
u8* x;
|
||||
|
||||
/* If the name ends with @, extract level and continue. */
|
||||
|
||||
if ((x = strchr(dir, '@'))) {
|
||||
|
||||
*x = 0;
|
||||
dict_level = atoi(x + 1);
|
||||
|
||||
}
|
||||
|
||||
ACTF("Loading extra dictionary from '%s' (level %u)...", dir, dict_level);
|
||||
|
||||
d = opendir(dir);
|
||||
|
||||
if (!d) {
|
||||
|
||||
if (errno == ENOTDIR) {
|
||||
|
||||
load_extras_file(dir, &min_len, &max_len, dict_level);
|
||||
goto check_and_sort;
|
||||
|
||||
}
|
||||
|
||||
PFATAL("Unable to open '%s'", dir);
|
||||
|
||||
}
|
||||
|
||||
if (x) FATAL("Dictionary levels not supported for directories.");
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
|
||||
struct stat st;
|
||||
u8* fn = alloc_printf("%s/%s", dir, de->d_name);
|
||||
s32 fd;
|
||||
|
||||
if (lstat(fn, &st) || access(fn, R_OK)) PFATAL("Unable to access '%s'", fn);
|
||||
|
||||
/* This also takes care of . and .. */
|
||||
if (!S_ISREG(st.st_mode) || !st.st_size) {
|
||||
|
||||
ck_free(fn);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (st.st_size > MAX_DICT_FILE)
|
||||
FATAL("Extra '%s' is too big (%s, limit is %s)", fn, DMS(st.st_size),
|
||||
DMS(MAX_DICT_FILE));
|
||||
|
||||
if (min_len > st.st_size) min_len = st.st_size;
|
||||
if (max_len < st.st_size) max_len = st.st_size;
|
||||
|
||||
extras =
|
||||
ck_realloc_block(extras, (extras_cnt + 1) * sizeof(struct extra_data));
|
||||
|
||||
extras[extras_cnt].data = ck_alloc(st.st_size);
|
||||
extras[extras_cnt].len = st.st_size;
|
||||
|
||||
fd = open(fn, O_RDONLY);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to open '%s'", fn);
|
||||
|
||||
ck_read(fd, extras[extras_cnt].data, st.st_size, fn);
|
||||
|
||||
close(fd);
|
||||
ck_free(fn);
|
||||
|
||||
++extras_cnt;
|
||||
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
|
||||
check_and_sort:
|
||||
|
||||
if (!extras_cnt) FATAL("No usable files in '%s'", dir);
|
||||
|
||||
qsort(extras, extras_cnt, sizeof(struct extra_data), compare_extras_len);
|
||||
|
||||
OKF("Loaded %u extra tokens, size range %s to %s.", extras_cnt, DMS(min_len),
|
||||
DMS(max_len));
|
||||
|
||||
if (max_len > 32)
|
||||
WARNF("Some tokens are relatively large (%s) - consider trimming.",
|
||||
DMS(max_len));
|
||||
|
||||
if (extras_cnt > MAX_DET_EXTRAS)
|
||||
WARNF("More than %d tokens - will use them probabilistically.",
|
||||
MAX_DET_EXTRAS);
|
||||
|
||||
}
|
||||
|
||||
/* Helper function for maybe_add_auto() */
|
||||
|
||||
static inline u8 memcmp_nocase(u8* m1, u8* m2, u32 len) {
|
||||
|
||||
while (len--)
|
||||
if (tolower(*(m1++)) ^ tolower(*(m2++))) return 1;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* Maybe add automatic extra. */
|
||||
|
||||
void maybe_add_auto(u8* mem, u32 len) {
|
||||
|
||||
u32 i;
|
||||
|
||||
/* Allow users to specify that they don't want auto dictionaries. */
|
||||
|
||||
if (!MAX_AUTO_EXTRAS || !USE_AUTO_EXTRAS) return;
|
||||
|
||||
/* Skip runs of identical bytes. */
|
||||
|
||||
for (i = 1; i < len; ++i)
|
||||
if (mem[0] ^ mem[i]) break;
|
||||
|
||||
if (i == len) return;
|
||||
|
||||
/* Reject builtin interesting values. */
|
||||
|
||||
if (len == 2) {
|
||||
|
||||
i = sizeof(interesting_16) >> 1;
|
||||
|
||||
while (i--)
|
||||
if (*((u16*)mem) == interesting_16[i] ||
|
||||
*((u16*)mem) == SWAP16(interesting_16[i]))
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (len == 4) {
|
||||
|
||||
i = sizeof(interesting_32) >> 2;
|
||||
|
||||
while (i--)
|
||||
if (*((u32*)mem) == interesting_32[i] ||
|
||||
*((u32*)mem) == SWAP32(interesting_32[i]))
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/* Reject anything that matches existing extras. Do a case-insensitive
|
||||
match. We optimize by exploiting the fact that extras[] are sorted
|
||||
by size. */
|
||||
|
||||
for (i = 0; i < extras_cnt; ++i)
|
||||
if (extras[i].len >= len) break;
|
||||
|
||||
for (; i < extras_cnt && extras[i].len == len; ++i)
|
||||
if (!memcmp_nocase(extras[i].data, mem, len)) return;
|
||||
|
||||
/* Last but not least, check a_extras[] for matches. There are no
|
||||
guarantees of a particular sort order. */
|
||||
|
||||
auto_changed = 1;
|
||||
|
||||
for (i = 0; i < a_extras_cnt; ++i) {
|
||||
|
||||
if (a_extras[i].len == len && !memcmp_nocase(a_extras[i].data, mem, len)) {
|
||||
|
||||
a_extras[i].hit_cnt++;
|
||||
goto sort_a_extras;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* At this point, looks like we're dealing with a new entry. So, let's
|
||||
append it if we have room. Otherwise, let's randomly evict some other
|
||||
entry from the bottom half of the list. */
|
||||
|
||||
if (a_extras_cnt < MAX_AUTO_EXTRAS) {
|
||||
|
||||
a_extras = ck_realloc_block(a_extras,
|
||||
(a_extras_cnt + 1) * sizeof(struct extra_data));
|
||||
|
||||
a_extras[a_extras_cnt].data = ck_memdup(mem, len);
|
||||
a_extras[a_extras_cnt].len = len;
|
||||
++a_extras_cnt;
|
||||
|
||||
} else {
|
||||
|
||||
i = MAX_AUTO_EXTRAS / 2 + UR((MAX_AUTO_EXTRAS + 1) / 2);
|
||||
|
||||
ck_free(a_extras[i].data);
|
||||
|
||||
a_extras[i].data = ck_memdup(mem, len);
|
||||
a_extras[i].len = len;
|
||||
a_extras[i].hit_cnt = 0;
|
||||
|
||||
}
|
||||
|
||||
sort_a_extras:
|
||||
|
||||
/* First, sort all auto extras by use count, descending order. */
|
||||
|
||||
qsort(a_extras, a_extras_cnt, sizeof(struct extra_data),
|
||||
compare_extras_use_d);
|
||||
|
||||
/* Then, sort the top USE_AUTO_EXTRAS entries by size. */
|
||||
|
||||
qsort(a_extras, MIN(USE_AUTO_EXTRAS, a_extras_cnt), sizeof(struct extra_data),
|
||||
compare_extras_len);
|
||||
|
||||
}
|
||||
|
||||
/* Save automatically generated extras. */
|
||||
|
||||
void save_auto(void) {
|
||||
|
||||
u32 i;
|
||||
|
||||
if (!auto_changed) return;
|
||||
auto_changed = 0;
|
||||
|
||||
for (i = 0; i < MIN(USE_AUTO_EXTRAS, a_extras_cnt); ++i) {
|
||||
|
||||
u8* fn = alloc_printf("%s/queue/.state/auto_extras/auto_%06u", out_dir, i);
|
||||
s32 fd;
|
||||
|
||||
fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", fn);
|
||||
|
||||
ck_write(fd, a_extras[i].data, a_extras[i].len, fn);
|
||||
|
||||
close(fd);
|
||||
ck_free(fn);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Load automatically generated extras. */
|
||||
|
||||
void load_auto(void) {
|
||||
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < USE_AUTO_EXTRAS; ++i) {
|
||||
|
||||
u8 tmp[MAX_AUTO_EXTRA + 1];
|
||||
u8* fn = alloc_printf("%s/.state/auto_extras/auto_%06u", in_dir, i);
|
||||
s32 fd, len;
|
||||
|
||||
fd = open(fn, O_RDONLY, 0600);
|
||||
|
||||
if (fd < 0) {
|
||||
|
||||
if (errno != ENOENT) PFATAL("Unable to open '%s'", fn);
|
||||
ck_free(fn);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
/* We read one byte more to cheaply detect tokens that are too
|
||||
long (and skip them). */
|
||||
|
||||
len = read(fd, tmp, MAX_AUTO_EXTRA + 1);
|
||||
|
||||
if (len < 0) PFATAL("Unable to read from '%s'", fn);
|
||||
|
||||
if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA)
|
||||
maybe_add_auto(tmp, len);
|
||||
|
||||
close(fd);
|
||||
ck_free(fn);
|
||||
|
||||
}
|
||||
|
||||
if (i)
|
||||
OKF("Loaded %u auto-discovered dictionary tokens.", i);
|
||||
else
|
||||
OKF("No auto-generated dictionary tokens to reuse.");
|
||||
|
||||
}
|
||||
|
||||
/* Destroy extras. */
|
||||
|
||||
void destroy_extras(void) {
|
||||
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < extras_cnt; ++i)
|
||||
ck_free(extras[i].data);
|
||||
|
||||
ck_free(extras);
|
||||
|
||||
for (i = 0; i < a_extras_cnt; ++i)
|
||||
ck_free(a_extras[i].data);
|
||||
|
||||
ck_free(a_extras);
|
||||
|
||||
}
|
||||
|
260
src/afl-fuzz-globals.c
Normal file
260
src/afl-fuzz-globals.c
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
american fuzzy lop++ - globals declarations
|
||||
-------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This is the real deal: the program takes an instrumented binary and
|
||||
attempts a variety of basic fuzzing tricks, paying close attention to
|
||||
how they affect the execution path.
|
||||
|
||||
*/
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* MOpt:
|
||||
Lots of globals, but mostly for the status UI and other things where it
|
||||
really makes no sense to haul them around as function parameters. */
|
||||
u64 limit_time_puppet, orig_hit_cnt_puppet, last_limit_time_start,
|
||||
tmp_pilot_time, total_pacemaker_time, total_puppet_find, temp_puppet_find,
|
||||
most_time_key, most_time, most_execs_key, most_execs, old_hit_count;
|
||||
|
||||
s32 SPLICE_CYCLES_puppet, limit_time_sig, key_puppet, key_module;
|
||||
|
||||
double w_init = 0.9, w_end = 0.3, w_now;
|
||||
|
||||
s32 g_now;
|
||||
s32 g_max = 5000;
|
||||
|
||||
u64 tmp_core_time;
|
||||
s32 swarm_now;
|
||||
|
||||
double x_now[swarm_num][operator_num], L_best[swarm_num][operator_num],
|
||||
eff_best[swarm_num][operator_num], G_best[operator_num],
|
||||
v_now[swarm_num][operator_num], probability_now[swarm_num][operator_num],
|
||||
swarm_fitness[swarm_num];
|
||||
|
||||
u64 stage_finds_puppet[swarm_num]
|
||||
[operator_num], /* Patterns found per fuzz stage */
|
||||
stage_finds_puppet_v2[swarm_num][operator_num],
|
||||
stage_cycles_puppet_v2[swarm_num][operator_num],
|
||||
stage_cycles_puppet_v3[swarm_num][operator_num],
|
||||
stage_cycles_puppet[swarm_num][operator_num],
|
||||
operator_finds_puppet[operator_num],
|
||||
core_operator_finds_puppet[operator_num],
|
||||
core_operator_finds_puppet_v2[operator_num],
|
||||
core_operator_cycles_puppet[operator_num],
|
||||
core_operator_cycles_puppet_v2[operator_num],
|
||||
core_operator_cycles_puppet_v3[operator_num]; /* Execs per fuzz stage */
|
||||
|
||||
double period_pilot_tmp = 5000.0;
|
||||
s32 key_lv;
|
||||
|
||||
u8 *in_dir, /* Input directory with test cases */
|
||||
*out_dir, /* Working & output directory */
|
||||
*tmp_dir, /* Temporary directory for input */
|
||||
*sync_dir, /* Synchronization directory */
|
||||
*sync_id, /* Fuzzer ID */
|
||||
*power_name, /* Power schedule name */
|
||||
*use_banner, /* Display banner */
|
||||
*in_bitmap, /* Input bitmap */
|
||||
*file_extension, /* File extension */
|
||||
*orig_cmdline; /* Original command line */
|
||||
u8 *doc_path, /* Path to documentation dir */
|
||||
*target_path, /* Path to target binary */
|
||||
*out_file; /* File to fuzz, if any */
|
||||
|
||||
u32 exec_tmout = EXEC_TIMEOUT; /* Configurable exec timeout (ms) */
|
||||
u32 hang_tmout = EXEC_TIMEOUT; /* Timeout used for hang det (ms) */
|
||||
|
||||
u64 mem_limit = MEM_LIMIT; /* Memory cap for child (MB) */
|
||||
|
||||
u8 cal_cycles = CAL_CYCLES, /* Calibration cycles defaults */
|
||||
cal_cycles_long = CAL_CYCLES_LONG, debug, /* Debug mode */
|
||||
python_only; /* Python-only mode */
|
||||
|
||||
u32 stats_update_freq = 1; /* Stats update frequency (execs) */
|
||||
|
||||
char *power_names[POWER_SCHEDULES_NUM] = {"explore", "fast", "coe",
|
||||
"lin", "quad", "exploit"};
|
||||
|
||||
u8 schedule = EXPLORE; /* Power schedule (default: EXPLORE)*/
|
||||
u8 havoc_max_mult = HAVOC_MAX_MULT;
|
||||
|
||||
u8 skip_deterministic, /* Skip deterministic stages? */
|
||||
force_deterministic, /* Force deterministic stages? */
|
||||
use_splicing, /* Recombine input files? */
|
||||
dumb_mode, /* Run in non-instrumented mode? */
|
||||
score_changed, /* Scoring for favorites changed? */
|
||||
kill_signal, /* Signal that killed the child */
|
||||
resuming_fuzz, /* Resuming an older fuzzing job? */
|
||||
timeout_given, /* Specific timeout given? */
|
||||
not_on_tty, /* stdout is not a tty */
|
||||
term_too_small, /* terminal dimensions too small */
|
||||
no_forkserver, /* Disable forkserver? */
|
||||
crash_mode, /* Crash mode! Yeah! */
|
||||
in_place_resume, /* Attempt in-place resume? */
|
||||
auto_changed, /* Auto-generated tokens changed? */
|
||||
no_cpu_meter_red, /* Feng shui on the status screen */
|
||||
no_arith, /* Skip most arithmetic ops */
|
||||
shuffle_queue, /* Shuffle input queue? */
|
||||
bitmap_changed = 1, /* Time to update bitmap? */
|
||||
qemu_mode, /* Running in QEMU mode? */
|
||||
unicorn_mode, /* Running in Unicorn mode? */
|
||||
skip_requested, /* Skip request, via SIGUSR1 */
|
||||
run_over10m, /* Run time over 10 minutes? */
|
||||
persistent_mode, /* Running in persistent mode? */
|
||||
deferred_mode, /* Deferred forkserver mode? */
|
||||
fixed_seed, /* do not reseed */
|
||||
fast_cal, /* Try to calibrate faster? */
|
||||
uses_asan; /* Target uses ASAN? */
|
||||
|
||||
s32 out_fd, /* Persistent fd for out_file */
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
dev_urandom_fd = -1, /* Persistent fd for /dev/urandom */
|
||||
#endif
|
||||
dev_null_fd = -1, /* Persistent fd for /dev/null */
|
||||
fsrv_ctl_fd, /* Fork server control pipe (write) */
|
||||
fsrv_st_fd; /* Fork server status pipe (read) */
|
||||
|
||||
s32 forksrv_pid, /* PID of the fork server */
|
||||
child_pid = -1, /* PID of the fuzzed program */
|
||||
out_dir_fd = -1; /* FD of the lock file */
|
||||
|
||||
u8 *trace_bits; /* SHM with instrumentation bitmap */
|
||||
|
||||
u8 virgin_bits[MAP_SIZE], /* Regions yet untouched by fuzzing */
|
||||
virgin_tmout[MAP_SIZE], /* Bits we haven't seen in tmouts */
|
||||
virgin_crash[MAP_SIZE]; /* Bits we haven't seen in crashes */
|
||||
|
||||
u8 var_bytes[MAP_SIZE]; /* Bytes that appear to be variable */
|
||||
|
||||
volatile u8 stop_soon, /* Ctrl-C pressed? */
|
||||
clear_screen = 1, /* Window resized? */
|
||||
child_timed_out; /* Traced process timed out? */
|
||||
|
||||
u32 queued_paths, /* Total number of queued testcases */
|
||||
queued_variable, /* Testcases with variable behavior */
|
||||
queued_at_start, /* Total number of initial inputs */
|
||||
queued_discovered, /* Items discovered during this run */
|
||||
queued_imported, /* Items imported via -S */
|
||||
queued_favored, /* Paths deemed favorable */
|
||||
queued_with_cov, /* Paths with new coverage bytes */
|
||||
pending_not_fuzzed, /* Queued but not done yet */
|
||||
pending_favored, /* Pending favored paths */
|
||||
cur_skipped_paths, /* Abandoned inputs in cur cycle */
|
||||
cur_depth, /* Current path depth */
|
||||
max_depth, /* Max path depth */
|
||||
useless_at_start, /* Number of useless starting paths */
|
||||
var_byte_count, /* Bitmap bytes with var behavior */
|
||||
current_entry, /* Current queue entry ID */
|
||||
havoc_div = 1; /* Cycle count divisor for havoc */
|
||||
|
||||
u64 total_crashes, /* Total number of crashes */
|
||||
unique_crashes, /* Crashes with unique signatures */
|
||||
total_tmouts, /* Total number of timeouts */
|
||||
unique_tmouts, /* Timeouts with unique signatures */
|
||||
unique_hangs, /* Hangs with unique signatures */
|
||||
total_execs, /* Total execve() calls */
|
||||
slowest_exec_ms, /* Slowest testcase non hang in ms */
|
||||
start_time, /* Unix start time (ms) */
|
||||
last_path_time, /* Time for most recent path (ms) */
|
||||
last_crash_time, /* Time for most recent crash (ms) */
|
||||
last_hang_time, /* Time for most recent hang (ms) */
|
||||
last_crash_execs, /* Exec counter at last crash */
|
||||
queue_cycle, /* Queue round counter */
|
||||
cycles_wo_finds, /* Cycles without any new paths */
|
||||
trim_execs, /* Execs done to trim input files */
|
||||
bytes_trim_in, /* Bytes coming into the trimmer */
|
||||
bytes_trim_out, /* Bytes coming outa the trimmer */
|
||||
blocks_eff_total, /* Blocks subject to effector maps */
|
||||
blocks_eff_select; /* Blocks selected as fuzzable */
|
||||
|
||||
u32 subseq_tmouts; /* Number of timeouts in a row */
|
||||
|
||||
u8 *stage_name = "init", /* Name of the current fuzz stage */
|
||||
*stage_short, /* Short stage name */
|
||||
*syncing_party; /* Currently syncing with... */
|
||||
|
||||
s32 stage_cur, stage_max; /* Stage progression */
|
||||
s32 splicing_with = -1; /* Splicing with which test case? */
|
||||
|
||||
u32 master_id, master_max; /* Master instance job splitting */
|
||||
|
||||
u32 syncing_case; /* Syncing with case #... */
|
||||
|
||||
s32 stage_cur_byte, /* Byte offset of current stage op */
|
||||
stage_cur_val; /* Value used for stage op */
|
||||
|
||||
u8 stage_val_type; /* Value type (STAGE_VAL_*) */
|
||||
|
||||
u64 stage_finds[32], /* Patterns found per fuzz stage */
|
||||
stage_cycles[32]; /* Execs per fuzz stage */
|
||||
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
u32 rand_cnt; /* Random number counter */
|
||||
#endif
|
||||
|
||||
u64 total_cal_us, /* Total calibration time (us) */
|
||||
total_cal_cycles; /* Total calibration cycles */
|
||||
|
||||
u64 total_bitmap_size, /* Total bit count for all bitmaps */
|
||||
total_bitmap_entries; /* Number of bitmaps counted */
|
||||
|
||||
s32 cpu_core_count; /* CPU core count */
|
||||
|
||||
#ifdef HAVE_AFFINITY
|
||||
|
||||
s32 cpu_aff = -1; /* Selected CPU core */
|
||||
|
||||
#endif /* HAVE_AFFINITY */
|
||||
|
||||
FILE *plot_file; /* Gnuplot output file */
|
||||
|
||||
struct queue_entry *queue, /* Fuzzing queue (linked list) */
|
||||
*queue_cur, /* Current offset within the queue */
|
||||
*queue_top, /* Top of the list */
|
||||
*q_prev100; /* Previous 100 marker */
|
||||
|
||||
struct queue_entry *top_rated[MAP_SIZE]; /* Top entries for bitmap bytes */
|
||||
|
||||
struct extra_data *extras; /* Extra tokens to fuzz with */
|
||||
u32 extras_cnt; /* Total number of tokens read */
|
||||
|
||||
struct extra_data *a_extras; /* Automatically selected extras */
|
||||
u32 a_extras_cnt; /* Total number of tokens available */
|
||||
|
||||
u8 *(*post_handler)(u8 *buf, u32 *len);
|
||||
|
||||
/* hooks for the custom mutator function */
|
||||
size_t (*custom_mutator)(u8 *data, size_t size, u8 *mutated_out,
|
||||
size_t max_size, unsigned int seed);
|
||||
size_t (*pre_save_handler)(u8 *data, size_t size, u8 **new_data);
|
||||
|
||||
/* Interesting values, as per config.h */
|
||||
|
||||
s8 interesting_8[] = {INTERESTING_8};
|
||||
s16 interesting_16[] = {INTERESTING_8, INTERESTING_16};
|
||||
s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32};
|
||||
|
||||
/* Python stuff */
|
||||
#ifdef USE_PYTHON
|
||||
|
||||
PyObject *py_module;
|
||||
PyObject *py_functions[PY_FUNC_COUNT];
|
||||
|
||||
#endif
|
||||
|
2067
src/afl-fuzz-init.c
Normal file
2067
src/afl-fuzz-init.c
Normal file
File diff suppressed because it is too large
Load Diff
186
src/afl-fuzz-misc.c
Normal file
186
src/afl-fuzz-misc.c
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
american fuzzy lop++ - misc stuffs from Mordor
|
||||
----------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This is the real deal: the program takes an instrumented binary and
|
||||
attempts a variety of basic fuzzing tricks, paying close attention to
|
||||
how they affect the execution path.
|
||||
|
||||
*/
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* Describe integer. Uses 12 cyclic static buffers for return values. The value
|
||||
returned should be five characters or less for all the integers we reasonably
|
||||
expect to see. */
|
||||
|
||||
u8* DI(u64 val) {
|
||||
|
||||
static u8 tmp[12][16];
|
||||
static u8 cur;
|
||||
|
||||
cur = (cur + 1) % 12;
|
||||
|
||||
#define CHK_FORMAT(_divisor, _limit_mult, _fmt, _cast) \
|
||||
do { \
|
||||
\
|
||||
if (val < (_divisor) * (_limit_mult)) { \
|
||||
\
|
||||
sprintf(tmp[cur], _fmt, ((_cast)val) / (_divisor)); \
|
||||
return tmp[cur]; \
|
||||
\
|
||||
} \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* 0-9999 */
|
||||
CHK_FORMAT(1, 10000, "%llu", u64);
|
||||
|
||||
/* 10.0k - 99.9k */
|
||||
CHK_FORMAT(1000, 99.95, "%0.01fk", double);
|
||||
|
||||
/* 100k - 999k */
|
||||
CHK_FORMAT(1000, 1000, "%lluk", u64);
|
||||
|
||||
/* 1.00M - 9.99M */
|
||||
CHK_FORMAT(1000 * 1000, 9.995, "%0.02fM", double);
|
||||
|
||||
/* 10.0M - 99.9M */
|
||||
CHK_FORMAT(1000 * 1000, 99.95, "%0.01fM", double);
|
||||
|
||||
/* 100M - 999M */
|
||||
CHK_FORMAT(1000 * 1000, 1000, "%lluM", u64);
|
||||
|
||||
/* 1.00G - 9.99G */
|
||||
CHK_FORMAT(1000LL * 1000 * 1000, 9.995, "%0.02fG", double);
|
||||
|
||||
/* 10.0G - 99.9G */
|
||||
CHK_FORMAT(1000LL * 1000 * 1000, 99.95, "%0.01fG", double);
|
||||
|
||||
/* 100G - 999G */
|
||||
CHK_FORMAT(1000LL * 1000 * 1000, 1000, "%lluG", u64);
|
||||
|
||||
/* 1.00T - 9.99G */
|
||||
CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 9.995, "%0.02fT", double);
|
||||
|
||||
/* 10.0T - 99.9T */
|
||||
CHK_FORMAT(1000LL * 1000 * 1000 * 1000, 99.95, "%0.01fT", double);
|
||||
|
||||
/* 100T+ */
|
||||
strcpy(tmp[cur], "infty");
|
||||
return tmp[cur];
|
||||
|
||||
}
|
||||
|
||||
/* Describe float. Similar to the above, except with a single
|
||||
static buffer. */
|
||||
|
||||
u8* DF(double val) {
|
||||
|
||||
static u8 tmp[16];
|
||||
|
||||
if (val < 99.995) {
|
||||
|
||||
sprintf(tmp, "%0.02f", val);
|
||||
return tmp;
|
||||
|
||||
}
|
||||
|
||||
if (val < 999.95) {
|
||||
|
||||
sprintf(tmp, "%0.01f", val);
|
||||
return tmp;
|
||||
|
||||
}
|
||||
|
||||
return DI((u64)val);
|
||||
|
||||
}
|
||||
|
||||
/* Describe integer as memory size. */
|
||||
|
||||
u8* DMS(u64 val) {
|
||||
|
||||
static u8 tmp[12][16];
|
||||
static u8 cur;
|
||||
|
||||
cur = (cur + 1) % 12;
|
||||
|
||||
/* 0-9999 */
|
||||
CHK_FORMAT(1, 10000, "%llu B", u64);
|
||||
|
||||
/* 10.0k - 99.9k */
|
||||
CHK_FORMAT(1024, 99.95, "%0.01f kB", double);
|
||||
|
||||
/* 100k - 999k */
|
||||
CHK_FORMAT(1024, 1000, "%llu kB", u64);
|
||||
|
||||
/* 1.00M - 9.99M */
|
||||
CHK_FORMAT(1024 * 1024, 9.995, "%0.02f MB", double);
|
||||
|
||||
/* 10.0M - 99.9M */
|
||||
CHK_FORMAT(1024 * 1024, 99.95, "%0.01f MB", double);
|
||||
|
||||
/* 100M - 999M */
|
||||
CHK_FORMAT(1024 * 1024, 1000, "%llu MB", u64);
|
||||
|
||||
/* 1.00G - 9.99G */
|
||||
CHK_FORMAT(1024LL * 1024 * 1024, 9.995, "%0.02f GB", double);
|
||||
|
||||
/* 10.0G - 99.9G */
|
||||
CHK_FORMAT(1024LL * 1024 * 1024, 99.95, "%0.01f GB", double);
|
||||
|
||||
/* 100G - 999G */
|
||||
CHK_FORMAT(1024LL * 1024 * 1024, 1000, "%llu GB", u64);
|
||||
|
||||
/* 1.00T - 9.99G */
|
||||
CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 9.995, "%0.02f TB", double);
|
||||
|
||||
/* 10.0T - 99.9T */
|
||||
CHK_FORMAT(1024LL * 1024 * 1024 * 1024, 99.95, "%0.01f TB", double);
|
||||
|
||||
#undef CHK_FORMAT
|
||||
|
||||
/* 100T+ */
|
||||
strcpy(tmp[cur], "infty");
|
||||
return tmp[cur];
|
||||
|
||||
}
|
||||
|
||||
/* Describe time delta. Returns one static buffer, 34 chars of less. */
|
||||
|
||||
u8* DTD(u64 cur_ms, u64 event_ms) {
|
||||
|
||||
static u8 tmp[64];
|
||||
u64 delta;
|
||||
s32 t_d, t_h, t_m, t_s;
|
||||
|
||||
if (!event_ms) return "none seen yet";
|
||||
|
||||
delta = cur_ms - event_ms;
|
||||
|
||||
t_d = delta / 1000 / 60 / 60 / 24;
|
||||
t_h = (delta / 1000 / 60 / 60) % 24;
|
||||
t_m = (delta / 1000 / 60) % 60;
|
||||
t_s = (delta / 1000) % 60;
|
||||
|
||||
sprintf(tmp, "%s days, %d hrs, %d min, %d sec", DI(t_d), t_h, t_m, t_s);
|
||||
return tmp;
|
||||
|
||||
}
|
||||
|
6027
src/afl-fuzz-one.c
Normal file
6027
src/afl-fuzz-one.c
Normal file
File diff suppressed because it is too large
Load Diff
405
src/afl-fuzz-python.c
Normal file
405
src/afl-fuzz-python.c
Normal file
@ -0,0 +1,405 @@
|
||||
/*
|
||||
american fuzzy lop++ - python extension routines
|
||||
------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This is the real deal: the program takes an instrumented binary and
|
||||
attempts a variety of basic fuzzing tricks, paying close attention to
|
||||
how they affect the execution path.
|
||||
|
||||
*/
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* Python stuff */
|
||||
#ifdef USE_PYTHON
|
||||
|
||||
int init_py() {
|
||||
|
||||
Py_Initialize();
|
||||
u8* module_name = getenv("AFL_PYTHON_MODULE");
|
||||
|
||||
if (module_name) {
|
||||
|
||||
PyObject* py_name = PyString_FromString(module_name);
|
||||
|
||||
py_module = PyImport_Import(py_name);
|
||||
Py_DECREF(py_name);
|
||||
|
||||
if (py_module != NULL) {
|
||||
|
||||
u8 py_notrim = 0;
|
||||
py_functions[PY_FUNC_INIT] = PyObject_GetAttrString(py_module, "init");
|
||||
py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz");
|
||||
py_functions[PY_FUNC_INIT_TRIM] =
|
||||
PyObject_GetAttrString(py_module, "init_trim");
|
||||
py_functions[PY_FUNC_POST_TRIM] =
|
||||
PyObject_GetAttrString(py_module, "post_trim");
|
||||
py_functions[PY_FUNC_TRIM] = PyObject_GetAttrString(py_module, "trim");
|
||||
|
||||
for (u8 py_idx = 0; py_idx < PY_FUNC_COUNT; ++py_idx) {
|
||||
|
||||
if (!py_functions[py_idx] || !PyCallable_Check(py_functions[py_idx])) {
|
||||
|
||||
if (py_idx >= PY_FUNC_INIT_TRIM && py_idx <= PY_FUNC_TRIM) {
|
||||
|
||||
// Implementing the trim API is optional for now
|
||||
if (PyErr_Occurred()) PyErr_Print();
|
||||
py_notrim = 1;
|
||||
|
||||
} else {
|
||||
|
||||
if (PyErr_Occurred()) PyErr_Print();
|
||||
fprintf(stderr,
|
||||
"Cannot find/call function with index %d in external "
|
||||
"Python module.\n",
|
||||
py_idx);
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (py_notrim) {
|
||||
|
||||
py_functions[PY_FUNC_INIT_TRIM] = NULL;
|
||||
py_functions[PY_FUNC_POST_TRIM] = NULL;
|
||||
py_functions[PY_FUNC_TRIM] = NULL;
|
||||
WARNF(
|
||||
"Python module does not implement trim API, standard trimming will "
|
||||
"be used.");
|
||||
|
||||
}
|
||||
|
||||
PyObject *py_args, *py_value;
|
||||
|
||||
/* Provide the init function a seed for the Python RNG */
|
||||
py_args = PyTuple_New(1);
|
||||
py_value = PyInt_FromLong(UR(0xFFFFFFFF));
|
||||
if (!py_value) {
|
||||
|
||||
Py_DECREF(py_args);
|
||||
fprintf(stderr, "Cannot convert argument\n");
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
PyTuple_SetItem(py_args, 0, py_value);
|
||||
|
||||
py_value = PyObject_CallObject(py_functions[PY_FUNC_INIT], py_args);
|
||||
|
||||
Py_DECREF(py_args);
|
||||
|
||||
if (py_value == NULL) {
|
||||
|
||||
PyErr_Print();
|
||||
fprintf(stderr, "Call failed\n");
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
PyErr_Print();
|
||||
fprintf(stderr, "Failed to load \"%s\"\n", module_name);
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void finalize_py() {
|
||||
|
||||
if (py_module != NULL) {
|
||||
|
||||
u32 i;
|
||||
for (i = 0; i < PY_FUNC_COUNT; ++i)
|
||||
Py_XDECREF(py_functions[i]);
|
||||
|
||||
Py_DECREF(py_module);
|
||||
|
||||
}
|
||||
|
||||
Py_Finalize();
|
||||
|
||||
}
|
||||
|
||||
void fuzz_py(char* buf, size_t buflen, char* add_buf, size_t add_buflen,
|
||||
char** ret, size_t* retlen) {
|
||||
|
||||
if (py_module != NULL) {
|
||||
|
||||
PyObject *py_args, *py_value;
|
||||
py_args = PyTuple_New(2);
|
||||
py_value = PyByteArray_FromStringAndSize(buf, buflen);
|
||||
if (!py_value) {
|
||||
|
||||
Py_DECREF(py_args);
|
||||
fprintf(stderr, "Cannot convert argument\n");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
PyTuple_SetItem(py_args, 0, py_value);
|
||||
|
||||
py_value = PyByteArray_FromStringAndSize(add_buf, add_buflen);
|
||||
if (!py_value) {
|
||||
|
||||
Py_DECREF(py_args);
|
||||
fprintf(stderr, "Cannot convert argument\n");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
PyTuple_SetItem(py_args, 1, py_value);
|
||||
|
||||
py_value = PyObject_CallObject(py_functions[PY_FUNC_FUZZ], py_args);
|
||||
|
||||
Py_DECREF(py_args);
|
||||
|
||||
if (py_value != NULL) {
|
||||
|
||||
*retlen = PyByteArray_Size(py_value);
|
||||
*ret = malloc(*retlen);
|
||||
memcpy(*ret, PyByteArray_AsString(py_value), *retlen);
|
||||
Py_DECREF(py_value);
|
||||
|
||||
} else {
|
||||
|
||||
PyErr_Print();
|
||||
fprintf(stderr, "Call failed\n");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u32 init_trim_py(char* buf, size_t buflen) {
|
||||
|
||||
PyObject *py_args, *py_value;
|
||||
|
||||
py_args = PyTuple_New(1);
|
||||
py_value = PyByteArray_FromStringAndSize(buf, buflen);
|
||||
if (!py_value) {
|
||||
|
||||
Py_DECREF(py_args);
|
||||
FATAL("Failed to convert arguments");
|
||||
|
||||
}
|
||||
|
||||
PyTuple_SetItem(py_args, 0, py_value);
|
||||
|
||||
py_value = PyObject_CallObject(py_functions[PY_FUNC_INIT_TRIM], py_args);
|
||||
Py_DECREF(py_args);
|
||||
|
||||
if (py_value != NULL) {
|
||||
|
||||
u32 retcnt = PyInt_AsLong(py_value);
|
||||
Py_DECREF(py_value);
|
||||
return retcnt;
|
||||
|
||||
} else {
|
||||
|
||||
PyErr_Print();
|
||||
FATAL("Call failed");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u32 post_trim_py(char success) {
|
||||
|
||||
PyObject *py_args, *py_value;
|
||||
|
||||
py_args = PyTuple_New(1);
|
||||
|
||||
py_value = PyBool_FromLong(success);
|
||||
if (!py_value) {
|
||||
|
||||
Py_DECREF(py_args);
|
||||
FATAL("Failed to convert arguments");
|
||||
|
||||
}
|
||||
|
||||
PyTuple_SetItem(py_args, 0, py_value);
|
||||
|
||||
py_value = PyObject_CallObject(py_functions[PY_FUNC_POST_TRIM], py_args);
|
||||
Py_DECREF(py_args);
|
||||
|
||||
if (py_value != NULL) {
|
||||
|
||||
u32 retcnt = PyInt_AsLong(py_value);
|
||||
Py_DECREF(py_value);
|
||||
return retcnt;
|
||||
|
||||
} else {
|
||||
|
||||
PyErr_Print();
|
||||
FATAL("Call failed");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void trim_py(char** ret, size_t* retlen) {
|
||||
|
||||
PyObject *py_args, *py_value;
|
||||
|
||||
py_args = PyTuple_New(0);
|
||||
py_value = PyObject_CallObject(py_functions[PY_FUNC_TRIM], py_args);
|
||||
Py_DECREF(py_args);
|
||||
|
||||
if (py_value != NULL) {
|
||||
|
||||
*retlen = PyByteArray_Size(py_value);
|
||||
*ret = malloc(*retlen);
|
||||
memcpy(*ret, PyByteArray_AsString(py_value), *retlen);
|
||||
Py_DECREF(py_value);
|
||||
|
||||
} else {
|
||||
|
||||
PyErr_Print();
|
||||
FATAL("Call failed");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u8 trim_case_python(char** argv, struct queue_entry* q, u8* in_buf) {
|
||||
|
||||
static u8 tmp[64];
|
||||
static u8 clean_trace[MAP_SIZE];
|
||||
|
||||
u8 needs_write = 0, fault = 0;
|
||||
u32 trim_exec = 0;
|
||||
u32 orig_len = q->len;
|
||||
|
||||
stage_name = tmp;
|
||||
bytes_trim_in += q->len;
|
||||
|
||||
/* Initialize trimming in the Python module */
|
||||
stage_cur = 0;
|
||||
stage_max = init_trim_py(in_buf, q->len);
|
||||
|
||||
if (not_on_tty && debug)
|
||||
SAYF("[Python Trimming] START: Max %d iterations, %u bytes", stage_max,
|
||||
q->len);
|
||||
|
||||
while (stage_cur < stage_max) {
|
||||
|
||||
sprintf(tmp, "ptrim %s", DI(trim_exec));
|
||||
|
||||
u32 cksum;
|
||||
|
||||
char* retbuf = NULL;
|
||||
size_t retlen = 0;
|
||||
|
||||
trim_py(&retbuf, &retlen);
|
||||
|
||||
if (retlen > orig_len)
|
||||
FATAL(
|
||||
"Trimmed data returned by Python module is larger than original "
|
||||
"data");
|
||||
|
||||
write_to_testcase(retbuf, retlen);
|
||||
|
||||
fault = run_target(argv, exec_tmout);
|
||||
++trim_execs;
|
||||
|
||||
if (stop_soon || fault == FAULT_ERROR) goto abort_trimming;
|
||||
|
||||
cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
|
||||
|
||||
if (cksum == q->exec_cksum) {
|
||||
|
||||
q->len = retlen;
|
||||
memcpy(in_buf, retbuf, retlen);
|
||||
|
||||
/* Let's save a clean trace, which will be needed by
|
||||
update_bitmap_score once we're done with the trimming stuff. */
|
||||
|
||||
if (!needs_write) {
|
||||
|
||||
needs_write = 1;
|
||||
memcpy(clean_trace, trace_bits, MAP_SIZE);
|
||||
|
||||
}
|
||||
|
||||
/* Tell the Python module that the trimming was successful */
|
||||
stage_cur = post_trim_py(1);
|
||||
|
||||
if (not_on_tty && debug)
|
||||
SAYF("[Python Trimming] SUCCESS: %d/%d iterations (now at %u bytes)",
|
||||
stage_cur, stage_max, q->len);
|
||||
|
||||
} else {
|
||||
|
||||
/* Tell the Python module that the trimming was unsuccessful */
|
||||
stage_cur = post_trim_py(0);
|
||||
if (not_on_tty && debug)
|
||||
SAYF("[Python Trimming] FAILURE: %d/%d iterations", stage_cur,
|
||||
stage_max);
|
||||
|
||||
}
|
||||
|
||||
/* Since this can be slow, update the screen every now and then. */
|
||||
|
||||
if (!(trim_exec++ % stats_update_freq)) show_stats();
|
||||
|
||||
}
|
||||
|
||||
if (not_on_tty && debug)
|
||||
SAYF("[Python Trimming] DONE: %u bytes -> %u bytes", orig_len, q->len);
|
||||
|
||||
/* If we have made changes to in_buf, we also need to update the on-disk
|
||||
version of the test case. */
|
||||
|
||||
if (needs_write) {
|
||||
|
||||
s32 fd;
|
||||
|
||||
unlink(q->fname); /* ignore errors */
|
||||
|
||||
fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", q->fname);
|
||||
|
||||
ck_write(fd, in_buf, q->len, q->fname);
|
||||
close(fd);
|
||||
|
||||
memcpy(trace_bits, clean_trace, MAP_SIZE);
|
||||
update_bitmap_score(q);
|
||||
|
||||
}
|
||||
|
||||
abort_trimming:
|
||||
|
||||
bytes_trim_out += q->len;
|
||||
return fault;
|
||||
|
||||
}
|
||||
|
||||
#endif /* USE_PYTHON */
|
||||
|
456
src/afl-fuzz-queue.c
Normal file
456
src/afl-fuzz-queue.c
Normal file
@ -0,0 +1,456 @@
|
||||
/*
|
||||
american fuzzy lop++ - queue relates routines
|
||||
---------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This is the real deal: the program takes an instrumented binary and
|
||||
attempts a variety of basic fuzzing tricks, paying close attention to
|
||||
how they affect the execution path.
|
||||
|
||||
*/
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* Mark deterministic checks as done for a particular queue entry. We use the
|
||||
.state file to avoid repeating deterministic fuzzing when resuming aborted
|
||||
scans. */
|
||||
|
||||
void mark_as_det_done(struct queue_entry* q) {
|
||||
|
||||
u8* fn = strrchr(q->fname, '/');
|
||||
s32 fd;
|
||||
|
||||
fn = alloc_printf("%s/queue/.state/deterministic_done/%s", out_dir, fn + 1);
|
||||
|
||||
fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", fn);
|
||||
close(fd);
|
||||
|
||||
ck_free(fn);
|
||||
|
||||
q->passed_det = 1;
|
||||
|
||||
}
|
||||
|
||||
/* Mark as variable. Create symlinks if possible to make it easier to examine
|
||||
the files. */
|
||||
|
||||
void mark_as_variable(struct queue_entry* q) {
|
||||
|
||||
u8 *fn = strrchr(q->fname, '/') + 1, *ldest;
|
||||
|
||||
ldest = alloc_printf("../../%s", fn);
|
||||
fn = alloc_printf("%s/queue/.state/variable_behavior/%s", out_dir, fn);
|
||||
|
||||
if (symlink(ldest, fn)) {
|
||||
|
||||
s32 fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", fn);
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
ck_free(ldest);
|
||||
ck_free(fn);
|
||||
|
||||
q->var_behavior = 1;
|
||||
|
||||
}
|
||||
|
||||
/* Mark / unmark as redundant (edge-only). This is not used for restoring state,
|
||||
but may be useful for post-processing datasets. */
|
||||
|
||||
void mark_as_redundant(struct queue_entry* q, u8 state) {
|
||||
|
||||
u8* fn;
|
||||
|
||||
if (state == q->fs_redundant) return;
|
||||
|
||||
q->fs_redundant = state;
|
||||
|
||||
fn = strrchr(q->fname, '/');
|
||||
fn = alloc_printf("%s/queue/.state/redundant_edges/%s", out_dir, fn + 1);
|
||||
|
||||
if (state) {
|
||||
|
||||
s32 fd;
|
||||
|
||||
fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", fn);
|
||||
close(fd);
|
||||
|
||||
} else {
|
||||
|
||||
if (unlink(fn)) PFATAL("Unable to remove '%s'", fn);
|
||||
|
||||
}
|
||||
|
||||
ck_free(fn);
|
||||
|
||||
}
|
||||
|
||||
/* Append new test case to the queue. */
|
||||
|
||||
void add_to_queue(u8* fname, u32 len, u8 passed_det) {
|
||||
|
||||
struct queue_entry* q = ck_alloc(sizeof(struct queue_entry));
|
||||
|
||||
q->fname = fname;
|
||||
q->len = len;
|
||||
q->depth = cur_depth + 1;
|
||||
q->passed_det = passed_det;
|
||||
q->n_fuzz = 1;
|
||||
|
||||
if (q->depth > max_depth) max_depth = q->depth;
|
||||
|
||||
if (queue_top) {
|
||||
|
||||
queue_top->next = q;
|
||||
queue_top = q;
|
||||
|
||||
} else
|
||||
|
||||
q_prev100 = queue = queue_top = q;
|
||||
|
||||
++queued_paths;
|
||||
++pending_not_fuzzed;
|
||||
|
||||
cycles_wo_finds = 0;
|
||||
|
||||
if (!(queued_paths % 100)) {
|
||||
|
||||
q_prev100->next_100 = q;
|
||||
q_prev100 = q;
|
||||
|
||||
}
|
||||
|
||||
last_path_time = get_cur_time();
|
||||
|
||||
}
|
||||
|
||||
/* Destroy the entire queue. */
|
||||
|
||||
void destroy_queue(void) {
|
||||
|
||||
struct queue_entry *q = queue, *n;
|
||||
|
||||
while (q) {
|
||||
|
||||
n = q->next;
|
||||
ck_free(q->fname);
|
||||
ck_free(q->trace_mini);
|
||||
ck_free(q);
|
||||
q = n;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* When we bump into a new path, we call this to see if the path appears
|
||||
more "favorable" than any of the existing ones. The purpose of the
|
||||
"favorables" is to have a minimal set of paths that trigger all the bits
|
||||
seen in the bitmap so far, and focus on fuzzing them at the expense of
|
||||
the rest.
|
||||
|
||||
The first step of the process is to maintain a list of top_rated[] entries
|
||||
for every byte in the bitmap. We win that slot if there is no previous
|
||||
contender, or if the contender has a more favorable speed x size factor. */
|
||||
|
||||
void update_bitmap_score(struct queue_entry* q) {
|
||||
|
||||
u32 i;
|
||||
u64 fav_factor = q->exec_us * q->len;
|
||||
u64 fuzz_p2 = next_p2(q->n_fuzz);
|
||||
|
||||
/* For every byte set in trace_bits[], see if there is a previous winner,
|
||||
and how it compares to us. */
|
||||
|
||||
for (i = 0; i < MAP_SIZE; ++i)
|
||||
|
||||
if (trace_bits[i]) {
|
||||
|
||||
if (top_rated[i]) {
|
||||
|
||||
/* Faster-executing or smaller test cases are favored. */
|
||||
u64 top_rated_fuzz_p2 = next_p2(top_rated[i]->n_fuzz);
|
||||
u64 top_rated_fav_factor = top_rated[i]->exec_us * top_rated[i]->len;
|
||||
|
||||
if (fuzz_p2 > top_rated_fuzz_p2) {
|
||||
|
||||
continue;
|
||||
|
||||
} else if (fuzz_p2 == top_rated_fuzz_p2) {
|
||||
|
||||
if (fav_factor > top_rated_fav_factor) continue;
|
||||
|
||||
}
|
||||
|
||||
if (fav_factor > top_rated[i]->exec_us * top_rated[i]->len) continue;
|
||||
|
||||
/* Looks like we're going to win. Decrease ref count for the
|
||||
previous winner, discard its trace_bits[] if necessary. */
|
||||
|
||||
if (!--top_rated[i]->tc_ref) {
|
||||
|
||||
ck_free(top_rated[i]->trace_mini);
|
||||
top_rated[i]->trace_mini = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Insert ourselves as the new winner. */
|
||||
|
||||
top_rated[i] = q;
|
||||
++q->tc_ref;
|
||||
|
||||
if (!q->trace_mini) {
|
||||
|
||||
q->trace_mini = ck_alloc(MAP_SIZE >> 3);
|
||||
minimize_bits(q->trace_mini, trace_bits);
|
||||
|
||||
}
|
||||
|
||||
score_changed = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* The second part of the mechanism discussed above is a routine that
|
||||
goes over top_rated[] entries, and then sequentially grabs winners for
|
||||
previously-unseen bytes (temp_v) and marks them as favored, at least
|
||||
until the next run. The favored entries are given more air time during
|
||||
all fuzzing steps. */
|
||||
|
||||
void cull_queue(void) {
|
||||
|
||||
struct queue_entry* q;
|
||||
static u8 temp_v[MAP_SIZE >> 3];
|
||||
u32 i;
|
||||
|
||||
if (dumb_mode || !score_changed) return;
|
||||
|
||||
score_changed = 0;
|
||||
|
||||
memset(temp_v, 255, MAP_SIZE >> 3);
|
||||
|
||||
queued_favored = 0;
|
||||
pending_favored = 0;
|
||||
|
||||
q = queue;
|
||||
|
||||
while (q) {
|
||||
|
||||
q->favored = 0;
|
||||
q = q->next;
|
||||
|
||||
}
|
||||
|
||||
/* Let's see if anything in the bitmap isn't captured in temp_v.
|
||||
If yes, and if it has a top_rated[] contender, let's use it. */
|
||||
|
||||
for (i = 0; i < MAP_SIZE; ++i)
|
||||
if (top_rated[i] && (temp_v[i >> 3] & (1 << (i & 7)))) {
|
||||
|
||||
u32 j = MAP_SIZE >> 3;
|
||||
|
||||
/* Remove all bits belonging to the current entry from temp_v. */
|
||||
|
||||
while (j--)
|
||||
if (top_rated[i]->trace_mini[j])
|
||||
temp_v[j] &= ~top_rated[i]->trace_mini[j];
|
||||
|
||||
top_rated[i]->favored = 1;
|
||||
++queued_favored;
|
||||
|
||||
if (top_rated[i]->fuzz_level == 0 || !top_rated[i]->was_fuzzed)
|
||||
++pending_favored;
|
||||
|
||||
}
|
||||
|
||||
q = queue;
|
||||
|
||||
while (q) {
|
||||
|
||||
mark_as_redundant(q, !q->favored);
|
||||
q = q->next;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Calculate case desirability score to adjust the length of havoc fuzzing.
|
||||
A helper function for fuzz_one(). Maybe some of these constants should
|
||||
go into config.h. */
|
||||
|
||||
u32 calculate_score(struct queue_entry* q) {
|
||||
|
||||
u32 avg_exec_us = total_cal_us / total_cal_cycles;
|
||||
u32 avg_bitmap_size = total_bitmap_size / total_bitmap_entries;
|
||||
u32 perf_score = 100;
|
||||
|
||||
/* Adjust score based on execution speed of this path, compared to the
|
||||
global average. Multiplier ranges from 0.1x to 3x. Fast inputs are
|
||||
less expensive to fuzz, so we're giving them more air time. */
|
||||
|
||||
// TODO BUG FIXME: is this really a good idea?
|
||||
// This sounds like looking for lost keys under a street light just because
|
||||
// the light is better there.
|
||||
// Longer execution time means longer work on the input, the deeper in
|
||||
// coverage, the better the fuzzing, right? -mh
|
||||
|
||||
if (q->exec_us * 0.1 > avg_exec_us)
|
||||
perf_score = 10;
|
||||
else if (q->exec_us * 0.25 > avg_exec_us)
|
||||
perf_score = 25;
|
||||
else if (q->exec_us * 0.5 > avg_exec_us)
|
||||
perf_score = 50;
|
||||
else if (q->exec_us * 0.75 > avg_exec_us)
|
||||
perf_score = 75;
|
||||
else if (q->exec_us * 4 < avg_exec_us)
|
||||
perf_score = 300;
|
||||
else if (q->exec_us * 3 < avg_exec_us)
|
||||
perf_score = 200;
|
||||
else if (q->exec_us * 2 < avg_exec_us)
|
||||
perf_score = 150;
|
||||
|
||||
/* Adjust score based on bitmap size. The working theory is that better
|
||||
coverage translates to better targets. Multiplier from 0.25x to 3x. */
|
||||
|
||||
if (q->bitmap_size * 0.3 > avg_bitmap_size)
|
||||
perf_score *= 3;
|
||||
else if (q->bitmap_size * 0.5 > avg_bitmap_size)
|
||||
perf_score *= 2;
|
||||
else if (q->bitmap_size * 0.75 > avg_bitmap_size)
|
||||
perf_score *= 1.5;
|
||||
else if (q->bitmap_size * 3 < avg_bitmap_size)
|
||||
perf_score *= 0.25;
|
||||
else if (q->bitmap_size * 2 < avg_bitmap_size)
|
||||
perf_score *= 0.5;
|
||||
else if (q->bitmap_size * 1.5 < avg_bitmap_size)
|
||||
perf_score *= 0.75;
|
||||
|
||||
/* Adjust score based on handicap. Handicap is proportional to how late
|
||||
in the game we learned about this path. Latecomers are allowed to run
|
||||
for a bit longer until they catch up with the rest. */
|
||||
|
||||
if (q->handicap >= 4) {
|
||||
|
||||
perf_score *= 4;
|
||||
q->handicap -= 4;
|
||||
|
||||
} else if (q->handicap) {
|
||||
|
||||
perf_score *= 2;
|
||||
--q->handicap;
|
||||
|
||||
}
|
||||
|
||||
/* Final adjustment based on input depth, under the assumption that fuzzing
|
||||
deeper test cases is more likely to reveal stuff that can't be
|
||||
discovered with traditional fuzzers. */
|
||||
|
||||
switch (q->depth) {
|
||||
|
||||
case 0 ... 3: break;
|
||||
case 4 ... 7: perf_score *= 2; break;
|
||||
case 8 ... 13: perf_score *= 3; break;
|
||||
case 14 ... 25: perf_score *= 4; break;
|
||||
default: perf_score *= 5;
|
||||
|
||||
}
|
||||
|
||||
u64 fuzz = q->n_fuzz;
|
||||
u64 fuzz_total;
|
||||
|
||||
u32 n_paths, fuzz_mu;
|
||||
u32 factor = 1;
|
||||
|
||||
switch (schedule) {
|
||||
|
||||
case EXPLORE: break;
|
||||
|
||||
case EXPLOIT: factor = MAX_FACTOR; break;
|
||||
|
||||
case COE:
|
||||
fuzz_total = 0;
|
||||
n_paths = 0;
|
||||
|
||||
struct queue_entry* queue_it = queue;
|
||||
while (queue_it) {
|
||||
|
||||
fuzz_total += queue_it->n_fuzz;
|
||||
n_paths++;
|
||||
queue_it = queue_it->next;
|
||||
|
||||
}
|
||||
|
||||
fuzz_mu = fuzz_total / n_paths;
|
||||
if (fuzz <= fuzz_mu) {
|
||||
|
||||
if (q->fuzz_level < 16)
|
||||
factor = ((u32)(1 << q->fuzz_level));
|
||||
else
|
||||
factor = MAX_FACTOR;
|
||||
|
||||
} else {
|
||||
|
||||
factor = 0;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case FAST:
|
||||
if (q->fuzz_level < 16) {
|
||||
|
||||
factor = ((u32)(1 << q->fuzz_level)) / (fuzz == 0 ? 1 : fuzz);
|
||||
|
||||
} else
|
||||
|
||||
factor = MAX_FACTOR / (fuzz == 0 ? 1 : next_p2(fuzz));
|
||||
break;
|
||||
|
||||
case LIN: factor = q->fuzz_level / (fuzz == 0 ? 1 : fuzz); break;
|
||||
|
||||
case QUAD:
|
||||
factor = q->fuzz_level * q->fuzz_level / (fuzz == 0 ? 1 : fuzz);
|
||||
break;
|
||||
|
||||
default: PFATAL("Unknown Power Schedule");
|
||||
|
||||
}
|
||||
|
||||
if (factor > MAX_FACTOR) factor = MAX_FACTOR;
|
||||
|
||||
perf_score *= factor / POWER_BETA;
|
||||
|
||||
// MOpt mode
|
||||
if (limit_time_sig != 0 && max_depth - q->depth < 3)
|
||||
perf_score *= 2;
|
||||
else if (perf_score < 1)
|
||||
perf_score =
|
||||
1; // Add a lower bound to AFLFast's energy assignment strategies
|
||||
|
||||
/* Make sure that we don't go over limit. */
|
||||
|
||||
if (perf_score > havoc_max_mult * 100) perf_score = havoc_max_mult * 100;
|
||||
|
||||
return perf_score;
|
||||
|
||||
}
|
||||
|
804
src/afl-fuzz-run.c
Normal file
804
src/afl-fuzz-run.c
Normal file
@ -0,0 +1,804 @@
|
||||
/*
|
||||
american fuzzy lop++ - target execution related routines
|
||||
--------------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This is the real deal: the program takes an instrumented binary and
|
||||
attempts a variety of basic fuzzing tricks, paying close attention to
|
||||
how they affect the execution path.
|
||||
|
||||
*/
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* Execute target application, monitoring for timeouts. Return status
|
||||
information. The called program will update trace_bits[]. */
|
||||
|
||||
u8 run_target(char** argv, u32 timeout) {
|
||||
|
||||
static struct itimerval it;
|
||||
static u32 prev_timed_out = 0;
|
||||
static u64 exec_ms = 0;
|
||||
|
||||
int status = 0;
|
||||
u32 tb4;
|
||||
|
||||
child_timed_out = 0;
|
||||
|
||||
/* After this memset, trace_bits[] are effectively volatile, so we
|
||||
must prevent any earlier operations from venturing into that
|
||||
territory. */
|
||||
|
||||
memset(trace_bits, 0, MAP_SIZE);
|
||||
MEM_BARRIER();
|
||||
|
||||
/* If we're running in "dumb" mode, we can't rely on the fork server
|
||||
logic compiled into the target program, so we will just keep calling
|
||||
execve(). There is a bit of code duplication between here and
|
||||
init_forkserver(), but c'est la vie. */
|
||||
|
||||
if (dumb_mode == 1 || no_forkserver) {
|
||||
|
||||
child_pid = fork();
|
||||
|
||||
if (child_pid < 0) PFATAL("fork() failed");
|
||||
|
||||
if (!child_pid) {
|
||||
|
||||
struct rlimit r;
|
||||
|
||||
if (mem_limit) {
|
||||
|
||||
r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;
|
||||
|
||||
#ifdef RLIMIT_AS
|
||||
|
||||
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
||||
|
||||
#else
|
||||
|
||||
setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
|
||||
|
||||
#endif /* ^RLIMIT_AS */
|
||||
|
||||
}
|
||||
|
||||
r.rlim_max = r.rlim_cur = 0;
|
||||
|
||||
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
|
||||
|
||||
/* Isolate the process and configure standard descriptors. If out_file is
|
||||
specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */
|
||||
|
||||
setsid();
|
||||
|
||||
dup2(dev_null_fd, 1);
|
||||
dup2(dev_null_fd, 2);
|
||||
|
||||
if (out_file) {
|
||||
|
||||
dup2(dev_null_fd, 0);
|
||||
|
||||
} else {
|
||||
|
||||
dup2(out_fd, 0);
|
||||
close(out_fd);
|
||||
|
||||
}
|
||||
|
||||
/* On Linux, would be faster to use O_CLOEXEC. Maybe TODO. */
|
||||
|
||||
close(dev_null_fd);
|
||||
close(out_dir_fd);
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
close(dev_urandom_fd);
|
||||
#endif
|
||||
close(fileno(plot_file));
|
||||
|
||||
/* Set sane defaults for ASAN if nothing else specified. */
|
||||
|
||||
setenv("ASAN_OPTIONS",
|
||||
"abort_on_error=1:"
|
||||
"detect_leaks=0:"
|
||||
"symbolize=0:"
|
||||
"allocator_may_return_null=1",
|
||||
0);
|
||||
|
||||
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
||||
"symbolize=0:"
|
||||
"msan_track_origins=0", 0);
|
||||
|
||||
execv(target_path, argv);
|
||||
|
||||
/* Use a distinctive bitmap value to tell the parent about execv()
|
||||
falling through. */
|
||||
|
||||
*(u32*)trace_bits = EXEC_FAIL_SIG;
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
s32 res;
|
||||
|
||||
/* In non-dumb mode, we have the fork server up and running, so simply
|
||||
tell it to have at it, and then read back PID. */
|
||||
|
||||
if ((res = write(fsrv_ctl_fd, &prev_timed_out, 4)) != 4) {
|
||||
|
||||
if (stop_soon) return 0;
|
||||
RPFATAL(res, "Unable to request new process from fork server (OOM?)");
|
||||
|
||||
}
|
||||
|
||||
if ((res = read(fsrv_st_fd, &child_pid, 4)) != 4) {
|
||||
|
||||
if (stop_soon) return 0;
|
||||
RPFATAL(res, "Unable to request new process from fork server (OOM?)");
|
||||
|
||||
}
|
||||
|
||||
if (child_pid <= 0) FATAL("Fork server is misbehaving (OOM?)");
|
||||
|
||||
}
|
||||
|
||||
/* Configure timeout, as requested by user, then wait for child to terminate.
|
||||
*/
|
||||
|
||||
it.it_value.tv_sec = (timeout / 1000);
|
||||
it.it_value.tv_usec = (timeout % 1000) * 1000;
|
||||
|
||||
setitimer(ITIMER_REAL, &it, NULL);
|
||||
|
||||
/* The SIGALRM handler simply kills the child_pid and sets child_timed_out. */
|
||||
|
||||
if (dumb_mode == 1 || no_forkserver) {
|
||||
|
||||
if (waitpid(child_pid, &status, 0) <= 0) PFATAL("waitpid() failed");
|
||||
|
||||
} else {
|
||||
|
||||
s32 res;
|
||||
|
||||
if ((res = read(fsrv_st_fd, &status, 4)) != 4) {
|
||||
|
||||
if (stop_soon) return 0;
|
||||
RPFATAL(res, "Unable to communicate with fork server (OOM?)");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!WIFSTOPPED(status)) child_pid = 0;
|
||||
|
||||
getitimer(ITIMER_REAL, &it);
|
||||
exec_ms =
|
||||
(u64)timeout - (it.it_value.tv_sec * 1000 + it.it_value.tv_usec / 1000);
|
||||
if (slowest_exec_ms < exec_ms) slowest_exec_ms = exec_ms;
|
||||
|
||||
it.it_value.tv_sec = 0;
|
||||
it.it_value.tv_usec = 0;
|
||||
|
||||
setitimer(ITIMER_REAL, &it, NULL);
|
||||
|
||||
++total_execs;
|
||||
|
||||
/* Any subsequent operations on trace_bits must not be moved by the
|
||||
compiler below this point. Past this location, trace_bits[] behave
|
||||
very normally and do not have to be treated as volatile. */
|
||||
|
||||
MEM_BARRIER();
|
||||
|
||||
tb4 = *(u32*)trace_bits;
|
||||
|
||||
#ifdef __x86_64__
|
||||
classify_counts((u64*)trace_bits);
|
||||
#else
|
||||
classify_counts((u32*)trace_bits);
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
prev_timed_out = child_timed_out;
|
||||
|
||||
/* Report outcome to caller. */
|
||||
|
||||
if (WIFSIGNALED(status) && !stop_soon) {
|
||||
|
||||
kill_signal = WTERMSIG(status);
|
||||
|
||||
if (child_timed_out && kill_signal == SIGKILL) return FAULT_TMOUT;
|
||||
|
||||
return FAULT_CRASH;
|
||||
|
||||
}
|
||||
|
||||
/* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and
|
||||
must use a special exit code. */
|
||||
|
||||
if (uses_asan && WEXITSTATUS(status) == MSAN_ERROR) {
|
||||
|
||||
kill_signal = 0;
|
||||
return FAULT_CRASH;
|
||||
|
||||
}
|
||||
|
||||
if ((dumb_mode == 1 || no_forkserver) && tb4 == EXEC_FAIL_SIG)
|
||||
return FAULT_ERROR;
|
||||
|
||||
return FAULT_NONE;
|
||||
|
||||
}
|
||||
|
||||
/* Write modified data to file for testing. If out_file is set, the old file
|
||||
is unlinked and a new one is created. Otherwise, out_fd is rewound and
|
||||
truncated. */
|
||||
|
||||
void write_to_testcase(void* mem, u32 len) {
|
||||
|
||||
s32 fd = out_fd;
|
||||
|
||||
if (out_file) {
|
||||
|
||||
unlink(out_file); /* Ignore errors. */
|
||||
|
||||
fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", out_file);
|
||||
|
||||
} else
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
if (pre_save_handler) {
|
||||
|
||||
u8* new_data;
|
||||
size_t new_size = pre_save_handler(mem, len, &new_data);
|
||||
ck_write(fd, new_data, new_size, out_file);
|
||||
|
||||
} else {
|
||||
|
||||
ck_write(fd, mem, len, out_file);
|
||||
|
||||
}
|
||||
|
||||
if (!out_file) {
|
||||
|
||||
if (ftruncate(fd, len)) PFATAL("ftruncate() failed");
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
} else
|
||||
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
/* The same, but with an adjustable gap. Used for trimming. */
|
||||
|
||||
void write_with_gap(void* mem, u32 len, u32 skip_at, u32 skip_len) {
|
||||
|
||||
s32 fd = out_fd;
|
||||
u32 tail_len = len - skip_at - skip_len;
|
||||
|
||||
if (out_file) {
|
||||
|
||||
unlink(out_file); /* Ignore errors. */
|
||||
|
||||
fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", out_file);
|
||||
|
||||
} else
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
if (skip_at) ck_write(fd, mem, skip_at, out_file);
|
||||
|
||||
u8* memu8 = mem;
|
||||
if (tail_len) ck_write(fd, memu8 + skip_at + skip_len, tail_len, out_file);
|
||||
|
||||
if (!out_file) {
|
||||
|
||||
if (ftruncate(fd, len - skip_len)) PFATAL("ftruncate() failed");
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
} else
|
||||
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
/* Calibrate a new test case. This is done when processing the input directory
|
||||
to warn about flaky or otherwise problematic test cases early on; and when
|
||||
new paths are discovered to detect variable behavior and so on. */
|
||||
|
||||
u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem, u32 handicap,
|
||||
u8 from_queue) {
|
||||
|
||||
static u8 first_trace[MAP_SIZE];
|
||||
|
||||
u8 fault = 0, new_bits = 0, var_detected = 0,
|
||||
first_run = (q->exec_cksum == 0);
|
||||
|
||||
u64 start_us, stop_us;
|
||||
|
||||
s32 old_sc = stage_cur, old_sm = stage_max;
|
||||
u32 use_tmout = exec_tmout;
|
||||
u8* old_sn = stage_name;
|
||||
|
||||
/* Be a bit more generous about timeouts when resuming sessions, or when
|
||||
trying to calibrate already-added finds. This helps avoid trouble due
|
||||
to intermittent latency. */
|
||||
|
||||
if (!from_queue || resuming_fuzz)
|
||||
use_tmout =
|
||||
MAX(exec_tmout + CAL_TMOUT_ADD, exec_tmout * CAL_TMOUT_PERC / 100);
|
||||
|
||||
++q->cal_failed;
|
||||
|
||||
stage_name = "calibration";
|
||||
stage_max = fast_cal ? 3 : CAL_CYCLES;
|
||||
|
||||
/* Make sure the forkserver is up before we do anything, and let's not
|
||||
count its spin-up time toward binary calibration. */
|
||||
|
||||
if (dumb_mode != 1 && !no_forkserver && !forksrv_pid) init_forkserver(argv);
|
||||
|
||||
if (q->exec_cksum) memcpy(first_trace, trace_bits, MAP_SIZE);
|
||||
|
||||
start_us = get_cur_time_us();
|
||||
|
||||
for (stage_cur = 0; stage_cur < stage_max; ++stage_cur) {
|
||||
|
||||
u32 cksum;
|
||||
|
||||
if (!first_run && !(stage_cur % stats_update_freq)) show_stats();
|
||||
|
||||
write_to_testcase(use_mem, q->len);
|
||||
|
||||
fault = run_target(argv, use_tmout);
|
||||
|
||||
/* stop_soon is set by the handler for Ctrl+C. When it's pressed,
|
||||
we want to bail out quickly. */
|
||||
|
||||
if (stop_soon || fault != crash_mode) goto abort_calibration;
|
||||
|
||||
if (!dumb_mode && !stage_cur && !count_bytes(trace_bits)) {
|
||||
|
||||
fault = FAULT_NOINST;
|
||||
goto abort_calibration;
|
||||
|
||||
}
|
||||
|
||||
cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
|
||||
|
||||
if (q->exec_cksum != cksum) {
|
||||
|
||||
u8 hnb = has_new_bits(virgin_bits);
|
||||
if (hnb > new_bits) new_bits = hnb;
|
||||
|
||||
if (q->exec_cksum) {
|
||||
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < MAP_SIZE; ++i) {
|
||||
|
||||
if (!var_bytes[i] && first_trace[i] != trace_bits[i]) {
|
||||
|
||||
var_bytes[i] = 1;
|
||||
stage_max = CAL_CYCLES_LONG;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var_detected = 1;
|
||||
|
||||
} else {
|
||||
|
||||
q->exec_cksum = cksum;
|
||||
memcpy(first_trace, trace_bits, MAP_SIZE);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stop_us = get_cur_time_us();
|
||||
|
||||
total_cal_us += stop_us - start_us;
|
||||
total_cal_cycles += stage_max;
|
||||
|
||||
/* OK, let's collect some stats about the performance of this test case.
|
||||
This is used for fuzzing air time calculations in calculate_score(). */
|
||||
|
||||
q->exec_us = (stop_us - start_us) / stage_max;
|
||||
q->bitmap_size = count_bytes(trace_bits);
|
||||
q->handicap = handicap;
|
||||
q->cal_failed = 0;
|
||||
|
||||
total_bitmap_size += q->bitmap_size;
|
||||
++total_bitmap_entries;
|
||||
|
||||
update_bitmap_score(q);
|
||||
|
||||
/* If this case didn't result in new output from the instrumentation, tell
|
||||
parent. This is a non-critical problem, but something to warn the user
|
||||
about. */
|
||||
|
||||
if (!dumb_mode && first_run && !fault && !new_bits) fault = FAULT_NOBITS;
|
||||
|
||||
abort_calibration:
|
||||
|
||||
if (new_bits == 2 && !q->has_new_cov) {
|
||||
|
||||
q->has_new_cov = 1;
|
||||
++queued_with_cov;
|
||||
|
||||
}
|
||||
|
||||
/* Mark variable paths. */
|
||||
|
||||
if (var_detected) {
|
||||
|
||||
var_byte_count = count_bytes(var_bytes);
|
||||
|
||||
if (!q->var_behavior) {
|
||||
|
||||
mark_as_variable(q);
|
||||
++queued_variable;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stage_name = old_sn;
|
||||
stage_cur = old_sc;
|
||||
stage_max = old_sm;
|
||||
|
||||
if (!first_run) show_stats();
|
||||
|
||||
return fault;
|
||||
|
||||
}
|
||||
|
||||
/* Grab interesting test cases from other fuzzers. */
|
||||
|
||||
void sync_fuzzers(char** argv) {
|
||||
|
||||
DIR* sd;
|
||||
struct dirent* sd_ent;
|
||||
u32 sync_cnt = 0;
|
||||
|
||||
sd = opendir(sync_dir);
|
||||
if (!sd) PFATAL("Unable to open '%s'", sync_dir);
|
||||
|
||||
stage_max = stage_cur = 0;
|
||||
cur_depth = 0;
|
||||
|
||||
/* Look at the entries created for every other fuzzer in the sync directory.
|
||||
*/
|
||||
|
||||
while ((sd_ent = readdir(sd))) {
|
||||
|
||||
static u8 stage_tmp[128];
|
||||
|
||||
DIR* qd;
|
||||
struct dirent* qd_ent;
|
||||
u8 * qd_path, *qd_synced_path;
|
||||
u32 min_accept = 0, next_min_accept;
|
||||
|
||||
s32 id_fd;
|
||||
|
||||
/* Skip dot files and our own output directory. */
|
||||
|
||||
if (sd_ent->d_name[0] == '.' || !strcmp(sync_id, sd_ent->d_name)) continue;
|
||||
|
||||
/* Skip anything that doesn't have a queue/ subdirectory. */
|
||||
|
||||
qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name);
|
||||
|
||||
if (!(qd = opendir(qd_path))) {
|
||||
|
||||
ck_free(qd_path);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* Retrieve the ID of the last seen test case. */
|
||||
|
||||
qd_synced_path = alloc_printf("%s/.synced/%s", out_dir, sd_ent->d_name);
|
||||
|
||||
id_fd = open(qd_synced_path, O_RDWR | O_CREAT, 0600);
|
||||
|
||||
if (id_fd < 0) PFATAL("Unable to create '%s'", qd_synced_path);
|
||||
|
||||
if (read(id_fd, &min_accept, sizeof(u32)) > 0) lseek(id_fd, 0, SEEK_SET);
|
||||
|
||||
next_min_accept = min_accept;
|
||||
|
||||
/* Show stats */
|
||||
|
||||
sprintf(stage_tmp, "sync %u", ++sync_cnt);
|
||||
stage_name = stage_tmp;
|
||||
stage_cur = 0;
|
||||
stage_max = 0;
|
||||
|
||||
/* For every file queued by this fuzzer, parse ID and see if we have looked
|
||||
at it before; exec a test case if not. */
|
||||
|
||||
while ((qd_ent = readdir(qd))) {
|
||||
|
||||
u8* path;
|
||||
s32 fd;
|
||||
struct stat st;
|
||||
|
||||
if (qd_ent->d_name[0] == '.' ||
|
||||
sscanf(qd_ent->d_name, CASE_PREFIX "%06u", &syncing_case) != 1 ||
|
||||
syncing_case < min_accept)
|
||||
continue;
|
||||
|
||||
/* OK, sounds like a new one. Let's give it a try. */
|
||||
|
||||
if (syncing_case >= next_min_accept) next_min_accept = syncing_case + 1;
|
||||
|
||||
path = alloc_printf("%s/%s", qd_path, qd_ent->d_name);
|
||||
|
||||
/* Allow this to fail in case the other fuzzer is resuming or so... */
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
|
||||
ck_free(path);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (fstat(fd, &st)) PFATAL("fstat() failed");
|
||||
|
||||
/* Ignore zero-sized or oversized files. */
|
||||
|
||||
if (st.st_size && st.st_size <= MAX_FILE) {
|
||||
|
||||
u8 fault;
|
||||
u8* mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
|
||||
if (mem == MAP_FAILED) PFATAL("Unable to mmap '%s'", path);
|
||||
|
||||
/* See what happens. We rely on save_if_interesting() to catch major
|
||||
errors and save the test case. */
|
||||
|
||||
write_to_testcase(mem, st.st_size);
|
||||
|
||||
fault = run_target(argv, exec_tmout);
|
||||
|
||||
if (stop_soon) return;
|
||||
|
||||
syncing_party = sd_ent->d_name;
|
||||
queued_imported += save_if_interesting(argv, mem, st.st_size, fault);
|
||||
syncing_party = 0;
|
||||
|
||||
munmap(mem, st.st_size);
|
||||
|
||||
if (!(stage_cur++ % stats_update_freq)) show_stats();
|
||||
|
||||
}
|
||||
|
||||
ck_free(path);
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path);
|
||||
|
||||
close(id_fd);
|
||||
closedir(qd);
|
||||
ck_free(qd_path);
|
||||
ck_free(qd_synced_path);
|
||||
|
||||
}
|
||||
|
||||
closedir(sd);
|
||||
|
||||
}
|
||||
|
||||
/* Trim all new test cases to save cycles when doing deterministic checks. The
|
||||
trimmer uses power-of-two increments somewhere between 1/16 and 1/1024 of
|
||||
file size, to keep the stage short and sweet. */
|
||||
|
||||
u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) {
|
||||
|
||||
#ifdef USE_PYTHON
|
||||
if (py_functions[PY_FUNC_TRIM]) return trim_case_python(argv, q, in_buf);
|
||||
#endif
|
||||
|
||||
static u8 tmp[64];
|
||||
static u8 clean_trace[MAP_SIZE];
|
||||
|
||||
u8 needs_write = 0, fault = 0;
|
||||
u32 trim_exec = 0;
|
||||
u32 remove_len;
|
||||
u32 len_p2;
|
||||
|
||||
/* Although the trimmer will be less useful when variable behavior is
|
||||
detected, it will still work to some extent, so we don't check for
|
||||
this. */
|
||||
|
||||
if (q->len < 5) return 0;
|
||||
|
||||
stage_name = tmp;
|
||||
bytes_trim_in += q->len;
|
||||
|
||||
/* Select initial chunk len, starting with large steps. */
|
||||
|
||||
len_p2 = next_p2(q->len);
|
||||
|
||||
remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES);
|
||||
|
||||
/* Continue until the number of steps gets too high or the stepover
|
||||
gets too small. */
|
||||
|
||||
while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)) {
|
||||
|
||||
u32 remove_pos = remove_len;
|
||||
|
||||
sprintf(tmp, "trim %s/%s", DI(remove_len), DI(remove_len));
|
||||
|
||||
stage_cur = 0;
|
||||
stage_max = q->len / remove_len;
|
||||
|
||||
while (remove_pos < q->len) {
|
||||
|
||||
u32 trim_avail = MIN(remove_len, q->len - remove_pos);
|
||||
u32 cksum;
|
||||
|
||||
write_with_gap(in_buf, q->len, remove_pos, trim_avail);
|
||||
|
||||
fault = run_target(argv, exec_tmout);
|
||||
++trim_execs;
|
||||
|
||||
if (stop_soon || fault == FAULT_ERROR) goto abort_trimming;
|
||||
|
||||
/* Note that we don't keep track of crashes or hangs here; maybe TODO? */
|
||||
|
||||
cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
|
||||
|
||||
/* If the deletion had no impact on the trace, make it permanent. This
|
||||
isn't perfect for variable-path inputs, but we're just making a
|
||||
best-effort pass, so it's not a big deal if we end up with false
|
||||
negatives every now and then. */
|
||||
|
||||
if (cksum == q->exec_cksum) {
|
||||
|
||||
u32 move_tail = q->len - remove_pos - trim_avail;
|
||||
|
||||
q->len -= trim_avail;
|
||||
len_p2 = next_p2(q->len);
|
||||
|
||||
memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail,
|
||||
move_tail);
|
||||
|
||||
/* Let's save a clean trace, which will be needed by
|
||||
update_bitmap_score once we're done with the trimming stuff. */
|
||||
|
||||
if (!needs_write) {
|
||||
|
||||
needs_write = 1;
|
||||
memcpy(clean_trace, trace_bits, MAP_SIZE);
|
||||
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
remove_pos += remove_len;
|
||||
|
||||
/* Since this can be slow, update the screen every now and then. */
|
||||
|
||||
if (!(trim_exec++ % stats_update_freq)) show_stats();
|
||||
++stage_cur;
|
||||
|
||||
}
|
||||
|
||||
remove_len >>= 1;
|
||||
|
||||
}
|
||||
|
||||
/* If we have made changes to in_buf, we also need to update the on-disk
|
||||
version of the test case. */
|
||||
|
||||
if (needs_write) {
|
||||
|
||||
s32 fd;
|
||||
|
||||
unlink(q->fname); /* ignore errors */
|
||||
|
||||
fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", q->fname);
|
||||
|
||||
ck_write(fd, in_buf, q->len, q->fname);
|
||||
close(fd);
|
||||
|
||||
memcpy(trace_bits, clean_trace, MAP_SIZE);
|
||||
update_bitmap_score(q);
|
||||
|
||||
}
|
||||
|
||||
abort_trimming:
|
||||
|
||||
bytes_trim_out += q->len;
|
||||
return fault;
|
||||
|
||||
}
|
||||
|
||||
/* Write a modified test case, run program, process results. Handle
|
||||
error conditions, returning 1 if it's time to bail out. This is
|
||||
a helper function for fuzz_one(). */
|
||||
|
||||
u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) {
|
||||
|
||||
u8 fault;
|
||||
|
||||
if (post_handler) {
|
||||
|
||||
out_buf = post_handler(out_buf, &len);
|
||||
if (!out_buf || !len) return 0;
|
||||
|
||||
}
|
||||
|
||||
write_to_testcase(out_buf, len);
|
||||
|
||||
fault = run_target(argv, exec_tmout);
|
||||
|
||||
if (stop_soon) return 1;
|
||||
|
||||
if (fault == FAULT_TMOUT) {
|
||||
|
||||
if (subseq_tmouts++ > TMOUT_LIMIT) {
|
||||
|
||||
++cur_skipped_paths;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
subseq_tmouts = 0;
|
||||
|
||||
/* Users can hit us with SIGUSR1 to request the current input
|
||||
to be abandoned. */
|
||||
|
||||
if (skip_requested) {
|
||||
|
||||
skip_requested = 0;
|
||||
++cur_skipped_paths;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* This handles FAULT_ERROR for us: */
|
||||
|
||||
queued_discovered += save_if_interesting(argv, out_buf, len, fault);
|
||||
|
||||
if (!(stage_cur % stats_update_freq) || stage_cur + 1 == stage_max)
|
||||
show_stats();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
811
src/afl-fuzz-stats.c
Normal file
811
src/afl-fuzz-stats.c
Normal file
@ -0,0 +1,811 @@
|
||||
/*
|
||||
american fuzzy lop++ - stats related routines
|
||||
---------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This is the real deal: the program takes an instrumented binary and
|
||||
attempts a variety of basic fuzzing tricks, paying close attention to
|
||||
how they affect the execution path.
|
||||
|
||||
*/
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* Update stats file for unattended monitoring. */
|
||||
|
||||
void write_stats_file(double bitmap_cvg, double stability, double eps) {
|
||||
|
||||
static double last_bcvg, last_stab, last_eps;
|
||||
static struct rusage rus;
|
||||
|
||||
u8* fn = alloc_printf("%s/fuzzer_stats", out_dir);
|
||||
s32 fd;
|
||||
FILE* f;
|
||||
|
||||
fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", fn);
|
||||
|
||||
ck_free(fn);
|
||||
|
||||
f = fdopen(fd, "w");
|
||||
|
||||
if (!f) PFATAL("fdopen() failed");
|
||||
|
||||
/* Keep last values in case we're called from another context
|
||||
where exec/sec stats and such are not readily available. */
|
||||
|
||||
if (!bitmap_cvg && !stability && !eps) {
|
||||
|
||||
bitmap_cvg = last_bcvg;
|
||||
stability = last_stab;
|
||||
eps = last_eps;
|
||||
|
||||
} else {
|
||||
|
||||
last_bcvg = bitmap_cvg;
|
||||
last_stab = stability;
|
||||
last_eps = eps;
|
||||
|
||||
}
|
||||
|
||||
if (getrusage(RUSAGE_CHILDREN, &rus)) rus.ru_maxrss = 0;
|
||||
|
||||
fprintf(f,
|
||||
"start_time : %llu\n"
|
||||
"last_update : %llu\n"
|
||||
"fuzzer_pid : %d\n"
|
||||
"cycles_done : %llu\n"
|
||||
"execs_done : %llu\n"
|
||||
"execs_per_sec : %0.02f\n"
|
||||
"paths_total : %u\n"
|
||||
"paths_favored : %u\n"
|
||||
"paths_found : %u\n"
|
||||
"paths_imported : %u\n"
|
||||
"max_depth : %u\n"
|
||||
"cur_path : %u\n" /* Must match find_start_position() */
|
||||
"pending_favs : %u\n"
|
||||
"pending_total : %u\n"
|
||||
"variable_paths : %u\n"
|
||||
"stability : %0.02f%%\n"
|
||||
"bitmap_cvg : %0.02f%%\n"
|
||||
"unique_crashes : %llu\n"
|
||||
"unique_hangs : %llu\n"
|
||||
"last_path : %llu\n"
|
||||
"last_crash : %llu\n"
|
||||
"last_hang : %llu\n"
|
||||
"execs_since_crash : %llu\n"
|
||||
"exec_timeout : %u\n"
|
||||
"slowest_exec_ms : %llu\n"
|
||||
"peak_rss_mb : %lu\n"
|
||||
"afl_banner : %s\n"
|
||||
"afl_version : " VERSION
|
||||
"\n"
|
||||
"target_mode : %s%s%s%s%s%s%s%s\n"
|
||||
"command_line : %s\n",
|
||||
start_time / 1000, get_cur_time() / 1000, getpid(),
|
||||
queue_cycle ? (queue_cycle - 1) : 0, total_execs, eps, queued_paths,
|
||||
queued_favored, queued_discovered, queued_imported, max_depth,
|
||||
current_entry, pending_favored, pending_not_fuzzed, queued_variable,
|
||||
stability, bitmap_cvg, unique_crashes, unique_hangs,
|
||||
last_path_time / 1000, last_crash_time / 1000, last_hang_time / 1000,
|
||||
total_execs - last_crash_execs, exec_tmout, slowest_exec_ms,
|
||||
#ifdef __APPLE__
|
||||
(unsigned long int)(rus.ru_maxrss >> 20),
|
||||
#else
|
||||
(unsigned long int)(rus.ru_maxrss >> 10),
|
||||
#endif
|
||||
use_banner, unicorn_mode ? "unicorn" : "", qemu_mode ? "qemu " : "",
|
||||
dumb_mode ? " dumb " : "", no_forkserver ? "no_forksrv " : "",
|
||||
crash_mode ? "crash " : "", persistent_mode ? "persistent " : "",
|
||||
deferred_mode ? "deferred " : "",
|
||||
(unicorn_mode || qemu_mode || dumb_mode || no_forkserver ||
|
||||
crash_mode || persistent_mode || deferred_mode)
|
||||
? ""
|
||||
: "default",
|
||||
orig_cmdline);
|
||||
/* ignore errors */
|
||||
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
|
||||
/* Update the plot file if there is a reason to. */
|
||||
|
||||
void maybe_update_plot_file(double bitmap_cvg, double eps) {
|
||||
|
||||
static u32 prev_qp, prev_pf, prev_pnf, prev_ce, prev_md;
|
||||
static u64 prev_qc, prev_uc, prev_uh;
|
||||
|
||||
if (prev_qp == queued_paths && prev_pf == pending_favored &&
|
||||
prev_pnf == pending_not_fuzzed && prev_ce == current_entry &&
|
||||
prev_qc == queue_cycle && prev_uc == unique_crashes &&
|
||||
prev_uh == unique_hangs && prev_md == max_depth)
|
||||
return;
|
||||
|
||||
prev_qp = queued_paths;
|
||||
prev_pf = pending_favored;
|
||||
prev_pnf = pending_not_fuzzed;
|
||||
prev_ce = current_entry;
|
||||
prev_qc = queue_cycle;
|
||||
prev_uc = unique_crashes;
|
||||
prev_uh = unique_hangs;
|
||||
prev_md = max_depth;
|
||||
|
||||
/* Fields in the file:
|
||||
|
||||
unix_time, cycles_done, cur_path, paths_total, paths_not_fuzzed,
|
||||
favored_not_fuzzed, unique_crashes, unique_hangs, max_depth,
|
||||
execs_per_sec */
|
||||
|
||||
fprintf(plot_file,
|
||||
"%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f\n",
|
||||
get_cur_time() / 1000, queue_cycle - 1, current_entry, queued_paths,
|
||||
pending_not_fuzzed, pending_favored, bitmap_cvg, unique_crashes,
|
||||
unique_hangs, max_depth, eps); /* ignore errors */
|
||||
|
||||
fflush(plot_file);
|
||||
|
||||
}
|
||||
|
||||
/* Check terminal dimensions after resize. */
|
||||
|
||||
static void check_term_size(void) {
|
||||
|
||||
struct winsize ws;
|
||||
|
||||
term_too_small = 0;
|
||||
|
||||
if (ioctl(1, TIOCGWINSZ, &ws)) return;
|
||||
|
||||
if (ws.ws_row == 0 || ws.ws_col == 0) return;
|
||||
if (ws.ws_row < 24 || ws.ws_col < 79) term_too_small = 1;
|
||||
|
||||
}
|
||||
|
||||
/* A spiffy retro stats screen! This is called every stats_update_freq
|
||||
execve() calls, plus in several other circumstances. */
|
||||
|
||||
void show_stats(void) {
|
||||
|
||||
static u64 last_stats_ms, last_plot_ms, last_ms, last_execs;
|
||||
static double avg_exec;
|
||||
double t_byte_ratio, stab_ratio;
|
||||
|
||||
u64 cur_ms;
|
||||
u32 t_bytes, t_bits;
|
||||
|
||||
u32 banner_len, banner_pad;
|
||||
u8 tmp[256];
|
||||
|
||||
cur_ms = get_cur_time();
|
||||
|
||||
/* If not enough time has passed since last UI update, bail out. */
|
||||
|
||||
if (cur_ms - last_ms < 1000 / UI_TARGET_HZ) return;
|
||||
|
||||
/* Check if we're past the 10 minute mark. */
|
||||
|
||||
if (cur_ms - start_time > 10 * 60 * 1000) run_over10m = 1;
|
||||
|
||||
/* Calculate smoothed exec speed stats. */
|
||||
|
||||
if (!last_execs) {
|
||||
|
||||
avg_exec = ((double)total_execs) * 1000 / (cur_ms - start_time);
|
||||
|
||||
} else {
|
||||
|
||||
double cur_avg =
|
||||
((double)(total_execs - last_execs)) * 1000 / (cur_ms - last_ms);
|
||||
|
||||
/* If there is a dramatic (5x+) jump in speed, reset the indicator
|
||||
more quickly. */
|
||||
|
||||
if (cur_avg * 5 < avg_exec || cur_avg / 5 > avg_exec) avg_exec = cur_avg;
|
||||
|
||||
avg_exec = avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) +
|
||||
cur_avg * (1.0 / AVG_SMOOTHING);
|
||||
|
||||
}
|
||||
|
||||
last_ms = cur_ms;
|
||||
last_execs = total_execs;
|
||||
|
||||
/* Tell the callers when to contact us (as measured in execs). */
|
||||
|
||||
stats_update_freq = avg_exec / (UI_TARGET_HZ * 10);
|
||||
if (!stats_update_freq) stats_update_freq = 1;
|
||||
|
||||
/* Do some bitmap stats. */
|
||||
|
||||
t_bytes = count_non_255_bytes(virgin_bits);
|
||||
t_byte_ratio = ((double)t_bytes * 100) / MAP_SIZE;
|
||||
|
||||
if (t_bytes)
|
||||
stab_ratio = 100 - ((double)var_byte_count) * 100 / t_bytes;
|
||||
else
|
||||
stab_ratio = 100;
|
||||
|
||||
/* Roughly every minute, update fuzzer stats and save auto tokens. */
|
||||
|
||||
if (cur_ms - last_stats_ms > STATS_UPDATE_SEC * 1000) {
|
||||
|
||||
last_stats_ms = cur_ms;
|
||||
write_stats_file(t_byte_ratio, stab_ratio, avg_exec);
|
||||
save_auto();
|
||||
write_bitmap();
|
||||
|
||||
}
|
||||
|
||||
/* Every now and then, write plot data. */
|
||||
|
||||
if (cur_ms - last_plot_ms > PLOT_UPDATE_SEC * 1000) {
|
||||
|
||||
last_plot_ms = cur_ms;
|
||||
maybe_update_plot_file(t_byte_ratio, avg_exec);
|
||||
|
||||
}
|
||||
|
||||
/* Honor AFL_EXIT_WHEN_DONE and AFL_BENCH_UNTIL_CRASH. */
|
||||
|
||||
if (!dumb_mode && cycles_wo_finds > 100 && !pending_not_fuzzed &&
|
||||
getenv("AFL_EXIT_WHEN_DONE"))
|
||||
stop_soon = 2;
|
||||
|
||||
if (total_crashes && getenv("AFL_BENCH_UNTIL_CRASH")) stop_soon = 2;
|
||||
|
||||
/* If we're not on TTY, bail out. */
|
||||
|
||||
if (not_on_tty) return;
|
||||
|
||||
/* Compute some mildly useful bitmap stats. */
|
||||
|
||||
t_bits = (MAP_SIZE << 3) - count_bits(virgin_bits);
|
||||
|
||||
/* Now, for the visuals... */
|
||||
|
||||
if (clear_screen) {
|
||||
|
||||
SAYF(TERM_CLEAR CURSOR_HIDE);
|
||||
clear_screen = 0;
|
||||
|
||||
check_term_size();
|
||||
|
||||
}
|
||||
|
||||
SAYF(TERM_HOME);
|
||||
|
||||
if (term_too_small) {
|
||||
|
||||
SAYF(cBRI
|
||||
"Your terminal is too small to display the UI.\n"
|
||||
"Please resize terminal window to at least 79x24.\n" cRST);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/* Let's start by drawing a centered banner. */
|
||||
|
||||
banner_len = (crash_mode ? 24 : 22) + strlen(VERSION) + strlen(use_banner) +
|
||||
strlen(power_name) + 3 + 5;
|
||||
banner_pad = (79 - banner_len) / 2;
|
||||
memset(tmp, ' ', banner_pad);
|
||||
|
||||
#ifdef HAVE_AFFINITY
|
||||
sprintf(tmp + banner_pad,
|
||||
"%s " cLCY VERSION cLGN " (%s) " cPIN "[%s]" cBLU " {%d}",
|
||||
crash_mode ? cPIN "peruvian were-rabbit" : cYEL "american fuzzy lop",
|
||||
use_banner, power_name, cpu_aff);
|
||||
#else
|
||||
sprintf(tmp + banner_pad, "%s " cLCY VERSION cLGN " (%s) " cPIN "[%s]",
|
||||
crash_mode ? cPIN "peruvian were-rabbit" : cYEL "american fuzzy lop",
|
||||
use_banner, power_name);
|
||||
#endif /* HAVE_AFFINITY */
|
||||
|
||||
SAYF("\n%s\n", tmp);
|
||||
|
||||
/* "Handy" shortcuts for drawing boxes... */
|
||||
|
||||
#define bSTG bSTART cGRA
|
||||
#define bH2 bH bH
|
||||
#define bH5 bH2 bH2 bH
|
||||
#define bH10 bH5 bH5
|
||||
#define bH20 bH10 bH10
|
||||
#define bH30 bH20 bH10
|
||||
#define SP5 " "
|
||||
#define SP10 SP5 SP5
|
||||
#define SP20 SP10 SP10
|
||||
|
||||
/* Lord, forgive me this. */
|
||||
|
||||
SAYF(SET_G1 bSTG bLT bH bSTOP cCYA
|
||||
" process timing " bSTG bH30 bH5 bH bHB bH bSTOP cCYA
|
||||
" overall results " bSTG bH2 bH2 bRT "\n");
|
||||
|
||||
if (dumb_mode) {
|
||||
|
||||
strcpy(tmp, cRST);
|
||||
|
||||
} else {
|
||||
|
||||
u64 min_wo_finds = (cur_ms - last_path_time) / 1000 / 60;
|
||||
|
||||
/* First queue cycle: don't stop now! */
|
||||
if (queue_cycle == 1 || min_wo_finds < 15)
|
||||
strcpy(tmp, cMGN);
|
||||
else
|
||||
|
||||
/* Subsequent cycles, but we're still making finds. */
|
||||
if (cycles_wo_finds < 25 || min_wo_finds < 30)
|
||||
strcpy(tmp, cYEL);
|
||||
else
|
||||
|
||||
/* No finds for a long time and no test cases to try. */
|
||||
if (cycles_wo_finds > 100 && !pending_not_fuzzed && min_wo_finds > 120)
|
||||
strcpy(tmp, cLGN);
|
||||
|
||||
/* Default: cautiously OK to stop? */
|
||||
else
|
||||
strcpy(tmp, cLBL);
|
||||
|
||||
}
|
||||
|
||||
SAYF(bV bSTOP " run time : " cRST "%-33s " bSTG bV bSTOP
|
||||
" cycles done : %s%-5s " bSTG bV "\n",
|
||||
DTD(cur_ms, start_time), tmp, DI(queue_cycle - 1));
|
||||
|
||||
/* We want to warn people about not seeing new paths after a full cycle,
|
||||
except when resuming fuzzing or running in non-instrumented mode. */
|
||||
|
||||
if (!dumb_mode && (last_path_time || resuming_fuzz || queue_cycle == 1 ||
|
||||
in_bitmap || crash_mode)) {
|
||||
|
||||
SAYF(bV bSTOP " last new path : " cRST "%-33s ",
|
||||
DTD(cur_ms, last_path_time));
|
||||
|
||||
} else {
|
||||
|
||||
if (dumb_mode)
|
||||
|
||||
SAYF(bV bSTOP " last new path : " cPIN "n/a" cRST
|
||||
" (non-instrumented mode) ");
|
||||
|
||||
else
|
||||
|
||||
SAYF(bV bSTOP " last new path : " cRST "none yet " cLRD
|
||||
"(odd, check syntax!) ");
|
||||
|
||||
}
|
||||
|
||||
SAYF(bSTG bV bSTOP " total paths : " cRST "%-5s " bSTG bV "\n",
|
||||
DI(queued_paths));
|
||||
|
||||
/* Highlight crashes in red if found, denote going over the KEEP_UNIQUE_CRASH
|
||||
limit with a '+' appended to the count. */
|
||||
|
||||
sprintf(tmp, "%s%s", DI(unique_crashes),
|
||||
(unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : "");
|
||||
|
||||
SAYF(bV bSTOP " last uniq crash : " cRST "%-33s " bSTG bV bSTOP
|
||||
" uniq crashes : %s%-6s" bSTG bV "\n",
|
||||
DTD(cur_ms, last_crash_time), unique_crashes ? cLRD : cRST, tmp);
|
||||
|
||||
sprintf(tmp, "%s%s", DI(unique_hangs),
|
||||
(unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : "");
|
||||
|
||||
SAYF(bV bSTOP " last uniq hang : " cRST "%-33s " bSTG bV bSTOP
|
||||
" uniq hangs : " cRST "%-6s" bSTG bV "\n",
|
||||
DTD(cur_ms, last_hang_time), tmp);
|
||||
|
||||
SAYF(bVR bH bSTOP cCYA
|
||||
" cycle progress " bSTG bH10 bH5 bH2 bH2 bHB bH bSTOP cCYA
|
||||
" map coverage " bSTG bH bHT bH20 bH2 bVL "\n");
|
||||
|
||||
/* This gets funny because we want to print several variable-length variables
|
||||
together, but then cram them into a fixed-width field - so we need to
|
||||
put them in a temporary buffer first. */
|
||||
|
||||
sprintf(tmp, "%s%s%u (%0.02f%%)", DI(current_entry),
|
||||
queue_cur->favored ? "." : "*", queue_cur->fuzz_level,
|
||||
((double)current_entry * 100) / queued_paths);
|
||||
|
||||
SAYF(bV bSTOP " now processing : " cRST "%-16s " bSTG bV bSTOP, tmp);
|
||||
|
||||
sprintf(tmp, "%0.02f%% / %0.02f%%",
|
||||
((double)queue_cur->bitmap_size) * 100 / MAP_SIZE, t_byte_ratio);
|
||||
|
||||
SAYF(" map density : %s%-21s" bSTG bV "\n",
|
||||
t_byte_ratio > 70 ? cLRD : ((t_bytes < 200 && !dumb_mode) ? cPIN : cRST),
|
||||
tmp);
|
||||
|
||||
sprintf(tmp, "%s (%0.02f%%)", DI(cur_skipped_paths),
|
||||
((double)cur_skipped_paths * 100) / queued_paths);
|
||||
|
||||
SAYF(bV bSTOP " paths timed out : " cRST "%-16s " bSTG bV, tmp);
|
||||
|
||||
sprintf(tmp, "%0.02f bits/tuple", t_bytes ? (((double)t_bits) / t_bytes) : 0);
|
||||
|
||||
SAYF(bSTOP " count coverage : " cRST "%-21s" bSTG bV "\n", tmp);
|
||||
|
||||
SAYF(bVR bH bSTOP cCYA
|
||||
" stage progress " bSTG bH10 bH5 bH2 bH2 bX bH bSTOP cCYA
|
||||
" findings in depth " bSTG bH10 bH5 bH2 bH2 bVL "\n");
|
||||
|
||||
sprintf(tmp, "%s (%0.02f%%)", DI(queued_favored),
|
||||
((double)queued_favored) * 100 / queued_paths);
|
||||
|
||||
/* Yeah... it's still going on... halp? */
|
||||
|
||||
SAYF(bV bSTOP " now trying : " cRST "%-20s " bSTG bV bSTOP
|
||||
" favored paths : " cRST "%-22s" bSTG bV "\n",
|
||||
stage_name, tmp);
|
||||
|
||||
if (!stage_max) {
|
||||
|
||||
sprintf(tmp, "%s/-", DI(stage_cur));
|
||||
|
||||
} else {
|
||||
|
||||
sprintf(tmp, "%s/%s (%0.02f%%)", DI(stage_cur), DI(stage_max),
|
||||
((double)stage_cur) * 100 / stage_max);
|
||||
|
||||
}
|
||||
|
||||
SAYF(bV bSTOP " stage execs : " cRST "%-20s " bSTG bV bSTOP, tmp);
|
||||
|
||||
sprintf(tmp, "%s (%0.02f%%)", DI(queued_with_cov),
|
||||
((double)queued_with_cov) * 100 / queued_paths);
|
||||
|
||||
SAYF(" new edges on : " cRST "%-22s" bSTG bV "\n", tmp);
|
||||
|
||||
sprintf(tmp, "%s (%s%s unique)", DI(total_crashes), DI(unique_crashes),
|
||||
(unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : "");
|
||||
|
||||
if (crash_mode) {
|
||||
|
||||
SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP
|
||||
" new crashes : %s%-22s" bSTG bV "\n",
|
||||
DI(total_execs), unique_crashes ? cLRD : cRST, tmp);
|
||||
|
||||
} else {
|
||||
|
||||
SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP
|
||||
" total crashes : %s%-22s" bSTG bV "\n",
|
||||
DI(total_execs), unique_crashes ? cLRD : cRST, tmp);
|
||||
|
||||
}
|
||||
|
||||
/* Show a warning about slow execution. */
|
||||
|
||||
if (avg_exec < 100) {
|
||||
|
||||
sprintf(tmp, "%s/sec (%s)", DF(avg_exec),
|
||||
avg_exec < 20 ? "zzzz..." : "slow!");
|
||||
|
||||
SAYF(bV bSTOP " exec speed : " cLRD "%-20s ", tmp);
|
||||
|
||||
} else {
|
||||
|
||||
sprintf(tmp, "%s/sec", DF(avg_exec));
|
||||
SAYF(bV bSTOP " exec speed : " cRST "%-20s ", tmp);
|
||||
|
||||
}
|
||||
|
||||
sprintf(tmp, "%s (%s%s unique)", DI(total_tmouts), DI(unique_tmouts),
|
||||
(unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : "");
|
||||
|
||||
SAYF(bSTG bV bSTOP " total tmouts : " cRST "%-22s" bSTG bV "\n", tmp);
|
||||
|
||||
/* Aaaalmost there... hold on! */
|
||||
|
||||
SAYF(bVR bH cCYA bSTOP
|
||||
" fuzzing strategy yields " bSTG bH10 bHT bH10 bH5 bHB bH bSTOP cCYA
|
||||
" path geometry " bSTG bH5 bH2 bVL "\n");
|
||||
|
||||
if (skip_deterministic) {
|
||||
|
||||
strcpy(tmp, "n/a, n/a, n/a");
|
||||
|
||||
} else {
|
||||
|
||||
sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_FLIP1]),
|
||||
DI(stage_cycles[STAGE_FLIP1]), DI(stage_finds[STAGE_FLIP2]),
|
||||
DI(stage_cycles[STAGE_FLIP2]), DI(stage_finds[STAGE_FLIP4]),
|
||||
DI(stage_cycles[STAGE_FLIP4]));
|
||||
|
||||
}
|
||||
|
||||
SAYF(bV bSTOP " bit flips : " cRST "%-36s " bSTG bV bSTOP
|
||||
" levels : " cRST "%-10s" bSTG bV "\n",
|
||||
tmp, DI(max_depth));
|
||||
|
||||
if (!skip_deterministic)
|
||||
sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_FLIP8]),
|
||||
DI(stage_cycles[STAGE_FLIP8]), DI(stage_finds[STAGE_FLIP16]),
|
||||
DI(stage_cycles[STAGE_FLIP16]), DI(stage_finds[STAGE_FLIP32]),
|
||||
DI(stage_cycles[STAGE_FLIP32]));
|
||||
|
||||
SAYF(bV bSTOP " byte flips : " cRST "%-36s " bSTG bV bSTOP
|
||||
" pending : " cRST "%-10s" bSTG bV "\n",
|
||||
tmp, DI(pending_not_fuzzed));
|
||||
|
||||
if (!skip_deterministic)
|
||||
sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_ARITH8]),
|
||||
DI(stage_cycles[STAGE_ARITH8]), DI(stage_finds[STAGE_ARITH16]),
|
||||
DI(stage_cycles[STAGE_ARITH16]), DI(stage_finds[STAGE_ARITH32]),
|
||||
DI(stage_cycles[STAGE_ARITH32]));
|
||||
|
||||
SAYF(bV bSTOP " arithmetics : " cRST "%-36s " bSTG bV bSTOP
|
||||
" pend fav : " cRST "%-10s" bSTG bV "\n",
|
||||
tmp, DI(pending_favored));
|
||||
|
||||
if (!skip_deterministic)
|
||||
sprintf(
|
||||
tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_INTEREST8]),
|
||||
DI(stage_cycles[STAGE_INTEREST8]), DI(stage_finds[STAGE_INTEREST16]),
|
||||
DI(stage_cycles[STAGE_INTEREST16]), DI(stage_finds[STAGE_INTEREST32]),
|
||||
DI(stage_cycles[STAGE_INTEREST32]));
|
||||
|
||||
SAYF(bV bSTOP " known ints : " cRST "%-36s " bSTG bV bSTOP
|
||||
" own finds : " cRST "%-10s" bSTG bV "\n",
|
||||
tmp, DI(queued_discovered));
|
||||
|
||||
if (!skip_deterministic)
|
||||
sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_EXTRAS_UO]),
|
||||
DI(stage_cycles[STAGE_EXTRAS_UO]), DI(stage_finds[STAGE_EXTRAS_UI]),
|
||||
DI(stage_cycles[STAGE_EXTRAS_UI]), DI(stage_finds[STAGE_EXTRAS_AO]),
|
||||
DI(stage_cycles[STAGE_EXTRAS_AO]));
|
||||
|
||||
SAYF(bV bSTOP " dictionary : " cRST "%-36s " bSTG bV bSTOP
|
||||
" imported : " cRST "%-10s" bSTG bV "\n",
|
||||
tmp, sync_id ? DI(queued_imported) : (u8*)"n/a");
|
||||
|
||||
sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_HAVOC]),
|
||||
DI(stage_cycles[STAGE_HAVOC]), DI(stage_finds[STAGE_SPLICE]),
|
||||
DI(stage_cycles[STAGE_SPLICE]), DI(stage_finds[STAGE_PYTHON]),
|
||||
DI(stage_cycles[STAGE_PYTHON]));
|
||||
|
||||
SAYF(bV bSTOP " havoc : " cRST "%-36s " bSTG bV bSTOP, tmp);
|
||||
|
||||
if (t_bytes)
|
||||
sprintf(tmp, "%0.02f%%", stab_ratio);
|
||||
else
|
||||
strcpy(tmp, "n/a");
|
||||
|
||||
SAYF(" stability : %s%-10s" bSTG bV "\n",
|
||||
(stab_ratio < 85 && var_byte_count > 40)
|
||||
? cLRD
|
||||
: ((queued_variable && (!persistent_mode || var_byte_count > 20))
|
||||
? cMGN
|
||||
: cRST),
|
||||
tmp);
|
||||
|
||||
if (!bytes_trim_out) {
|
||||
|
||||
sprintf(tmp, "n/a, ");
|
||||
|
||||
} else {
|
||||
|
||||
sprintf(tmp, "%0.02f%%/%s, ",
|
||||
((double)(bytes_trim_in - bytes_trim_out)) * 100 / bytes_trim_in,
|
||||
DI(trim_execs));
|
||||
|
||||
}
|
||||
|
||||
if (!blocks_eff_total) {
|
||||
|
||||
u8 tmp2[128];
|
||||
|
||||
sprintf(tmp2, "n/a");
|
||||
strcat(tmp, tmp2);
|
||||
|
||||
} else {
|
||||
|
||||
u8 tmp2[128];
|
||||
|
||||
sprintf(tmp2, "%0.02f%%",
|
||||
((double)(blocks_eff_total - blocks_eff_select)) * 100 /
|
||||
blocks_eff_total);
|
||||
|
||||
strcat(tmp, tmp2);
|
||||
|
||||
}
|
||||
|
||||
if (custom_mutator) {
|
||||
|
||||
sprintf(tmp, "%s/%s", DI(stage_finds[STAGE_CUSTOM_MUTATOR]),
|
||||
DI(stage_cycles[STAGE_CUSTOM_MUTATOR]));
|
||||
SAYF(bV bSTOP " custom mut. : " cRST "%-36s " bSTG bVR bH20 bH2 bH bRB
|
||||
"\n" bLB bH30 bH20 bH2 bH bRB bSTOP cRST RESET_G1,
|
||||
tmp);
|
||||
|
||||
} else {
|
||||
|
||||
SAYF(bV bSTOP " trim : " cRST "%-36s " bSTG bVR bH20 bH2 bH bRB
|
||||
"\n" bLB bH30 bH20 bH2 bRB bSTOP cRST RESET_G1,
|
||||
tmp);
|
||||
|
||||
}
|
||||
|
||||
/* Provide some CPU utilization stats. */
|
||||
|
||||
if (cpu_core_count) {
|
||||
|
||||
double cur_runnable = get_runnable_processes();
|
||||
u32 cur_utilization = cur_runnable * 100 / cpu_core_count;
|
||||
|
||||
u8* cpu_color = cCYA;
|
||||
|
||||
/* If we could still run one or more processes, use green. */
|
||||
|
||||
if (cpu_core_count > 1 && cur_runnable + 1 <= cpu_core_count)
|
||||
cpu_color = cLGN;
|
||||
|
||||
/* If we're clearly oversubscribed, use red. */
|
||||
|
||||
if (!no_cpu_meter_red && cur_utilization >= 150) cpu_color = cLRD;
|
||||
|
||||
#ifdef HAVE_AFFINITY
|
||||
|
||||
if (cpu_aff >= 0) {
|
||||
|
||||
SAYF(SP10 cGRA "[cpu%03u:%s%3u%%" cGRA "]\r" cRST, MIN(cpu_aff, 999),
|
||||
cpu_color, MIN(cur_utilization, 999));
|
||||
|
||||
} else {
|
||||
|
||||
SAYF(SP10 cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, cpu_color,
|
||||
MIN(cur_utilization, 999));
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
SAYF(SP10 cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, cpu_color,
|
||||
MIN(cur_utilization, 999));
|
||||
|
||||
#endif /* ^HAVE_AFFINITY */
|
||||
|
||||
} else
|
||||
|
||||
SAYF("\r");
|
||||
|
||||
/* Hallelujah! */
|
||||
|
||||
fflush(0);
|
||||
|
||||
}
|
||||
|
||||
/* Display quick statistics at the end of processing the input directory,
|
||||
plus a bunch of warnings. Some calibration stuff also ended up here,
|
||||
along with several hardcoded constants. Maybe clean up eventually. */
|
||||
|
||||
void show_init_stats(void) {
|
||||
|
||||
struct queue_entry* q = queue;
|
||||
u32 min_bits = 0, max_bits = 0;
|
||||
u64 min_us = 0, max_us = 0;
|
||||
u64 avg_us = 0;
|
||||
u32 max_len = 0;
|
||||
|
||||
if (total_cal_cycles) avg_us = total_cal_us / total_cal_cycles;
|
||||
|
||||
while (q) {
|
||||
|
||||
if (!min_us || q->exec_us < min_us) min_us = q->exec_us;
|
||||
if (q->exec_us > max_us) max_us = q->exec_us;
|
||||
|
||||
if (!min_bits || q->bitmap_size < min_bits) min_bits = q->bitmap_size;
|
||||
if (q->bitmap_size > max_bits) max_bits = q->bitmap_size;
|
||||
|
||||
if (q->len > max_len) max_len = q->len;
|
||||
|
||||
q = q->next;
|
||||
|
||||
}
|
||||
|
||||
SAYF("\n");
|
||||
|
||||
if (avg_us > ((qemu_mode || unicorn_mode) ? 50000 : 10000))
|
||||
WARNF(cLRD "The target binary is pretty slow! See %s/perf_tips.txt.",
|
||||
doc_path);
|
||||
|
||||
/* Let's keep things moving with slow binaries. */
|
||||
|
||||
if (avg_us > 50000)
|
||||
havoc_div = 10; /* 0-19 execs/sec */
|
||||
else if (avg_us > 20000)
|
||||
havoc_div = 5; /* 20-49 execs/sec */
|
||||
else if (avg_us > 10000)
|
||||
havoc_div = 2; /* 50-100 execs/sec */
|
||||
|
||||
if (!resuming_fuzz) {
|
||||
|
||||
if (max_len > 50 * 1024)
|
||||
WARNF(cLRD "Some test cases are huge (%s) - see %s/perf_tips.txt!",
|
||||
DMS(max_len), doc_path);
|
||||
else if (max_len > 10 * 1024)
|
||||
WARNF("Some test cases are big (%s) - see %s/perf_tips.txt.",
|
||||
DMS(max_len), doc_path);
|
||||
|
||||
if (useless_at_start && !in_bitmap)
|
||||
WARNF(cLRD "Some test cases look useless. Consider using a smaller set.");
|
||||
|
||||
if (queued_paths > 100)
|
||||
WARNF(cLRD
|
||||
"You probably have far too many input files! Consider trimming "
|
||||
"down.");
|
||||
else if (queued_paths > 20)
|
||||
WARNF("You have lots of input files; try starting small.");
|
||||
|
||||
}
|
||||
|
||||
OKF("Here are some useful stats:\n\n"
|
||||
|
||||
cGRA " Test case count : " cRST
|
||||
"%u favored, %u variable, %u total\n" cGRA " Bitmap range : " cRST
|
||||
"%u to %u bits (average: %0.02f bits)\n" cGRA
|
||||
" Exec timing : " cRST "%s to %s us (average: %s us)\n",
|
||||
queued_favored, queued_variable, queued_paths, min_bits, max_bits,
|
||||
((double)total_bitmap_size) /
|
||||
(total_bitmap_entries ? total_bitmap_entries : 1),
|
||||
DI(min_us), DI(max_us), DI(avg_us));
|
||||
|
||||
if (!timeout_given) {
|
||||
|
||||
/* Figure out the appropriate timeout. The basic idea is: 5x average or
|
||||
1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second.
|
||||
|
||||
If the program is slow, the multiplier is lowered to 2x or 3x, because
|
||||
random scheduler jitter is less likely to have any impact, and because
|
||||
our patience is wearing thin =) */
|
||||
|
||||
if (avg_us > 50000)
|
||||
exec_tmout = avg_us * 2 / 1000;
|
||||
else if (avg_us > 10000)
|
||||
exec_tmout = avg_us * 3 / 1000;
|
||||
else
|
||||
exec_tmout = avg_us * 5 / 1000;
|
||||
|
||||
exec_tmout = MAX(exec_tmout, max_us / 1000);
|
||||
exec_tmout = (exec_tmout + EXEC_TM_ROUND) / EXEC_TM_ROUND * EXEC_TM_ROUND;
|
||||
|
||||
if (exec_tmout > EXEC_TIMEOUT) exec_tmout = EXEC_TIMEOUT;
|
||||
|
||||
ACTF("No -t option specified, so I'll use exec timeout of %u ms.",
|
||||
exec_tmout);
|
||||
|
||||
timeout_given = 1;
|
||||
|
||||
} else if (timeout_given == 3) {
|
||||
|
||||
ACTF("Applying timeout settings from resumed session (%u ms).", exec_tmout);
|
||||
|
||||
}
|
||||
|
||||
/* In dumb mode, re-running every timing out test case with a generous time
|
||||
limit is very expensive, so let's select a more conservative default. */
|
||||
|
||||
if (dumb_mode && !getenv("AFL_HANG_TMOUT"))
|
||||
hang_tmout = MIN(EXEC_TIMEOUT, exec_tmout * 2 + 100);
|
||||
|
||||
OKF("All set and ready to roll!");
|
||||
|
||||
}
|
||||
|
889
src/afl-fuzz.c
Normal file
889
src/afl-fuzz.c
Normal file
@ -0,0 +1,889 @@
|
||||
/*
|
||||
american fuzzy lop - fuzzer code
|
||||
--------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This is the real deal: the program takes an instrumented binary and
|
||||
attempts a variety of basic fuzzing tricks, paying close attention to
|
||||
how they affect the execution path.
|
||||
|
||||
*/
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* Display usage hints. */
|
||||
|
||||
static void usage(u8* argv0) {
|
||||
|
||||
#ifdef USE_PYTHON
|
||||
#define PHYTON_SUPPORT \
|
||||
"Compiled with Python 2.7 module support, see docs/python_mutators.txt\n"
|
||||
#else
|
||||
#define PHYTON_SUPPORT ""
|
||||
#endif
|
||||
|
||||
SAYF(
|
||||
"\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n"
|
||||
|
||||
"Required parameters:\n"
|
||||
" -i dir - input directory with test cases\n"
|
||||
" -o dir - output directory for fuzzer findings\n\n"
|
||||
|
||||
"Execution control settings:\n"
|
||||
" -p schedule - power schedules recompute a seed's performance "
|
||||
"score.\n"
|
||||
" <explore (default), fast, coe, lin, quad, or "
|
||||
"exploit>\n"
|
||||
" see docs/power_schedules.txt\n"
|
||||
" -f file - location read by the fuzzed program (stdin)\n"
|
||||
" -t msec - timeout for each run (auto-scaled, 50-%d ms)\n"
|
||||
" -m megs - memory limit for child process (%d MB)\n"
|
||||
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||
" -U - use Unicorn-based instrumentation (Unicorn mode)\n\n"
|
||||
" -L minutes - use MOpt(imize) mode and set the limit time for "
|
||||
"entering the\n"
|
||||
" pacemaker mode (minutes of no new paths, 0 = "
|
||||
"immediately).\n"
|
||||
" a recommended value is 10-60. see docs/README.MOpt\n\n"
|
||||
|
||||
"Fuzzing behavior settings:\n"
|
||||
" -d - quick & dirty mode (skips deterministic steps)\n"
|
||||
" -n - fuzz without instrumentation (dumb mode)\n"
|
||||
" -x dir - optional fuzzer dictionary (see README)\n\n"
|
||||
|
||||
"Testing settings:\n"
|
||||
" -s seed - use a fixed seed for the RNG\n"
|
||||
" -V seconds - fuzz for a maximum total time of seconds then "
|
||||
"terminate\n"
|
||||
" -E execs - fuzz for a maximum number of total executions then "
|
||||
"terminate\n\n"
|
||||
|
||||
"Other stuff:\n"
|
||||
" -T text - text banner to show on the screen\n"
|
||||
" -M / -S id - distributed mode (see parallel_fuzzing.txt)\n"
|
||||
" -B bitmap.txt - mutate a specific test case, use the out/fuzz_bitmap "
|
||||
"file\n"
|
||||
" -C - crash exploration mode (the peruvian rabbit thing)\n"
|
||||
" -e ext - File extension for the temporarily generated test "
|
||||
"case\n\n"
|
||||
|
||||
PHYTON_SUPPORT
|
||||
|
||||
"For additional tips, please consult %s/README\n\n",
|
||||
|
||||
argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path);
|
||||
|
||||
exit(1);
|
||||
#undef PHYTON_SUPPORT
|
||||
|
||||
}
|
||||
|
||||
#ifndef AFL_LIB
|
||||
|
||||
static int stricmp(char const* a, char const* b) {
|
||||
|
||||
for (;; ++a, ++b) {
|
||||
|
||||
int d;
|
||||
d = tolower(*a) - tolower(*b);
|
||||
if (d != 0 || !*a) return d;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Main entry point */
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
s32 opt;
|
||||
u64 prev_queued = 0;
|
||||
u32 sync_interval_cnt = 0, seek_to;
|
||||
u8* extras_dir = 0;
|
||||
u8 mem_limit_given = 0;
|
||||
u8 exit_1 = !!getenv("AFL_BENCH_JUST_ONE");
|
||||
char** use_argv;
|
||||
s64 init_seed;
|
||||
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
SAYF(cCYA
|
||||
"afl-fuzz" VERSION cRST
|
||||
" based on afl by <lcamtuf@google.com> and a big online community\n");
|
||||
|
||||
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
|
||||
|
||||
gettimeofday(&tv, &tz);
|
||||
init_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
|
||||
|
||||
while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:QUe:p:s:V:E:L:h")) >
|
||||
0)
|
||||
|
||||
switch (opt) {
|
||||
|
||||
case 's': {
|
||||
|
||||
init_seed = strtoul(optarg, 0L, 10);
|
||||
fixed_seed = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 'p': /* Power schedule */
|
||||
|
||||
if (!stricmp(optarg, "fast")) {
|
||||
|
||||
schedule = FAST;
|
||||
|
||||
} else if (!stricmp(optarg, "coe")) {
|
||||
|
||||
schedule = COE;
|
||||
|
||||
} else if (!stricmp(optarg, "exploit")) {
|
||||
|
||||
schedule = EXPLOIT;
|
||||
|
||||
} else if (!stricmp(optarg, "lin")) {
|
||||
|
||||
schedule = LIN;
|
||||
|
||||
} else if (!stricmp(optarg, "quad")) {
|
||||
|
||||
schedule = QUAD;
|
||||
|
||||
} else if (!stricmp(optarg, "explore") || !stricmp(optarg, "default") ||
|
||||
|
||||
!stricmp(optarg, "normal") || !stricmp(optarg, "afl")) {
|
||||
|
||||
schedule = EXPLORE;
|
||||
|
||||
} else {
|
||||
|
||||
FATAL("Unknown -p power schedule");
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
|
||||
if (file_extension) FATAL("Multiple -e options not supported");
|
||||
|
||||
file_extension = optarg;
|
||||
|
||||
break;
|
||||
|
||||
case 'i': /* input dir */
|
||||
|
||||
if (in_dir) FATAL("Multiple -i options not supported");
|
||||
in_dir = optarg;
|
||||
|
||||
if (!strcmp(in_dir, "-")) in_place_resume = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 'o': /* output dir */
|
||||
|
||||
if (out_dir) FATAL("Multiple -o options not supported");
|
||||
out_dir = optarg;
|
||||
break;
|
||||
|
||||
case 'M': { /* master sync ID */
|
||||
|
||||
u8* c;
|
||||
|
||||
if (sync_id) FATAL("Multiple -S or -M options not supported");
|
||||
sync_id = ck_strdup(optarg);
|
||||
|
||||
if ((c = strchr(sync_id, ':'))) {
|
||||
|
||||
*c = 0;
|
||||
|
||||
if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 ||
|
||||
!master_id || !master_max || master_id > master_max ||
|
||||
master_max > 1000000)
|
||||
FATAL("Bogus master ID passed to -M");
|
||||
|
||||
}
|
||||
|
||||
force_deterministic = 1;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
|
||||
if (sync_id) FATAL("Multiple -S or -M options not supported");
|
||||
sync_id = ck_strdup(optarg);
|
||||
break;
|
||||
|
||||
case 'f': /* target file */
|
||||
|
||||
if (out_file) FATAL("Multiple -f options not supported");
|
||||
out_file = optarg;
|
||||
break;
|
||||
|
||||
case 'x': /* dictionary */
|
||||
|
||||
if (extras_dir) FATAL("Multiple -x options not supported");
|
||||
extras_dir = optarg;
|
||||
break;
|
||||
|
||||
case 't': { /* timeout */
|
||||
|
||||
u8 suffix = 0;
|
||||
|
||||
if (timeout_given) FATAL("Multiple -t options not supported");
|
||||
|
||||
if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 ||
|
||||
optarg[0] == '-')
|
||||
FATAL("Bad syntax used for -t");
|
||||
|
||||
if (exec_tmout < 5) FATAL("Dangerously low value of -t");
|
||||
|
||||
if (suffix == '+')
|
||||
timeout_given = 2;
|
||||
else
|
||||
timeout_given = 1;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 'm': { /* mem limit */
|
||||
|
||||
u8 suffix = 'M';
|
||||
|
||||
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
||||
mem_limit_given = 1;
|
||||
|
||||
if (!strcmp(optarg, "none")) {
|
||||
|
||||
mem_limit = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
|
||||
optarg[0] == '-')
|
||||
FATAL("Bad syntax used for -m");
|
||||
|
||||
switch (suffix) {
|
||||
|
||||
case 'T': mem_limit *= 1024 * 1024; break;
|
||||
case 'G': mem_limit *= 1024; break;
|
||||
case 'k': mem_limit /= 1024; break;
|
||||
case 'M': break;
|
||||
|
||||
default: FATAL("Unsupported suffix or bad syntax for -m");
|
||||
|
||||
}
|
||||
|
||||
if (mem_limit < 5) FATAL("Dangerously low value of -m");
|
||||
|
||||
if (sizeof(rlim_t) == 4 && mem_limit > 2000)
|
||||
FATAL("Value of -m out of range on 32-bit systems");
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'd': /* skip deterministic */
|
||||
|
||||
if (skip_deterministic) FATAL("Multiple -d options not supported");
|
||||
skip_deterministic = 1;
|
||||
use_splicing = 1;
|
||||
break;
|
||||
|
||||
case 'B': /* load bitmap */
|
||||
|
||||
/* This is a secret undocumented option! It is useful if you find
|
||||
an interesting test case during a normal fuzzing process, and want
|
||||
to mutate it without rediscovering any of the test cases already
|
||||
found during an earlier run.
|
||||
|
||||
To use this mode, you need to point -B to the fuzz_bitmap produced
|
||||
by an earlier run for the exact same binary... and that's it.
|
||||
|
||||
I only used this once or twice to get variants of a particular
|
||||
file, so I'm not making this an official setting. */
|
||||
|
||||
if (in_bitmap) FATAL("Multiple -B options not supported");
|
||||
|
||||
in_bitmap = optarg;
|
||||
read_bitmap(in_bitmap);
|
||||
break;
|
||||
|
||||
case 'C': /* crash mode */
|
||||
|
||||
if (crash_mode) FATAL("Multiple -C options not supported");
|
||||
crash_mode = FAULT_CRASH;
|
||||
break;
|
||||
|
||||
case 'n': /* dumb mode */
|
||||
|
||||
if (dumb_mode) FATAL("Multiple -n options not supported");
|
||||
if (getenv("AFL_DUMB_FORKSRV"))
|
||||
dumb_mode = 2;
|
||||
else
|
||||
dumb_mode = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 'T': /* banner */
|
||||
|
||||
if (use_banner) FATAL("Multiple -T options not supported");
|
||||
use_banner = optarg;
|
||||
break;
|
||||
|
||||
case 'Q': /* QEMU mode */
|
||||
|
||||
if (qemu_mode) FATAL("Multiple -Q options not supported");
|
||||
qemu_mode = 1;
|
||||
|
||||
if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
|
||||
|
||||
break;
|
||||
|
||||
case 'U': /* Unicorn mode */
|
||||
|
||||
if (unicorn_mode) FATAL("Multiple -U options not supported");
|
||||
unicorn_mode = 1;
|
||||
|
||||
if (!mem_limit_given) mem_limit = MEM_LIMIT_UNICORN;
|
||||
|
||||
break;
|
||||
|
||||
case 'V': {
|
||||
|
||||
most_time_key = 1;
|
||||
if (sscanf(optarg, "%llu", &most_time) < 1 || optarg[0] == '-')
|
||||
FATAL("Bad syntax used for -V");
|
||||
|
||||
} break;
|
||||
|
||||
case 'E': {
|
||||
|
||||
most_execs_key = 1;
|
||||
if (sscanf(optarg, "%llu", &most_execs) < 1 || optarg[0] == '-')
|
||||
FATAL("Bad syntax used for -E");
|
||||
|
||||
} break;
|
||||
|
||||
case 'L': { /* MOpt mode */
|
||||
|
||||
if (limit_time_sig) FATAL("Multiple -L options not supported");
|
||||
limit_time_sig = 1;
|
||||
havoc_max_mult = HAVOC_MAX_MULT_MOPT;
|
||||
|
||||
if (sscanf(optarg, "%llu", &limit_time_puppet) < 1 || optarg[0] == '-')
|
||||
FATAL("Bad syntax used for -L");
|
||||
|
||||
u64 limit_time_puppet2 = limit_time_puppet * 60 * 1000;
|
||||
|
||||
if (limit_time_puppet2 < limit_time_puppet)
|
||||
FATAL("limit_time overflow");
|
||||
limit_time_puppet = limit_time_puppet2;
|
||||
|
||||
SAYF("limit_time_puppet %llu\n", limit_time_puppet);
|
||||
swarm_now = 0;
|
||||
|
||||
if (limit_time_puppet == 0) key_puppet = 1;
|
||||
|
||||
int i;
|
||||
int tmp_swarm = 0;
|
||||
|
||||
if (g_now > g_max) g_now = 0;
|
||||
w_now = (w_init - w_end) * (g_max - g_now) / (g_max) + w_end;
|
||||
|
||||
for (tmp_swarm = 0; tmp_swarm < swarm_num; ++tmp_swarm) {
|
||||
|
||||
double total_puppet_temp = 0.0;
|
||||
swarm_fitness[tmp_swarm] = 0.0;
|
||||
|
||||
for (i = 0; i < operator_num; ++i) {
|
||||
|
||||
stage_finds_puppet[tmp_swarm][i] = 0;
|
||||
probability_now[tmp_swarm][i] = 0.0;
|
||||
x_now[tmp_swarm][i] = ((double)(random() % 7000) * 0.0001 + 0.1);
|
||||
total_puppet_temp += x_now[tmp_swarm][i];
|
||||
v_now[tmp_swarm][i] = 0.1;
|
||||
L_best[tmp_swarm][i] = 0.5;
|
||||
G_best[i] = 0.5;
|
||||
eff_best[tmp_swarm][i] = 0.0;
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < operator_num; ++i) {
|
||||
|
||||
stage_cycles_puppet_v2[tmp_swarm][i] =
|
||||
stage_cycles_puppet[tmp_swarm][i];
|
||||
stage_finds_puppet_v2[tmp_swarm][i] =
|
||||
stage_finds_puppet[tmp_swarm][i];
|
||||
x_now[tmp_swarm][i] = x_now[tmp_swarm][i] / total_puppet_temp;
|
||||
|
||||
}
|
||||
|
||||
double x_temp = 0.0;
|
||||
|
||||
for (i = 0; i < operator_num; ++i) {
|
||||
|
||||
probability_now[tmp_swarm][i] = 0.0;
|
||||
v_now[tmp_swarm][i] =
|
||||
w_now * v_now[tmp_swarm][i] +
|
||||
RAND_C * (L_best[tmp_swarm][i] - x_now[tmp_swarm][i]) +
|
||||
RAND_C * (G_best[i] - x_now[tmp_swarm][i]);
|
||||
|
||||
x_now[tmp_swarm][i] += v_now[tmp_swarm][i];
|
||||
|
||||
if (x_now[tmp_swarm][i] > v_max)
|
||||
x_now[tmp_swarm][i] = v_max;
|
||||
else if (x_now[tmp_swarm][i] < v_min)
|
||||
x_now[tmp_swarm][i] = v_min;
|
||||
|
||||
x_temp += x_now[tmp_swarm][i];
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < operator_num; ++i) {
|
||||
|
||||
x_now[tmp_swarm][i] = x_now[tmp_swarm][i] / x_temp;
|
||||
if (likely(i != 0))
|
||||
probability_now[tmp_swarm][i] =
|
||||
probability_now[tmp_swarm][i - 1] + x_now[tmp_swarm][i];
|
||||
else
|
||||
probability_now[tmp_swarm][i] = x_now[tmp_swarm][i];
|
||||
|
||||
}
|
||||
|
||||
if (probability_now[tmp_swarm][operator_num - 1] < 0.99 ||
|
||||
probability_now[tmp_swarm][operator_num - 1] > 1.01)
|
||||
FATAL("ERROR probability");
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < operator_num; ++i) {
|
||||
|
||||
core_operator_finds_puppet[i] = 0;
|
||||
core_operator_finds_puppet_v2[i] = 0;
|
||||
core_operator_cycles_puppet[i] = 0;
|
||||
core_operator_cycles_puppet_v2[i] = 0;
|
||||
core_operator_cycles_puppet_v3[i] = 0;
|
||||
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
break; // not needed
|
||||
|
||||
default: usage(argv[0]);
|
||||
|
||||
}
|
||||
|
||||
if (optind == argc || !in_dir || !out_dir) usage(argv[0]);
|
||||
|
||||
if (fixed_seed) OKF("Running with fixed seed: %u", (u32)init_seed);
|
||||
srandom((u32)init_seed);
|
||||
setup_signal_handlers();
|
||||
check_asan_opts();
|
||||
|
||||
power_name = power_names[schedule];
|
||||
|
||||
if (sync_id) fix_up_sync();
|
||||
|
||||
if (!strcmp(in_dir, out_dir))
|
||||
FATAL("Input and output directories can't be the same");
|
||||
|
||||
if ((tmp_dir = getenv("AFL_TMPDIR")) != NULL) {
|
||||
|
||||
char tmpfile[strlen(tmp_dir + 16)];
|
||||
sprintf(tmpfile, "%s/%s", tmp_dir, ".cur_input");
|
||||
if (access(tmpfile, F_OK) !=
|
||||
-1) // there is still a race condition here, but well ...
|
||||
FATAL("TMP_DIR already has an existing temporary input file: %s",
|
||||
tmpfile);
|
||||
|
||||
} else
|
||||
|
||||
tmp_dir = out_dir;
|
||||
|
||||
if (dumb_mode) {
|
||||
|
||||
if (crash_mode) FATAL("-C and -n are mutually exclusive");
|
||||
if (qemu_mode) FATAL("-Q and -n are mutually exclusive");
|
||||
if (unicorn_mode) FATAL("-U and -n are mutually exclusive");
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_NO_UI") && getenv("AFL_FORCE_UI"))
|
||||
FATAL("AFL_NO_UI and AFL_FORCE_UI are mutually exclusive");
|
||||
|
||||
if (strchr(argv[optind], '/') == NULL)
|
||||
WARNF(cLRD
|
||||
"Target binary called without a prefixed path, make sure you are "
|
||||
"fuzzing the right binary: " cRST "%s",
|
||||
argv[optind]);
|
||||
|
||||
OKF("afl++ is maintained by Marc \"van Hauser\" Heuse, Heiko \"hexcoder\" "
|
||||
"Eissfeldt and Andrea Fioraldi");
|
||||
OKF("afl++ is open source, get it at "
|
||||
"https://github.com/vanhauser-thc/AFLplusplus");
|
||||
OKF("Power schedules from github.com/mboehme/aflfast");
|
||||
OKF("Python Mutator and llvm_mode whitelisting from github.com/choller/afl");
|
||||
OKF("afl-tmin fork server patch from github.com/nccgroup/TriforceAFL");
|
||||
OKF("MOpt Mutator from github.com/puppet-meteor/MOpt-AFL");
|
||||
ACTF("Getting to work...");
|
||||
|
||||
switch (schedule) {
|
||||
|
||||
case FAST: OKF("Using exponential power schedule (FAST)"); break;
|
||||
case COE: OKF("Using cut-off exponential power schedule (COE)"); break;
|
||||
case EXPLOIT:
|
||||
OKF("Using exploitation-based constant power schedule (EXPLOIT)");
|
||||
break;
|
||||
case LIN: OKF("Using linear power schedule (LIN)"); break;
|
||||
case QUAD: OKF("Using quadratic power schedule (QUAD)"); break;
|
||||
case EXPLORE:
|
||||
OKF("Using exploration-based constant power schedule (EXPLORE)");
|
||||
break;
|
||||
default: FATAL("Unknown power schedule"); break;
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1;
|
||||
if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1;
|
||||
if (getenv("AFL_NO_ARITH")) no_arith = 1;
|
||||
if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1;
|
||||
if (getenv("AFL_FAST_CAL")) fast_cal = 1;
|
||||
|
||||
if (getenv("AFL_HANG_TMOUT")) {
|
||||
|
||||
hang_tmout = atoi(getenv("AFL_HANG_TMOUT"));
|
||||
if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT");
|
||||
|
||||
}
|
||||
|
||||
if (dumb_mode == 2 && no_forkserver)
|
||||
FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive");
|
||||
|
||||
if (getenv("AFL_PRELOAD")) {
|
||||
|
||||
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
||||
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_LD_PRELOAD"))
|
||||
FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD");
|
||||
|
||||
save_cmdline(argc, argv);
|
||||
|
||||
fix_up_banner(argv[optind]);
|
||||
|
||||
check_if_tty();
|
||||
if (getenv("AFL_FORCE_UI")) not_on_tty = 0;
|
||||
|
||||
if (getenv("AFL_CAL_FAST")) {
|
||||
|
||||
/* Use less calibration cycles, for slow applications */
|
||||
cal_cycles = 3;
|
||||
cal_cycles_long = 5;
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_DEBUG")) debug = 1;
|
||||
|
||||
if (getenv("AFL_PYTHON_ONLY")) {
|
||||
|
||||
/* This ensures we don't proceed to havoc/splice */
|
||||
python_only = 1;
|
||||
|
||||
/* Ensure we also skip all deterministic steps */
|
||||
skip_deterministic = 1;
|
||||
|
||||
}
|
||||
|
||||
get_core_count();
|
||||
|
||||
#ifdef HAVE_AFFINITY
|
||||
bind_to_free_cpu();
|
||||
#endif /* HAVE_AFFINITY */
|
||||
|
||||
check_crash_handling();
|
||||
check_cpu_governor();
|
||||
|
||||
setup_post();
|
||||
setup_custom_mutator();
|
||||
setup_shm(dumb_mode);
|
||||
|
||||
if (!in_bitmap) memset(virgin_bits, 255, MAP_SIZE);
|
||||
memset(virgin_tmout, 255, MAP_SIZE);
|
||||
memset(virgin_crash, 255, MAP_SIZE);
|
||||
|
||||
init_count_class16();
|
||||
|
||||
setup_dirs_fds();
|
||||
|
||||
#ifdef USE_PYTHON
|
||||
if (init_py()) FATAL("Failed to initialize Python module");
|
||||
#else
|
||||
if (getenv("AFL_PYTHON_MODULE"))
|
||||
FATAL("Your AFL binary was built without Python support");
|
||||
#endif
|
||||
|
||||
setup_cmdline_file(argv + optind);
|
||||
|
||||
read_testcases();
|
||||
load_auto();
|
||||
|
||||
pivot_inputs();
|
||||
|
||||
if (extras_dir) load_extras(extras_dir);
|
||||
|
||||
if (!timeout_given) find_timeout();
|
||||
|
||||
/* If we don't have a file name chosen yet, use a safe default. */
|
||||
|
||||
if (!out_file) {
|
||||
|
||||
u32 i = optind + 1;
|
||||
while (argv[i]) {
|
||||
|
||||
u8* aa_loc = strstr(argv[i], "@@");
|
||||
|
||||
if (aa_loc && !out_file) {
|
||||
|
||||
if (file_extension) {
|
||||
|
||||
out_file = alloc_printf("%s/.cur_input.%s", out_dir, file_extension);
|
||||
|
||||
} else {
|
||||
|
||||
out_file = alloc_printf("%s/.cur_input", out_dir);
|
||||
|
||||
}
|
||||
|
||||
detect_file_args(argv + optind + 1, out_file);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
++i;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!out_file) setup_stdio_file();
|
||||
|
||||
check_binary(argv[optind]);
|
||||
|
||||
start_time = get_cur_time();
|
||||
|
||||
if (qemu_mode)
|
||||
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
||||
else
|
||||
use_argv = argv + optind;
|
||||
|
||||
perform_dry_run(use_argv);
|
||||
|
||||
cull_queue();
|
||||
|
||||
show_init_stats();
|
||||
|
||||
seek_to = find_start_position();
|
||||
|
||||
write_stats_file(0, 0, 0);
|
||||
save_auto();
|
||||
|
||||
if (stop_soon) goto stop_fuzzing;
|
||||
|
||||
/* Woop woop woop */
|
||||
|
||||
if (!not_on_tty) {
|
||||
|
||||
sleep(4);
|
||||
start_time += 4000;
|
||||
if (stop_soon) goto stop_fuzzing;
|
||||
|
||||
}
|
||||
|
||||
// real start time, we reset, so this works correctly with -V
|
||||
start_time = get_cur_time();
|
||||
|
||||
while (1) {
|
||||
|
||||
u8 skipped_fuzz;
|
||||
|
||||
cull_queue();
|
||||
|
||||
if (!queue_cur) {
|
||||
|
||||
++queue_cycle;
|
||||
current_entry = 0;
|
||||
cur_skipped_paths = 0;
|
||||
queue_cur = queue;
|
||||
|
||||
while (seek_to) {
|
||||
|
||||
++current_entry;
|
||||
--seek_to;
|
||||
queue_cur = queue_cur->next;
|
||||
|
||||
}
|
||||
|
||||
show_stats();
|
||||
|
||||
if (not_on_tty) {
|
||||
|
||||
ACTF("Entering queue cycle %llu.", queue_cycle);
|
||||
fflush(stdout);
|
||||
|
||||
}
|
||||
|
||||
/* If we had a full queue cycle with no new finds, try
|
||||
recombination strategies next. */
|
||||
|
||||
if (queued_paths == prev_queued) {
|
||||
|
||||
if (use_splicing)
|
||||
++cycles_wo_finds;
|
||||
else
|
||||
use_splicing = 1;
|
||||
|
||||
} else
|
||||
|
||||
cycles_wo_finds = 0;
|
||||
|
||||
prev_queued = queued_paths;
|
||||
|
||||
if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST"))
|
||||
sync_fuzzers(use_argv);
|
||||
|
||||
}
|
||||
|
||||
skipped_fuzz = fuzz_one(use_argv);
|
||||
|
||||
if (!stop_soon && sync_id && !skipped_fuzz) {
|
||||
|
||||
if (!(sync_interval_cnt++ % SYNC_INTERVAL)) sync_fuzzers(use_argv);
|
||||
|
||||
}
|
||||
|
||||
if (!stop_soon && exit_1) stop_soon = 2;
|
||||
|
||||
if (stop_soon) break;
|
||||
|
||||
queue_cur = queue_cur->next;
|
||||
++current_entry;
|
||||
|
||||
if (most_time_key == 1) {
|
||||
|
||||
u64 cur_ms_lv = get_cur_time();
|
||||
if (most_time * 1000 < cur_ms_lv - start_time) {
|
||||
|
||||
most_time_key = 2;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (most_execs_key == 1) {
|
||||
|
||||
if (most_execs <= total_execs) {
|
||||
|
||||
most_execs_key = 2;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (queue_cur) show_stats();
|
||||
|
||||
/*
|
||||
* ATTENTION - the following 10 lines were copied from a PR to Google's afl
|
||||
* repository - and slightly fixed.
|
||||
* These lines have nothing to do with the purpose of original PR though.
|
||||
* Looks like when an exit condition was completed (AFL_BENCH_JUST_ONE,
|
||||
* AFL_EXIT_WHEN_DONE or AFL_BENCH_UNTIL_CRASH) the child and forkserver
|
||||
* where not killed?
|
||||
*/
|
||||
/* if we stopped programmatically, we kill the forkserver and the current
|
||||
runner. if we stopped manually, this is done by the signal handler */
|
||||
if (stop_soon == 2) {
|
||||
|
||||
if (child_pid > 0) kill(child_pid, SIGKILL);
|
||||
if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL);
|
||||
/* Now that we've killed the forkserver, we wait for it to be able to get
|
||||
* rusage stats. */
|
||||
if (waitpid(forksrv_pid, NULL, 0) <= 0) { WARNF("error waitpid\n"); }
|
||||
|
||||
}
|
||||
|
||||
write_bitmap();
|
||||
write_stats_file(0, 0, 0);
|
||||
save_auto();
|
||||
|
||||
stop_fuzzing:
|
||||
|
||||
SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST,
|
||||
stop_soon == 2 ? "programmatically" : "by user");
|
||||
|
||||
if (most_time_key == 2) SAYF(cYEL "[!] " cRST "Time limit was reached\n");
|
||||
if (most_execs_key == 2)
|
||||
SAYF(cYEL "[!] " cRST "Execution limit was reached\n");
|
||||
|
||||
/* Running for more than 30 minutes but still doing first cycle? */
|
||||
|
||||
if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) {
|
||||
|
||||
SAYF("\n" cYEL "[!] " cRST
|
||||
"Stopped during the first cycle, results may be incomplete.\n"
|
||||
" (For info on resuming, see %s/README)\n",
|
||||
doc_path);
|
||||
|
||||
}
|
||||
|
||||
fclose(plot_file);
|
||||
destroy_queue();
|
||||
destroy_extras();
|
||||
ck_free(target_path);
|
||||
ck_free(sync_id);
|
||||
|
||||
alloc_report();
|
||||
|
||||
#ifdef USE_PYTHON
|
||||
finalize_py();
|
||||
#endif
|
||||
|
||||
OKF("We're done here. Have a nice day!\n");
|
||||
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
||||
#endif /* !AFL_LIB */
|
||||
|
@ -1,10 +1,15 @@
|
||||
/*
|
||||
american fuzzy lop - wrapper for GCC and clang
|
||||
----------------------------------------------
|
||||
american fuzzy lop++ - wrapper for GCC and clang
|
||||
------------------------------------------------
|
||||
|
||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Copyright 2013, 2014, 2015 Google Inc. All rights reserved.
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -43,19 +48,18 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static u8* as_path; /* Path to the AFL 'as' wrapper */
|
||||
static u8** cc_params; /* Parameters passed to the real CC */
|
||||
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
|
||||
static u8 be_quiet, /* Quiet mode */
|
||||
clang_mode; /* Invoked as afl-clang*? */
|
||||
|
||||
static u8* as_path; /* Path to the AFL 'as' wrapper */
|
||||
static u8** cc_params; /* Parameters passed to the real CC */
|
||||
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
|
||||
static u8 be_quiet, /* Quiet mode */
|
||||
clang_mode; /* Invoked as afl-clang*? */
|
||||
|
||||
/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
|
||||
from argv[0]. If that fails, abort. */
|
||||
|
||||
static void find_as(u8* argv0) {
|
||||
|
||||
u8 *afl_path = getenv("AFL_PATH");
|
||||
u8* afl_path = getenv("AFL_PATH");
|
||||
u8 *slash, *tmp;
|
||||
|
||||
if (afl_path) {
|
||||
@ -63,9 +67,11 @@ static void find_as(u8* argv0) {
|
||||
tmp = alloc_printf("%s/as", afl_path);
|
||||
|
||||
if (!access(tmp, X_OK)) {
|
||||
|
||||
as_path = afl_path;
|
||||
ck_free(tmp);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ck_free(tmp);
|
||||
@ -76,7 +82,7 @@ static void find_as(u8* argv0) {
|
||||
|
||||
if (slash) {
|
||||
|
||||
u8 *dir;
|
||||
u8* dir;
|
||||
|
||||
*slash = 0;
|
||||
dir = ck_strdup(argv0);
|
||||
@ -85,9 +91,11 @@ static void find_as(u8* argv0) {
|
||||
tmp = alloc_printf("%s/afl-as", dir);
|
||||
|
||||
if (!access(tmp, X_OK)) {
|
||||
|
||||
as_path = dir;
|
||||
ck_free(tmp);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ck_free(tmp);
|
||||
@ -96,21 +104,22 @@ static void find_as(u8* argv0) {
|
||||
}
|
||||
|
||||
if (!access(AFL_PATH "/as", X_OK)) {
|
||||
|
||||
as_path = AFL_PATH;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
FATAL("Unable to find AFL wrapper binary for 'as'. 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;
|
||||
u8 *name;
|
||||
u8 fortify_set = 0, asan_set = 0;
|
||||
u8* name;
|
||||
|
||||
#if defined(__FreeBSD__) && defined(__x86_64__)
|
||||
u8 m32_set = 0;
|
||||
@ -119,7 +128,10 @@ static void edit_params(u32 argc, char** argv) {
|
||||
cc_params = ck_alloc((argc + 128) * sizeof(u8*));
|
||||
|
||||
name = strrchr(argv[0], '/');
|
||||
if (!name) name = argv[0]; else name++;
|
||||
if (!name)
|
||||
name = argv[0];
|
||||
else
|
||||
name++;
|
||||
|
||||
if (!strncmp(name, "afl-clang", 9)) {
|
||||
|
||||
@ -128,11 +140,15 @@ static void edit_params(u32 argc, char** argv) {
|
||||
setenv(CLANG_ENV_VAR, "1", 1);
|
||||
|
||||
if (!strcmp(name, "afl-clang++")) {
|
||||
|
||||
u8* alt_cxx = getenv("AFL_CXX");
|
||||
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
|
||||
|
||||
} else {
|
||||
|
||||
u8* alt_cc = getenv("AFL_CC");
|
||||
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -145,16 +161,22 @@ static void edit_params(u32 argc, char** argv) {
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
if (!strcmp(name, "afl-g++")) cc_params[0] = getenv("AFL_CXX");
|
||||
else if (!strcmp(name, "afl-gcj")) cc_params[0] = getenv("AFL_GCJ");
|
||||
else cc_params[0] = getenv("AFL_CC");
|
||||
if (!strcmp(name, "afl-g++"))
|
||||
cc_params[0] = getenv("AFL_CXX");
|
||||
else if (!strcmp(name, "afl-gcj"))
|
||||
cc_params[0] = getenv("AFL_GCJ");
|
||||
else
|
||||
cc_params[0] = getenv("AFL_CC");
|
||||
|
||||
if (!cc_params[0]) {
|
||||
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
"On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n"
|
||||
" 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n"
|
||||
" set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n");
|
||||
"On Apple systems, 'gcc' is usually just a wrapper for clang. "
|
||||
"Please use the\n"
|
||||
" 'afl-clang' utility instead of 'afl-gcc'. If you really have "
|
||||
"GCC installed,\n"
|
||||
" set AFL_CC or AFL_CXX to specify the correct path to that "
|
||||
"compiler.\n");
|
||||
|
||||
FATAL("AFL_CC or AFL_CXX required on MacOS X");
|
||||
|
||||
@ -163,28 +185,41 @@ static void edit_params(u32 argc, char** argv) {
|
||||
#else
|
||||
|
||||
if (!strcmp(name, "afl-g++")) {
|
||||
|
||||
u8* alt_cxx = getenv("AFL_CXX");
|
||||
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
|
||||
|
||||
} else if (!strcmp(name, "afl-gcj")) {
|
||||
|
||||
u8* alt_cc = getenv("AFL_GCJ");
|
||||
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
|
||||
|
||||
} else {
|
||||
|
||||
u8* alt_cc = getenv("AFL_CC");
|
||||
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
|
||||
|
||||
}
|
||||
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
}
|
||||
|
||||
while (--argc) {
|
||||
|
||||
u8* cur = *(++argv);
|
||||
|
||||
if (!strncmp(cur, "-B", 2)) {
|
||||
|
||||
if (!be_quiet) WARNF("-B is already set, overriding");
|
||||
|
||||
if (!cur[2] && argc > 1) { argc--; argv++; }
|
||||
if (!cur[2] && argc > 1) {
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
@ -197,8 +232,8 @@ static void edit_params(u32 argc, char** argv) {
|
||||
if (!strcmp(cur, "-m32")) m32_set = 1;
|
||||
#endif
|
||||
|
||||
if (!strcmp(cur, "-fsanitize=address") ||
|
||||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;
|
||||
if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
|
||||
asan_set = 1;
|
||||
|
||||
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
|
||||
|
||||
@ -209,15 +244,13 @@ static void edit_params(u32 argc, char** argv) {
|
||||
cc_params[cc_par_cnt++] = "-B";
|
||||
cc_params[cc_par_cnt++] = as_path;
|
||||
|
||||
if (clang_mode)
|
||||
cc_params[cc_par_cnt++] = "-no-integrated-as";
|
||||
if (clang_mode) cc_params[cc_par_cnt++] = "-no-integrated-as";
|
||||
|
||||
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 (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
|
||||
|
||||
}
|
||||
|
||||
@ -229,8 +262,7 @@ static void edit_params(u32 argc, char** argv) {
|
||||
|
||||
} else if (getenv("AFL_USE_ASAN")) {
|
||||
|
||||
if (getenv("AFL_USE_MSAN"))
|
||||
FATAL("ASAN and MSAN are mutually exclusive");
|
||||
if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive");
|
||||
|
||||
if (getenv("AFL_HARDEN"))
|
||||
FATAL("ASAN and AFL_HARDEN are mutually exclusive");
|
||||
@ -240,8 +272,7 @@ static void edit_params(u32 argc, char** argv) {
|
||||
|
||||
} else if (getenv("AFL_USE_MSAN")) {
|
||||
|
||||
if (getenv("AFL_USE_ASAN"))
|
||||
FATAL("ASAN and MSAN are mutually exclusive");
|
||||
if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive");
|
||||
|
||||
if (getenv("AFL_HARDEN"))
|
||||
FATAL("MSAN and AFL_HARDEN are mutually exclusive");
|
||||
@ -249,11 +280,10 @@ static void edit_params(u32 argc, char** argv) {
|
||||
cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE";
|
||||
cc_params[cc_par_cnt++] = "-fsanitize=memory";
|
||||
|
||||
|
||||
}
|
||||
|
||||
#ifdef USEMMAP
|
||||
cc_params[cc_par_cnt++] = "-lrt";
|
||||
cc_params[cc_par_cnt++] = "-lrt";
|
||||
#endif
|
||||
|
||||
if (!getenv("AFL_DONT_OPTIMIZE")) {
|
||||
@ -264,12 +294,11 @@ static void edit_params(u32 argc, char** argv) {
|
||||
works OK. This has nothing to do with us, but let's avoid triggering
|
||||
that bug. */
|
||||
|
||||
if (!clang_mode || !m32_set)
|
||||
cc_params[cc_par_cnt++] = "-g";
|
||||
if (!clang_mode || !m32_set) cc_params[cc_par_cnt++] = "-g";
|
||||
|
||||
#else
|
||||
|
||||
cc_params[cc_par_cnt++] = "-g";
|
||||
cc_params[cc_par_cnt++] = "-g";
|
||||
|
||||
#endif
|
||||
|
||||
@ -300,31 +329,48 @@ static void edit_params(u32 argc, char** argv) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Main entry point */
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
if (argc == 2 && strcmp(argv[1], "-h") == 0) {
|
||||
printf("afl-cc" VERSION" by <lcamtuf@google.com>\n\n");
|
||||
printf("%s \n\n", argv[0]);
|
||||
printf("afl-gcc has no command line options\n");
|
||||
printf("NOTE: afl-gcc is deprecated, llvm_mode is much faster and has more options\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (isatty(2) && !getenv("AFL_QUIET")) {
|
||||
|
||||
SAYF(cCYA "afl-cc" VERSION cRST " by <lcamtuf@google.com>\n");
|
||||
SAYF(cYEL "[!] " cBRI "NOTE: " cRST "afl-gcc is deprecated, llvm_mode is much faster and has more options\n");
|
||||
SAYF(cYEL "[!] " cBRI "NOTE: " cRST
|
||||
"afl-gcc is deprecated, llvm_mode is much faster and has more "
|
||||
"options\n");
|
||||
|
||||
} else be_quiet = 1;
|
||||
} else
|
||||
|
||||
be_quiet = 1;
|
||||
|
||||
if (argc < 2) {
|
||||
|
||||
SAYF("\n"
|
||||
"This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
|
||||
"for gcc or clang, letting you recompile third-party code with the required\n"
|
||||
"runtime instrumentation. A common use pattern would be one of the following:\n\n"
|
||||
SAYF(
|
||||
"\n"
|
||||
"This is a helper application for afl-fuzz. It serves as a drop-in "
|
||||
"replacement\n"
|
||||
"for gcc or clang, letting you recompile third-party code with the "
|
||||
"required\n"
|
||||
"runtime instrumentation. A common use pattern would be one of the "
|
||||
"following:\n\n"
|
||||
|
||||
" CC=%s/afl-gcc ./configure\n"
|
||||
" CXX=%s/afl-g++ ./configure\n\n"
|
||||
" CC=%s/afl-gcc ./configure\n"
|
||||
" CXX=%s/afl-g++ ./configure\n\n"
|
||||
|
||||
"You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n"
|
||||
"Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
|
||||
BIN_PATH, BIN_PATH);
|
||||
"You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and "
|
||||
"AFL_AS.\n"
|
||||
"Setting AFL_HARDEN enables hardening optimizations in the compiled "
|
||||
"code.\n\n",
|
||||
BIN_PATH, BIN_PATH);
|
||||
|
||||
exit(1);
|
||||
|
||||
@ -341,3 +387,4 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
@ -2,9 +2,14 @@
|
||||
american fuzzy lop - free CPU gizmo
|
||||
-----------------------------------
|
||||
|
||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Copyright 2015, 2016 Google Inc. All rights reserved.
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -26,8 +31,13 @@
|
||||
*/
|
||||
|
||||
#define AFL_MAIN
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
@ -43,15 +53,14 @@
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef __linux__
|
||||
# define HAVE_AFFINITY 1
|
||||
#endif /* __linux__ */
|
||||
|
||||
#define HAVE_AFFINITY 1
|
||||
#endif /* __linux__ */
|
||||
|
||||
/* Get unix time in microseconds. */
|
||||
|
||||
static u64 get_cur_time_us(void) {
|
||||
|
||||
struct timeval tv;
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
gettimeofday(&tv, &tz);
|
||||
@ -60,7 +69,6 @@ static u64 get_cur_time_us(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Get CPU usage in microseconds. */
|
||||
|
||||
static u64 get_cpu_usage_us(void) {
|
||||
@ -74,7 +82,6 @@ static u64 get_cpu_usage_us(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Measure preemption rate. */
|
||||
|
||||
static u32 measure_preemption(u32 target_ms) {
|
||||
@ -91,14 +98,17 @@ repeat_loop:
|
||||
|
||||
v1 = CTEST_BUSY_CYCLES;
|
||||
|
||||
while (v1--) v2++;
|
||||
while (v1--)
|
||||
v2++;
|
||||
sched_yield();
|
||||
|
||||
en_t = get_cur_time_us();
|
||||
|
||||
if (en_t - st_t < target_ms * 1000) {
|
||||
|
||||
loop_repeats++;
|
||||
goto repeat_loop;
|
||||
|
||||
}
|
||||
|
||||
/* Let's see what percentage of this time we actually had a chance to
|
||||
@ -106,22 +116,28 @@ repeat_loop:
|
||||
|
||||
en_c = get_cpu_usage_us();
|
||||
|
||||
real_delta = (en_t - st_t) / 1000;
|
||||
real_delta = (en_t - st_t) / 1000;
|
||||
slice_delta = (en_c - st_c) / 1000;
|
||||
|
||||
return real_delta * 100 / slice_delta;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Do the benchmark thing. */
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
if (argc > 1) {
|
||||
printf("afl-gotcpu" VERSION " by <lcamtuf@google.com>\n");
|
||||
printf("\n%s \n\n", argv[0]);
|
||||
printf("afl-gotcpu does not have command line options\n");
|
||||
printf("afl-gotcpu prints out which CPUs are available\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_AFFINITY
|
||||
|
||||
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN),
|
||||
idle_cpus = 0, maybe_cpus = 0, i;
|
||||
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN), idle_cpus = 0, maybe_cpus = 0, i;
|
||||
|
||||
SAYF(cCYA "afl-gotcpu" VERSION cRST " by <lcamtuf@google.com>\n");
|
||||
|
||||
@ -137,7 +153,7 @@ int main(int argc, char** argv) {
|
||||
if (!fr) {
|
||||
|
||||
cpu_set_t c;
|
||||
u32 util_perc;
|
||||
u32 util_perc;
|
||||
|
||||
CPU_ZERO(&c);
|
||||
CPU_SET(i, &c);
|
||||
@ -154,7 +170,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
} else if (util_perc < 250) {
|
||||
|
||||
SAYF(" Core #%u: " cYEL "CAUTION " cRST "(%u%%)\n", i, util_perc);
|
||||
SAYF(" Core #%u: " cYEL "CAUTION " cRST "(%u%%)\n", i, util_perc);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
@ -247,6 +263,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
return (util_perc > 105) + (util_perc > 130);
|
||||
|
||||
#endif /* ^HAVE_AFFINITY */
|
||||
#endif /* ^HAVE_AFFINITY */
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,34 @@
|
||||
/*
|
||||
american fuzzy lop++ - shared memory related code
|
||||
-------------------------------------------------
|
||||
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
Shared code to handle the shared memory. This is used by the fuzzer
|
||||
as well the other components like afl-tmin, afl-showmap, etc...
|
||||
|
||||
*/
|
||||
|
||||
#define AFL_MAIN
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
@ -29,68 +54,79 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifndef USEMMAP
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
extern unsigned char*trace_bits;
|
||||
extern unsigned char *trace_bits;
|
||||
|
||||
#ifdef USEMMAP
|
||||
/* ================ Proteas ================ */
|
||||
int g_shm_fd = -1;
|
||||
int g_shm_fd = -1;
|
||||
unsigned char *g_shm_base = NULL;
|
||||
char g_shm_file_path[L_tmpnam];
|
||||
char g_shm_file_path[L_tmpnam];
|
||||
/* ========================================= */
|
||||
#else
|
||||
static s32 shm_id; /* ID of the SHM region */
|
||||
static s32 shm_id; /* ID of the SHM region */
|
||||
#endif
|
||||
|
||||
/* Get rid of shared memory (atexit handler). */
|
||||
|
||||
void remove_shm(void) {
|
||||
|
||||
#ifdef USEMMAP
|
||||
if (g_shm_base != NULL) {
|
||||
|
||||
munmap(g_shm_base, MAP_SIZE);
|
||||
g_shm_base = NULL;
|
||||
|
||||
}
|
||||
|
||||
if (g_shm_fd != -1) {
|
||||
|
||||
close(g_shm_fd);
|
||||
g_shm_fd = -1;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
shmctl(shm_id, IPC_RMID, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Configure shared memory. */
|
||||
|
||||
void setup_shm(unsigned char dumb_mode) {
|
||||
|
||||
#ifdef USEMMAP
|
||||
/* generate random file name for multi instance */
|
||||
|
||||
/* thanks to f*cking glibc we can not use tmpnam securely, it generates a security warning that cannot be suppressed */
|
||||
/* thanks to f*cking glibc we can not use tmpnam securely, it generates a
|
||||
* security warning that cannot be suppressed */
|
||||
/* so we do this worse workaround */
|
||||
snprintf(g_shm_file_path, L_tmpnam, "/afl_%d_%ld", getpid(), random());
|
||||
|
||||
/* create the shared memory segment as if it was a file */
|
||||
g_shm_fd = shm_open(g_shm_file_path, O_CREAT | O_RDWR | O_EXCL, 0600);
|
||||
if (g_shm_fd == -1) {
|
||||
PFATAL("shm_open() failed");
|
||||
}
|
||||
if (g_shm_fd == -1) { PFATAL("shm_open() failed"); }
|
||||
|
||||
/* configure the size of the shared memory segment */
|
||||
if (ftruncate(g_shm_fd, MAP_SIZE)) {
|
||||
|
||||
PFATAL("setup_shm(): ftruncate() failed");
|
||||
|
||||
}
|
||||
|
||||
/* map the shared memory segment to the address space of the process */
|
||||
g_shm_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, g_shm_fd, 0);
|
||||
g_shm_base =
|
||||
mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, g_shm_fd, 0);
|
||||
if (g_shm_base == MAP_FAILED) {
|
||||
|
||||
close(g_shm_fd);
|
||||
g_shm_fd = -1;
|
||||
PFATAL("mmap() failed");
|
||||
|
||||
}
|
||||
|
||||
atexit(remove_shm);
|
||||
@ -105,7 +141,7 @@ void setup_shm(unsigned char dumb_mode) {
|
||||
trace_bits = g_shm_base;
|
||||
|
||||
if (!trace_bits) PFATAL("mmap() failed");
|
||||
|
||||
|
||||
#else
|
||||
u8* shm_str;
|
||||
|
||||
@ -129,9 +165,10 @@ void setup_shm(unsigned char dumb_mode) {
|
||||
ck_free(shm_str);
|
||||
|
||||
trace_bits = shmat(shm_id, NULL, 0);
|
||||
|
||||
|
||||
if (!trace_bits) PFATAL("shmat() failed");
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,17 @@
|
||||
/*
|
||||
american fuzzy lop - map display utility
|
||||
----------------------------------------
|
||||
american fuzzy lop++ - map display utility
|
||||
------------------------------------------
|
||||
|
||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
||||
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
Forkserver design by Jann Horn <jannhorn@googlemail.com>
|
||||
|
||||
Now maintained by by Marc Heuse <mh@mh-sec.de>,
|
||||
Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
|
||||
Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -23,13 +30,16 @@
|
||||
|
||||
#define AFL_MAIN
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "android-ashmem.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "alloc-inl.h"
|
||||
#include "hash.h"
|
||||
#include "sharedmem.h"
|
||||
#include "afl-common.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
@ -48,61 +58,54 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
static s32 child_pid; /* PID of the tested program */
|
||||
static s32 child_pid; /* PID of the tested program */
|
||||
|
||||
u8* trace_bits; /* SHM with instrumentation bitmap */
|
||||
u8* trace_bits; /* SHM with instrumentation bitmap */
|
||||
|
||||
static u8 *out_file, /* Trace output file */
|
||||
*doc_path, /* Path to docs */
|
||||
*target_path, /* Path to target binary */
|
||||
*at_file; /* Substitution string for @@ */
|
||||
static u8 *out_file, /* Trace output file */
|
||||
*doc_path, /* Path to docs */
|
||||
*target_path, /* Path to target binary */
|
||||
*at_file; /* Substitution string for @@ */
|
||||
|
||||
static u32 exec_tmout; /* Exec timeout (ms) */
|
||||
static u32 exec_tmout; /* Exec timeout (ms) */
|
||||
|
||||
static u32 total, highest; /* tuple content information */
|
||||
static u32 total, highest; /* tuple content information */
|
||||
|
||||
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
||||
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
||||
|
||||
static u8 quiet_mode, /* Hide non-essential messages? */
|
||||
edges_only, /* Ignore hit counts? */
|
||||
raw_instr_output, /* Do not apply AFL filters */
|
||||
cmin_mode, /* Generate output in afl-cmin mode? */
|
||||
binary_mode, /* Write output as a binary map */
|
||||
keep_cores; /* Allow coredumps? */
|
||||
static u8 quiet_mode, /* Hide non-essential messages? */
|
||||
edges_only, /* Ignore hit counts? */
|
||||
raw_instr_output, /* Do not apply AFL filters */
|
||||
cmin_mode, /* Generate output in afl-cmin mode? */
|
||||
binary_mode, /* Write output as a binary map */
|
||||
keep_cores; /* Allow coredumps? */
|
||||
|
||||
static volatile u8
|
||||
stop_soon, /* Ctrl-C pressed? */
|
||||
child_timed_out, /* Child timed out? */
|
||||
child_crashed; /* Child crashed? */
|
||||
static volatile u8 stop_soon, /* Ctrl-C pressed? */
|
||||
child_timed_out, /* Child timed out? */
|
||||
child_crashed; /* Child crashed? */
|
||||
|
||||
/* Classify tuple counts. Instead of mapping to individual bits, as in
|
||||
afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */
|
||||
|
||||
static const u8 count_class_human[256] = {
|
||||
|
||||
[0] = 0,
|
||||
[1] = 1,
|
||||
[2] = 2,
|
||||
[3] = 3,
|
||||
[4 ... 7] = 4,
|
||||
[8 ... 15] = 5,
|
||||
[16 ... 31] = 6,
|
||||
[32 ... 127] = 7,
|
||||
[128 ... 255] = 8
|
||||
[0] = 0, [1] = 1, [2] = 2, [3] = 3,
|
||||
[4 ... 7] = 4, [8 ... 15] = 5, [16 ... 31] = 6, [32 ... 127] = 7,
|
||||
[128 ... 255] = 8
|
||||
|
||||
};
|
||||
|
||||
static const u8 count_class_binary[256] = {
|
||||
|
||||
[0] = 0,
|
||||
[1] = 1,
|
||||
[2] = 2,
|
||||
[3] = 4,
|
||||
[4 ... 7] = 8,
|
||||
[8 ... 15] = 16,
|
||||
[16 ... 31] = 32,
|
||||
[32 ... 127] = 64,
|
||||
[128 ... 255] = 128
|
||||
[0] = 0,
|
||||
[1] = 1,
|
||||
[2] = 2,
|
||||
[3] = 4,
|
||||
[4 ... 7] = 8,
|
||||
[8 ... 15] = 16,
|
||||
[16 ... 31] = 32,
|
||||
[32 ... 127] = 64,
|
||||
[128 ... 255] = 128
|
||||
|
||||
};
|
||||
|
||||
@ -113,22 +116,25 @@ static void classify_counts(u8* mem, const u8* map) {
|
||||
if (edges_only) {
|
||||
|
||||
while (i--) {
|
||||
|
||||
if (*mem) *mem = 1;
|
||||
mem++;
|
||||
|
||||
}
|
||||
|
||||
} else if (!raw_instr_output) {
|
||||
|
||||
while (i--) {
|
||||
|
||||
*mem = map[*mem];
|
||||
mem++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Write results. */
|
||||
|
||||
static u32 write_results(void) {
|
||||
@ -136,8 +142,8 @@ static u32 write_results(void) {
|
||||
s32 fd;
|
||||
u32 i, ret = 0;
|
||||
|
||||
u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"),
|
||||
caa = !!getenv("AFL_CMIN_ALLOW_ANY");
|
||||
u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"),
|
||||
caa = !!getenv("AFL_CMIN_ALLOW_ANY");
|
||||
|
||||
if (!strncmp(out_file, "/dev/", 5)) {
|
||||
|
||||
@ -151,7 +157,7 @@ static u32 write_results(void) {
|
||||
|
||||
} else {
|
||||
|
||||
unlink(out_file); /* Ignore errors */
|
||||
unlink(out_file); /* Ignore errors */
|
||||
fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fd < 0) PFATAL("Unable to create '%s'", out_file);
|
||||
|
||||
@ -161,7 +167,7 @@ static u32 write_results(void) {
|
||||
|
||||
for (i = 0; i < MAP_SIZE; i++)
|
||||
if (trace_bits[i]) ret++;
|
||||
|
||||
|
||||
ck_write(fd, trace_bits, MAP_SIZE, out_file);
|
||||
close(fd);
|
||||
|
||||
@ -175,10 +181,9 @@ static u32 write_results(void) {
|
||||
|
||||
if (!trace_bits[i]) continue;
|
||||
ret++;
|
||||
|
||||
|
||||
total += trace_bits[i];
|
||||
if (highest < trace_bits[i])
|
||||
highest = trace_bits[i];
|
||||
if (highest < trace_bits[i]) highest = trace_bits[i];
|
||||
|
||||
if (cmin_mode) {
|
||||
|
||||
@ -187,10 +192,12 @@ static u32 write_results(void) {
|
||||
|
||||
fprintf(f, "%u%u\n", trace_bits[i], i);
|
||||
|
||||
} else fprintf(f, "%06u:%u\n", i, trace_bits[i]);
|
||||
} else
|
||||
|
||||
fprintf(f, "%06u:%u\n", i, trace_bits[i]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
@ -199,7 +206,6 @@ static u32 write_results(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Handle timeout signal. */
|
||||
|
||||
static void handle_timeout(int sig) {
|
||||
@ -209,16 +215,14 @@ static void handle_timeout(int sig) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Execute target application. */
|
||||
|
||||
static void run_target(char** argv) {
|
||||
|
||||
static struct itimerval it;
|
||||
int status = 0;
|
||||
int status = 0;
|
||||
|
||||
if (!quiet_mode)
|
||||
SAYF("-- Program output begins --\n" cRST);
|
||||
if (!quiet_mode) SAYF("-- Program output begins --\n" cRST);
|
||||
|
||||
MEM_BARRIER();
|
||||
|
||||
@ -235,8 +239,10 @@ static void run_target(char** argv) {
|
||||
s32 fd = open("/dev/null", O_RDWR);
|
||||
|
||||
if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) {
|
||||
|
||||
*(u32*)trace_bits = EXEC_FAIL_SIG;
|
||||
PFATAL("Descriptor initialization failed");
|
||||
|
||||
}
|
||||
|
||||
close(fd);
|
||||
@ -249,20 +255,22 @@ static void run_target(char** argv) {
|
||||
|
||||
#ifdef RLIMIT_AS
|
||||
|
||||
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
||||
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
||||
|
||||
#else
|
||||
|
||||
setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
|
||||
setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
|
||||
|
||||
#endif /* ^RLIMIT_AS */
|
||||
#endif /* ^RLIMIT_AS */
|
||||
|
||||
}
|
||||
|
||||
if (!keep_cores) r.rlim_max = r.rlim_cur = 0;
|
||||
else r.rlim_max = r.rlim_cur = RLIM_INFINITY;
|
||||
if (!keep_cores)
|
||||
r.rlim_max = r.rlim_cur = 0;
|
||||
else
|
||||
r.rlim_max = r.rlim_cur = RLIM_INFINITY;
|
||||
|
||||
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
|
||||
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
|
||||
|
||||
if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0);
|
||||
|
||||
@ -301,14 +309,12 @@ static void run_target(char** argv) {
|
||||
if (*(u32*)trace_bits == EXEC_FAIL_SIG)
|
||||
FATAL("Unable to execute '%s'", argv[0]);
|
||||
|
||||
classify_counts(trace_bits, binary_mode ?
|
||||
count_class_binary : count_class_human);
|
||||
classify_counts(trace_bits,
|
||||
binary_mode ? count_class_binary : count_class_human);
|
||||
|
||||
if (!quiet_mode)
|
||||
SAYF(cRST "-- Program output ends --\n");
|
||||
if (!quiet_mode) SAYF(cRST "-- Program output ends --\n");
|
||||
|
||||
if (!child_timed_out && !stop_soon && WIFSIGNALED(status))
|
||||
child_crashed = 1;
|
||||
if (!child_timed_out && !stop_soon && WIFSIGNALED(status)) child_crashed = 1;
|
||||
|
||||
if (!quiet_mode) {
|
||||
|
||||
@ -317,14 +323,13 @@ static void run_target(char** argv) {
|
||||
else if (stop_soon)
|
||||
SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST);
|
||||
else if (child_crashed)
|
||||
SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST, WTERMSIG(status));
|
||||
SAYF(cLRD "\n+++ Program killed by signal %u +++\n" cRST,
|
||||
WTERMSIG(status));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Handle Ctrl-C and the like. */
|
||||
|
||||
static void handle_stop_sig(int sig) {
|
||||
@ -335,15 +340,16 @@ static void handle_stop_sig(int sig) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Do basic preparations - persistent fds, filenames, etc. */
|
||||
|
||||
static void set_up_environment(void) {
|
||||
|
||||
setenv("ASAN_OPTIONS", "abort_on_error=1:"
|
||||
"detect_leaks=0:"
|
||||
"symbolize=0:"
|
||||
"allocator_may_return_null=1", 0);
|
||||
setenv("ASAN_OPTIONS",
|
||||
"abort_on_error=1:"
|
||||
"detect_leaks=0:"
|
||||
"symbolize=0:"
|
||||
"allocator_may_return_null=1",
|
||||
0);
|
||||
|
||||
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
||||
"symbolize=0:"
|
||||
@ -352,21 +358,22 @@ static void set_up_environment(void) {
|
||||
"msan_track_origins=0", 0);
|
||||
|
||||
if (getenv("AFL_PRELOAD")) {
|
||||
|
||||
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
||||
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Setup signal handlers, duh. */
|
||||
|
||||
static void setup_signal_handlers(void) {
|
||||
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = NULL;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = NULL;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_sigaction = NULL;
|
||||
|
||||
sigemptyset(&sa.sa_mask);
|
||||
@ -385,7 +392,6 @@ static void setup_signal_handlers(void) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Show banner. */
|
||||
|
||||
static void show_banner(void) {
|
||||
@ -400,42 +406,43 @@ static void usage(u8* argv0) {
|
||||
|
||||
show_banner();
|
||||
|
||||
SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
|
||||
SAYF(
|
||||
"\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
|
||||
|
||||
"Required parameters:\n\n"
|
||||
"Required parameters:\n\n"
|
||||
|
||||
" -o file - file to write the trace data to\n\n"
|
||||
" -o file - file to write the trace data to\n\n"
|
||||
|
||||
"Execution control settings:\n\n"
|
||||
"Execution control settings:\n\n"
|
||||
|
||||
" -t msec - timeout for each run (none)\n"
|
||||
" -m megs - memory limit for child process (%u MB)\n"
|
||||
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||
" -U - use Unicorn-based instrumentation (Unicorn mode)\n"
|
||||
" (Not necessary, here for consistency with other afl-* tools)\n\n"
|
||||
" -t msec - timeout for each run (none)\n"
|
||||
" -m megs - memory limit for child process (%d MB)\n"
|
||||
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||
" -U - use Unicorn-based instrumentation (Unicorn mode)\n"
|
||||
" (Not necessary, here for consistency with other afl-* "
|
||||
"tools)\n\n"
|
||||
|
||||
"Other settings:\n\n"
|
||||
"Other settings:\n\n"
|
||||
|
||||
" -q - sink program's output and don't show messages\n"
|
||||
" -e - show edge coverage only, ignore hit counts\n"
|
||||
" -r - show real tuple values instead of AFL filter values\n"
|
||||
" -c - allow core dumps\n\n"
|
||||
" -q - sink program's output and don't show messages\n"
|
||||
" -e - show edge coverage only, ignore hit counts\n"
|
||||
" -r - show real tuple values instead of AFL filter values\n"
|
||||
" -c - allow core dumps\n\n"
|
||||
|
||||
"This tool displays raw tuple data captured by AFL instrumentation.\n"
|
||||
"For additional help, consult %s/README.\n\n" cRST,
|
||||
"This tool displays raw tuple data captured by AFL instrumentation.\n"
|
||||
"For additional help, consult %s/README.\n\n" cRST,
|
||||
|
||||
argv0, MEM_LIMIT, doc_path);
|
||||
argv0, MEM_LIMIT, doc_path);
|
||||
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Find binary. */
|
||||
|
||||
static void find_binary(u8* fname) {
|
||||
|
||||
u8* env_path = 0;
|
||||
u8* env_path = 0;
|
||||
struct stat st;
|
||||
|
||||
if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
|
||||
@ -458,7 +465,9 @@ static void find_binary(u8* fname) {
|
||||
memcpy(cur_elem, env_path, delim - env_path);
|
||||
delim++;
|
||||
|
||||
} else cur_elem = ck_strdup(env_path);
|
||||
} else
|
||||
|
||||
cur_elem = ck_strdup(env_path);
|
||||
|
||||
env_path = delim;
|
||||
|
||||
@ -470,7 +479,8 @@ static void find_binary(u8* fname) {
|
||||
ck_free(cur_elem);
|
||||
|
||||
if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
|
||||
(st.st_mode & 0111) && st.st_size >= 4) break;
|
||||
(st.st_mode & 0111) && st.st_size >= 4)
|
||||
break;
|
||||
|
||||
ck_free(target_path);
|
||||
target_path = 0;
|
||||
@ -483,13 +493,12 @@ static void find_binary(u8* fname) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Fix up argv for QEMU. */
|
||||
|
||||
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
||||
|
||||
char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
|
||||
u8 *tmp, *cp, *rsl, *own_copy;
|
||||
u8 * tmp, *cp, *rsl, *own_copy;
|
||||
|
||||
memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc);
|
||||
|
||||
@ -504,8 +513,7 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
||||
|
||||
cp = alloc_printf("%s/afl-qemu-trace", tmp);
|
||||
|
||||
if (access(cp, X_OK))
|
||||
FATAL("Unable to find '%s'", tmp);
|
||||
if (access(cp, X_OK)) FATAL("Unable to find '%s'", tmp);
|
||||
|
||||
target_path = new_argv[0] = cp;
|
||||
return new_argv;
|
||||
@ -529,7 +537,9 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
||||
|
||||
}
|
||||
|
||||
} else ck_free(own_copy);
|
||||
} else
|
||||
|
||||
ck_free(own_copy);
|
||||
|
||||
if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {
|
||||
|
||||
@ -553,7 +563,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
|
||||
|
||||
while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQUbcr")) > 0)
|
||||
while ((opt = getopt(argc, argv, "+o:m:t:A:eqZQUbcrh")) > 0)
|
||||
|
||||
switch (opt) {
|
||||
|
||||
@ -565,40 +575,41 @@ int main(int argc, char** argv) {
|
||||
|
||||
case 'm': {
|
||||
|
||||
u8 suffix = 'M';
|
||||
u8 suffix = 'M';
|
||||
|
||||
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
||||
mem_limit_given = 1;
|
||||
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
||||
mem_limit_given = 1;
|
||||
|
||||
if (!strcmp(optarg, "none")) {
|
||||
if (!strcmp(optarg, "none")) {
|
||||
|
||||
mem_limit = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
|
||||
optarg[0] == '-') FATAL("Bad syntax used for -m");
|
||||
|
||||
switch (suffix) {
|
||||
|
||||
case 'T': mem_limit *= 1024 * 1024; break;
|
||||
case 'G': mem_limit *= 1024; break;
|
||||
case 'k': mem_limit /= 1024; break;
|
||||
case 'M': break;
|
||||
|
||||
default: FATAL("Unsupported suffix or bad syntax for -m");
|
||||
|
||||
}
|
||||
|
||||
if (mem_limit < 5) FATAL("Dangerously low value of -m");
|
||||
|
||||
if (sizeof(rlim_t) == 4 && mem_limit > 2000)
|
||||
FATAL("Value of -m out of range on 32-bit systems");
|
||||
mem_limit = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
|
||||
optarg[0] == '-')
|
||||
FATAL("Bad syntax used for -m");
|
||||
|
||||
switch (suffix) {
|
||||
|
||||
case 'T': mem_limit *= 1024 * 1024; break;
|
||||
case 'G': mem_limit *= 1024; break;
|
||||
case 'k': mem_limit /= 1024; break;
|
||||
case 'M': break;
|
||||
|
||||
default: FATAL("Unsupported suffix or bad syntax for -m");
|
||||
|
||||
}
|
||||
|
||||
if (mem_limit < 5) FATAL("Dangerously low value of -m");
|
||||
|
||||
if (sizeof(rlim_t) == 4 && mem_limit > 2000)
|
||||
FATAL("Value of -m out of range on 32-bit systems");
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 't':
|
||||
|
||||
@ -606,6 +617,7 @@ int main(int argc, char** argv) {
|
||||
timeout_given = 1;
|
||||
|
||||
if (strcmp(optarg, "none")) {
|
||||
|
||||
exec_tmout = atoi(optarg);
|
||||
|
||||
if (exec_tmout < 20 || optarg[0] == '-')
|
||||
@ -633,7 +645,7 @@ int main(int argc, char** argv) {
|
||||
/* This is an undocumented option to write data in the syntax expected
|
||||
by afl-cmin. Nobody else should have any use for this. */
|
||||
|
||||
cmin_mode = 1;
|
||||
cmin_mode = 1;
|
||||
quiet_mode = 1;
|
||||
break;
|
||||
|
||||
@ -672,17 +684,20 @@ int main(int argc, char** argv) {
|
||||
if (keep_cores) FATAL("Multiple -c options not supported");
|
||||
keep_cores = 1;
|
||||
break;
|
||||
|
||||
|
||||
case 'r':
|
||||
|
||||
if (raw_instr_output) FATAL("Multiple -r options not supported");
|
||||
if (edges_only) FATAL("-e and -r are mutually exclusive");
|
||||
raw_instr_output = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
break;
|
||||
|
||||
default: usage(argv[0]);
|
||||
|
||||
}
|
||||
|
||||
@ -696,8 +711,10 @@ int main(int argc, char** argv) {
|
||||
find_binary(argv[optind]);
|
||||
|
||||
if (!quiet_mode) {
|
||||
|
||||
show_banner();
|
||||
ACTF("Executing '%s'...\n", target_path);
|
||||
|
||||
}
|
||||
|
||||
detect_file_args(argv + optind, at_file);
|
||||
@ -714,7 +731,8 @@ int main(int argc, char** argv) {
|
||||
if (!quiet_mode) {
|
||||
|
||||
if (!tcnt) FATAL("No instrumentation detected" cRST);
|
||||
OKF("Captured %u tuples (highest value %u, total values %u) in '%s'." cRST, tcnt, highest, total, out_file);
|
||||
OKF("Captured %u tuples (highest value %u, total values %u) in '%s'." cRST,
|
||||
tcnt, highest, total, out_file);
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
10
test-instr.c
10
test-instr.c
@ -20,11 +20,16 @@
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
char buf[8];
|
||||
char buff[8];
|
||||
char* buf = buff;
|
||||
|
||||
if (argc > 1)
|
||||
buf = argv[1];
|
||||
else if (read(0, buf, sizeof(buf)) < 1) {
|
||||
|
||||
if (read(0, buf, sizeof(buf)) < 1) {
|
||||
printf("Hum?\n");
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
if (buf[0] == '0')
|
||||
@ -37,3 +42,4 @@ int main(int argc, char** argv) {
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
||||
|
91
types.h
91
types.h
@ -1,91 +0,0 @@
|
||||
/*
|
||||
american fuzzy lop - type definitions and minor macros
|
||||
------------------------------------------------------
|
||||
|
||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Copyright 2013, 2014, 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _HAVE_TYPES_H
|
||||
#define _HAVE_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
|
||||
/*
|
||||
|
||||
Ugh. There is an unintended compiler / glibc #include glitch caused by
|
||||
combining the u64 type an %llu in format strings, necessitating a workaround.
|
||||
|
||||
In essence, the compiler is always looking for 'unsigned long long' for %llu.
|
||||
On 32-bit systems, the u64 type (aliased to uint64_t) is expanded to
|
||||
'unsigned long long' in <bits/types.h>, so everything checks out.
|
||||
|
||||
But on 64-bit systems, it is #ifdef'ed in the same file as 'unsigned long'.
|
||||
Now, it only happens in circumstances where the type happens to have the
|
||||
expected bit width, *but* the compiler does not know that... and complains
|
||||
about 'unsigned long' being unsafe to pass to %llu.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef __x86_64__
|
||||
typedef unsigned long long u64;
|
||||
#else
|
||||
typedef uint64_t u64;
|
||||
#endif /* ^__x86_64__ */
|
||||
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
#ifndef MIN
|
||||
# define MIN(_a,_b) ((_a) > (_b) ? (_b) : (_a))
|
||||
# define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b))
|
||||
#endif /* !MIN */
|
||||
|
||||
#define SWAP16(_x) ({ \
|
||||
u16 _ret = (_x); \
|
||||
(u16)((_ret << 8) | (_ret >> 8)); \
|
||||
})
|
||||
|
||||
#define SWAP32(_x) ({ \
|
||||
u32 _ret = (_x); \
|
||||
(u32)((_ret << 24) | (_ret >> 24) | \
|
||||
((_ret << 8) & 0x00FF0000) | \
|
||||
((_ret >> 8) & 0x0000FF00)); \
|
||||
})
|
||||
|
||||
#ifdef AFL_LLVM_PASS
|
||||
# define AFL_R(x) (random() % (x))
|
||||
#else
|
||||
# define R(x) (random() % (x))
|
||||
#endif /* ^AFL_LLVM_PASS */
|
||||
|
||||
#define STRINGIFY_INTERNAL(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
|
||||
|
||||
#define MEM_BARRIER() \
|
||||
__asm__ volatile("" ::: "memory")
|
||||
|
||||
#if __GNUC__ < 6
|
||||
#define likely(_x) (_x)
|
||||
#define unlikely(_x) (_x)
|
||||
#else
|
||||
#define likely(_x) __builtin_expect(!!(_x), 1)
|
||||
#define unlikely(_x) __builtin_expect(!!(_x), 0)
|
||||
#endif
|
||||
|
||||
#endif /* ! _HAVE_TYPES_H */
|
@ -1,23 +1,119 @@
|
||||
```
|
||||
__ _ _
|
||||
__ _ / _| | _ _ _ __ (_) ___ ___ _ __ _ __
|
||||
/ _` | |_| |___| | | | '_ \| |/ __/ _ \| '__| '_ \
|
||||
| (_| | _| |___| |_| | | | | | (_| (_) | | | | | |
|
||||
\__,_|_| |_| \__,_|_| |_|_|\___\___/|_| |_| |_|
|
||||
|
||||
```
|
||||
# Unicorn-based binary-only instrumentation for afl-fuzz
|
||||
|
||||
afl-unicorn lets you fuzz any piece of binary that can be emulated by
|
||||
[Unicorn Engine](http://www.unicorn-engine.org/).
|
||||
The idea and much of the original implementation comes from Nathan Voss <njvoss299@gmail.com>.
|
||||
|
||||
Requirements: Python2
|
||||
The port to afl++ if by Dominik Maier <mail@dmnk.co>.
|
||||
|
||||
For the full readme please see docs/unicorn_mode.txt
|
||||
The CompareCoverage and NeverZero counters features by Andrea Fioraldi <andreafioraldi@gmail.com>.
|
||||
|
||||
For an in-depth description of what this is, how to install it, and how to use
|
||||
it check out this [blog post](https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf).
|
||||
## 1) Introduction
|
||||
|
||||
For general help with AFL, please refer to the documents in the ./docs/ directory.
|
||||
The code in ./unicorn_mode allows you to build a standalone feature that
|
||||
leverages the Unicorn Engine and allows callers to obtain instrumentation
|
||||
output for black-box, closed-source binary code snippets. This mechanism
|
||||
can be then used by afl-fuzz to stress-test targets that couldn't be built
|
||||
with afl-gcc or used in QEMU mode, or with other extensions such as
|
||||
TriforceAFL.
|
||||
|
||||
Created by Nathan Voss, originally funded by
|
||||
[Battelle](https://www.battelle.org/cyber).
|
||||
There is a significant performance penalty compared to native AFL,
|
||||
but at least we're able to use AFL on these binaries, right?
|
||||
|
||||
## 2) How to use
|
||||
|
||||
Requirements: you need an installed python2 environment.
|
||||
|
||||
### Building AFL's Unicorn Mode
|
||||
|
||||
First, make afl++ as usual.
|
||||
Once that completes successfully you need to build and add in the Unicorn Mode
|
||||
features:
|
||||
|
||||
$ cd unicorn_mode
|
||||
$ ./build_unicorn_support.sh
|
||||
|
||||
NOTE: This script downloads a Unicorn Engine commit that has been tested
|
||||
and is stable-ish from the Unicorn github page. If you are offline, you'll need
|
||||
to hack up this script a little bit and supply your own copy of Unicorn's latest
|
||||
stable release. It's not very hard, just check out the beginning of the
|
||||
build_unicorn_support.sh script and adjust as necessary.
|
||||
|
||||
Building Unicorn will take a little bit (~5-10 minutes). Once it completes
|
||||
it automatically compiles a sample application and verify that it works.
|
||||
|
||||
### Fuzzing with Unicorn Mode
|
||||
|
||||
To really use unicorn-mode effectively you need to prepare the following:
|
||||
|
||||
* Relevant binary code to be fuzzed
|
||||
* Knowledge of the memory map and good starting state
|
||||
* Folder containing sample inputs to start fuzzing with
|
||||
+ Same ideas as any other AFL inputs
|
||||
+ Quality/speed of results will depend greatly on quality of starting
|
||||
samples
|
||||
+ See AFL's guidance on how to create a sample corpus
|
||||
* Unicorn-based test harness which:
|
||||
+ Adds memory map regions
|
||||
+ Loads binary code into memory
|
||||
+ Emulates at least one instruction*
|
||||
+ Yeah, this is lame. See 'Gotchas' section below for more info
|
||||
+ Loads and verifies data to fuzz from a command-line specified file
|
||||
+ AFL will provide mutated inputs by changing the file passed to
|
||||
the test harness
|
||||
+ Presumably the data to be fuzzed is at a fixed buffer address
|
||||
+ If input constraints (size, invalid bytes, etc.) are known they
|
||||
should be checked after the file is loaded. If a constraint
|
||||
fails, just exit the test harness. AFL will treat the input as
|
||||
'uninteresting' and move on.
|
||||
+ Sets up registers and memory state for beginning of test
|
||||
+ Emulates the interested code from beginning to end
|
||||
+ If a crash is detected, the test harness must 'crash' by
|
||||
throwing a signal (SIGSEGV, SIGKILL, SIGABORT, etc.)
|
||||
|
||||
Once you have all those things ready to go you just need to run afl-fuzz in
|
||||
'unicorn-mode' by passing in the '-U' flag:
|
||||
|
||||
$ afl-fuzz -U -m none -i /path/to/inputs -o /path/to/results -- ./test_harness @@
|
||||
|
||||
The normal afl-fuzz command line format applies to everything here. Refer to
|
||||
AFL's main documentation for more info about how to use afl-fuzz effectively.
|
||||
|
||||
For a much clearer vision of what all of this looks like, please refer to the
|
||||
sample provided in the 'unicorn_mode/samples' directory. There is also a blog
|
||||
post that goes over the basics at:
|
||||
|
||||
https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf
|
||||
|
||||
The 'helper_scripts' directory also contains several helper scripts that allow you
|
||||
to dump context from a running process, load it, and hook heap allocations. For details
|
||||
on how to use this check out the follow-up blog post to the one linked above.
|
||||
|
||||
A example use of AFL-Unicorn mode is discussed in the Paper Unicorefuzz:
|
||||
https://www.usenix.org/conference/woot19/presentation/maier
|
||||
|
||||
## 3) Options
|
||||
|
||||
As for the QEMU-based instrumentation, the afl-unicorn twist of afl++
|
||||
comes with a sub-instruction based instrumentation similar in purpose to laf-intel.
|
||||
|
||||
The options that enables Unicorn CompareCoverage are the same used for QEMU.
|
||||
AFL_COMPCOV_LEVEL=1 is to instrument comparisons with only immediate
|
||||
values. QEMU_COMPCOV_LEVEL=2 instruments all
|
||||
comparison instructions. Comparison instructions are currently instrumented only
|
||||
on the x86 and x86_64 targets.
|
||||
|
||||
## 4) Gotchas, feedback, bugs
|
||||
|
||||
To make sure that AFL's fork server starts up correctly the Unicorn test
|
||||
harness script must emulate at least one instruction before loading the
|
||||
data that will be fuzzed from the input file. It doesn't matter what the
|
||||
instruction is, nor if it is valid. This is an artifact of how the fork-server
|
||||
is started and could likely be fixed with some clever re-arranging of the
|
||||
patches applied to Unicorn.
|
||||
|
||||
Running the build script builds Unicorn and its python bindings and installs
|
||||
them on your system. This installation will supersede any existing Unicorn
|
||||
installation with the patched afl-unicorn version.
|
||||
|
||||
Refer to the unicorn_mode/samples/arm_example/arm_tester.c for an example
|
||||
of how to do this properly! If you don't get this right, AFL will not
|
||||
load any mutated inputs and your fuzzing will be useless!
|
||||
|
21
unicorn_mode/build_unicorn_support.sh
Normal file → Executable file
21
unicorn_mode/build_unicorn_support.sh
Normal file → Executable file
@ -1,16 +1,20 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# american fuzzy lop - Unicorn-Mode build script
|
||||
# --------------------------------------
|
||||
# american fuzzy lop++ - unicorn mode build script
|
||||
# ------------------------------------------------
|
||||
#
|
||||
# Written by Nathan Voss <njvoss99@gmail.com>
|
||||
# Originally written by Nathan Voss <njvoss99@gmail.com>
|
||||
#
|
||||
# Adapted from code by Andrew Griffiths <agriffiths@google.com> and
|
||||
# Michal Zalewski <lcamtuf@google.com>
|
||||
#
|
||||
# Adapted for Afl++ by Dominik Maier <mail@dmnk.co>
|
||||
# Adapted for AFLplusplus by Dominik Maier <mail@dmnk.co>
|
||||
#
|
||||
# CompareCoverage and NeverZero counters by Andrea Fioraldi
|
||||
# <andreafioraldi@gmail.com>
|
||||
#
|
||||
# Copyright 2017 Battelle Memorial Institute. All rights reserved.
|
||||
# Copyright 2019 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.
|
||||
@ -127,12 +131,13 @@ tar xzf "$ARCHIVE" -C ./unicorn --strip-components=1 || exit 1
|
||||
|
||||
echo "[+] Unpacking successful."
|
||||
|
||||
rm -rf "$ARCHIVE" || exit 1
|
||||
#rm -rf "$ARCHIVE" || exit 1
|
||||
|
||||
echo "[*] Applying patches..."
|
||||
|
||||
cp patches/afl-unicorn-cpu-inl.h unicorn || exit 1
|
||||
patch -p1 --directory unicorn <patches/patches.diff || exit 1
|
||||
cp patches/*.h unicorn || exit 1
|
||||
patch -p1 --directory unicorn < patches/patches.diff || exit 1
|
||||
patch -p1 --directory unicorn < patches/compcov.diff || exit 1
|
||||
|
||||
echo "[+] Patching done."
|
||||
|
||||
@ -144,7 +149,7 @@ echo "[+] Configuration complete."
|
||||
|
||||
echo "[*] Attempting to build Unicorn (fingers crossed!)..."
|
||||
|
||||
UNICORN_QEMU_FLAGS='--python=python2' make || exit 1
|
||||
UNICORN_QEMU_FLAGS='--python=python2' make -j `nproc` || exit 1
|
||||
|
||||
echo "[+] Build process successful!"
|
||||
|
||||
|
48
unicorn_mode/patches/afl-unicorn-common.h
Normal file
48
unicorn_mode/patches/afl-unicorn-common.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
american fuzzy lop++ - unicorn instrumentation
|
||||
----------------------------------------------
|
||||
|
||||
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Adapted for afl-unicorn by Dominik Maier <mail@dmnk.co>
|
||||
|
||||
CompareCoverage and NeverZero counters by Andrea Fioraldi
|
||||
<andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This code is a shim patched into the separately-distributed source
|
||||
code of Unicorn 1.0.1. It leverages the built-in QEMU tracing functionality
|
||||
to implement AFL-style instrumentation and to take care of the remaining
|
||||
parts of the AFL fork server logic.
|
||||
|
||||
The resulting libunicorn binary is essentially a standalone instrumentation
|
||||
tool; for an example of how to leverage it for other purposes, you can
|
||||
have a look at afl-showmap.c.
|
||||
|
||||
*/
|
||||
|
||||
#include "../../config.h"
|
||||
|
||||
/* NeverZero */
|
||||
|
||||
#if (defined(__x86_64__) || defined(__i386__)) && defined(AFL_QEMU_NOT_ZERO)
|
||||
#define INC_AFL_AREA(loc) \
|
||||
asm volatile( \
|
||||
"incb (%0, %1, 1)\n" \
|
||||
"adcb $0, (%0, %1, 1)\n" \
|
||||
: /* no out */ \
|
||||
: "r"(afl_area_ptr), "r"(loc) \
|
||||
: "memory", "eax")
|
||||
#else
|
||||
#define INC_AFL_AREA(loc) afl_area_ptr[loc]++
|
||||
#endif
|
||||
|
@ -1,17 +1,17 @@
|
||||
/*
|
||||
american fuzzy lop - high-performance binary-only instrumentation
|
||||
-----------------------------------------------------------------
|
||||
american fuzzy lop++ - unicorn instrumentation
|
||||
----------------------------------------------
|
||||
|
||||
Written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
TCG instrumentation and block chaining support by Andrea Biondo
|
||||
<andrea.biondo965@gmail.com>
|
||||
Adapted for afl-unicorn by Dominik Maier <mail@dmnk.co>
|
||||
|
||||
Idea & design very much by Andrew Griffiths.
|
||||
CompareCoverage and NeverZero counters by Andrea Fioraldi
|
||||
<andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2015, 2016 Google Inc. All rights reserved.
|
||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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.
|
||||
@ -24,7 +24,7 @@
|
||||
to implement AFL-style instrumentation and to take care of the remaining
|
||||
parts of the AFL fork server logic.
|
||||
|
||||
The resulting QEMU binary is essentially a standalone instrumentation
|
||||
The resulting libunicorn binary is essentially a standalone instrumentation
|
||||
tool; for an example of how to leverage it for other purposes, you can
|
||||
have a look at afl-showmap.c.
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
#include <sys/shm.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include "../../config.h"
|
||||
#include "afl-unicorn-common.h"
|
||||
|
||||
/***************************
|
||||
* VARIOUS AUXILIARY STUFF *
|
||||
@ -44,21 +44,29 @@
|
||||
it to translate within its own context, too (this avoids translation
|
||||
overhead in the next forked-off copy). */
|
||||
|
||||
#define AFL_UNICORN_CPU_SNIPPET1 do { \
|
||||
#define AFL_UNICORN_CPU_SNIPPET1 \
|
||||
do { \
|
||||
\
|
||||
afl_request_tsl(pc, cs_base, flags); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* This snippet kicks in when the instruction pointer is positioned at
|
||||
_start and does the usual forkserver stuff, not very different from
|
||||
regular instrumentation injected via afl-as.h. */
|
||||
|
||||
#define AFL_UNICORN_CPU_SNIPPET2 do { \
|
||||
if(unlikely(afl_first_instr == 0)) { \
|
||||
afl_setup(); \
|
||||
afl_forkserver(env); \
|
||||
afl_first_instr = 1; \
|
||||
} \
|
||||
afl_maybe_log(tb->pc); \
|
||||
#define AFL_UNICORN_CPU_SNIPPET2 \
|
||||
do { \
|
||||
\
|
||||
if (unlikely(afl_first_instr == 0)) { \
|
||||
\
|
||||
afl_setup(env->uc); \
|
||||
afl_forkserver(env); \
|
||||
afl_first_instr = 1; \
|
||||
\
|
||||
} \
|
||||
afl_maybe_log(env->uc, tb->pc); \
|
||||
\
|
||||
} while (0)
|
||||
|
||||
/* We use one additional file descriptor to relay "needs translation"
|
||||
@ -66,37 +74,31 @@
|
||||
|
||||
#define TSL_FD (FORKSRV_FD - 1)
|
||||
|
||||
/* This is equivalent to afl-as.h: */
|
||||
|
||||
static unsigned char *afl_area_ptr;
|
||||
|
||||
/* Set in the child process in forkserver mode: */
|
||||
|
||||
static unsigned char afl_fork_child;
|
||||
static unsigned int afl_forksrv_pid;
|
||||
|
||||
/* Instrumentation ratio: */
|
||||
|
||||
static unsigned int afl_inst_rms = MAP_SIZE;
|
||||
static unsigned int afl_forksrv_pid;
|
||||
|
||||
/* Function declarations. */
|
||||
|
||||
static void afl_setup(void);
|
||||
static void afl_forkserver(CPUArchState*);
|
||||
static inline void afl_maybe_log(unsigned long);
|
||||
static void afl_setup(struct uc_struct* uc);
|
||||
static void afl_forkserver(CPUArchState*);
|
||||
static inline void afl_maybe_log(struct uc_struct* uc, unsigned long);
|
||||
|
||||
static void afl_wait_tsl(CPUArchState*, int);
|
||||
static void afl_request_tsl(target_ulong, target_ulong, uint64_t);
|
||||
|
||||
static TranslationBlock *tb_find_slow(CPUArchState*, target_ulong,
|
||||
target_ulong, uint64_t);
|
||||
static TranslationBlock* tb_find_slow(CPUArchState*, target_ulong, target_ulong,
|
||||
uint64_t);
|
||||
|
||||
/* Data structure passed around by the translate handlers: */
|
||||
|
||||
struct afl_tsl {
|
||||
|
||||
target_ulong pc;
|
||||
target_ulong cs_base;
|
||||
uint64_t flags;
|
||||
uint64_t flags;
|
||||
|
||||
};
|
||||
|
||||
/*************************
|
||||
@ -105,10 +107,9 @@ struct afl_tsl {
|
||||
|
||||
/* Set up SHM region and initialize other stuff. */
|
||||
|
||||
static void afl_setup(void) {
|
||||
static void afl_setup(struct uc_struct* uc) {
|
||||
|
||||
char *id_str = getenv(SHM_ENV_VAR),
|
||||
*inst_r = getenv("AFL_INST_RATIO");
|
||||
char *id_str = getenv(SHM_ENV_VAR), *inst_r = getenv("AFL_INST_RATIO");
|
||||
|
||||
int shm_id;
|
||||
|
||||
@ -121,31 +122,45 @@ static void afl_setup(void) {
|
||||
if (r > 100) r = 100;
|
||||
if (!r) r = 1;
|
||||
|
||||
afl_inst_rms = MAP_SIZE * r / 100;
|
||||
uc->afl_inst_rms = MAP_SIZE * r / 100;
|
||||
|
||||
} else {
|
||||
|
||||
uc->afl_inst_rms = MAP_SIZE;
|
||||
|
||||
}
|
||||
|
||||
if (id_str) {
|
||||
|
||||
shm_id = atoi(id_str);
|
||||
afl_area_ptr = shmat(shm_id, NULL, 0);
|
||||
uc->afl_area_ptr = shmat(shm_id, NULL, 0);
|
||||
|
||||
if (afl_area_ptr == (void*)-1) exit(1);
|
||||
if (uc->afl_area_ptr == (void*)-1) exit(1);
|
||||
|
||||
/* With AFL_INST_RATIO set to a low value, we want to touch the bitmap
|
||||
so that the parent doesn't give up on us. */
|
||||
|
||||
if (inst_r) afl_area_ptr[0] = 1;
|
||||
if (inst_r) uc->afl_area_ptr[0] = 1;
|
||||
|
||||
}
|
||||
|
||||
/* Maintain for compatibility */
|
||||
if (getenv("AFL_QEMU_COMPCOV")) { uc->afl_compcov_level = 1; }
|
||||
if (getenv("AFL_COMPCOV_LEVEL")) {
|
||||
|
||||
uc->afl_compcov_level = atoi(getenv("AFL_COMPCOV_LEVEL"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Fork server logic, invoked once we hit first emulated instruction. */
|
||||
|
||||
static void afl_forkserver(CPUArchState *env) {
|
||||
static void afl_forkserver(CPUArchState* env) {
|
||||
|
||||
static unsigned char tmp[4];
|
||||
|
||||
if (!afl_area_ptr) return;
|
||||
if (!env->uc->afl_area_ptr) return;
|
||||
|
||||
/* Tell the parent that we're alive. If the parent doesn't want
|
||||
to talk, assume that we're not running in forkserver mode. */
|
||||
@ -159,13 +174,13 @@ static void afl_forkserver(CPUArchState *env) {
|
||||
while (1) {
|
||||
|
||||
pid_t child_pid;
|
||||
int status, t_fd[2];
|
||||
int status, t_fd[2];
|
||||
|
||||
/* Whoops, parent dead? */
|
||||
|
||||
if (read(FORKSRV_FD, tmp, 4) != 4) exit(2);
|
||||
|
||||
/* Establish a channel with child to grab translation commands. We'll
|
||||
/* Establish a channel with child to grab translation commands. We'll
|
||||
read from t_fd[0], child will write to TSL_FD. */
|
||||
|
||||
if (pipe(t_fd) || dup2(t_fd[1], TSL_FD) < 0) exit(3);
|
||||
@ -205,48 +220,36 @@ static void afl_forkserver(CPUArchState *env) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* The equivalent of the tuple logging routine from afl-as.h. */
|
||||
|
||||
static inline void afl_maybe_log(unsigned long cur_loc) {
|
||||
static inline void afl_maybe_log(struct uc_struct* uc, unsigned long cur_loc) {
|
||||
|
||||
static __thread unsigned long prev_loc;
|
||||
|
||||
// DEBUG
|
||||
//printf("IN AFL_MAYBE_LOG 0x%lx\n", cur_loc);
|
||||
u8* afl_area_ptr = uc->afl_area_ptr;
|
||||
|
||||
// MODIFIED FOR UNICORN MODE -> We want to log all addresses,
|
||||
// so the checks for 'start < addr < end' are removed
|
||||
if(!afl_area_ptr)
|
||||
return;
|
||||
|
||||
// DEBUG
|
||||
//printf("afl_area_ptr = %p\n", afl_area_ptr);
|
||||
if (!afl_area_ptr) return;
|
||||
|
||||
/* Looks like QEMU always maps to fixed locations, so ASAN is not a
|
||||
concern. Phew. But instruction addresses may be aligned. Let's mangle
|
||||
the value to get something quasi-uniform. */
|
||||
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
/* Implement probabilistic instrumentation by looking at scrambled block
|
||||
address. This keeps the instrumented locations stable across runs. */
|
||||
|
||||
// DEBUG
|
||||
//printf("afl_inst_rms = 0x%lx\n", afl_inst_rms);
|
||||
if (cur_loc >= uc->afl_inst_rms) return;
|
||||
|
||||
if (cur_loc >= afl_inst_rms) return;
|
||||
register uintptr_t afl_idx = cur_loc ^ prev_loc;
|
||||
|
||||
// DEBUG
|
||||
//printf("cur_loc = 0x%lx\n", cur_loc);
|
||||
INC_AFL_AREA(afl_idx);
|
||||
|
||||
afl_area_ptr[cur_loc ^ prev_loc]++;
|
||||
prev_loc = cur_loc >> 1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* This code is invoked whenever QEMU decides that it doesn't have a
|
||||
translation of a particular block and needs to compute it. When this happens,
|
||||
we tell the parent to mirror the operation, so that the next fork() has a
|
||||
@ -258,20 +261,19 @@ static void afl_request_tsl(target_ulong pc, target_ulong cb, uint64_t flags) {
|
||||
|
||||
if (!afl_fork_child) return;
|
||||
|
||||
t.pc = pc;
|
||||
t.pc = pc;
|
||||
t.cs_base = cb;
|
||||
t.flags = flags;
|
||||
t.flags = flags;
|
||||
|
||||
if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* This is the other side of the same channel. Since timeouts are handled by
|
||||
afl-fuzz simply killing the child, we can just wait until the pipe breaks. */
|
||||
|
||||
static void afl_wait_tsl(CPUArchState *env, int fd) {
|
||||
static void afl_wait_tsl(CPUArchState* env, int fd) {
|
||||
|
||||
struct afl_tsl t;
|
||||
|
||||
@ -279,12 +281,13 @@ static void afl_wait_tsl(CPUArchState *env, int fd) {
|
||||
|
||||
/* Broken pipe means it's time to return to the fork server routine. */
|
||||
|
||||
if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
||||
break;
|
||||
if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) break;
|
||||
|
||||
tb_find_slow(env, t.pc, t.cs_base, t.flags);
|
||||
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
|
57
unicorn_mode/patches/afl-unicorn-cpu-translate-inl.h
Normal file
57
unicorn_mode/patches/afl-unicorn-cpu-translate-inl.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
american fuzzy lop++ - unicorn instrumentation
|
||||
----------------------------------------------
|
||||
|
||||
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Adapted for afl-unicorn by Dominik Maier <mail@dmnk.co>
|
||||
|
||||
CompareCoverage and NeverZero counters by Andrea Fioraldi
|
||||
<andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This code is a shim patched into the separately-distributed source
|
||||
code of Unicorn 1.0.1. It leverages the built-in QEMU tracing functionality
|
||||
to implement AFL-style instrumentation and to take care of the remaining
|
||||
parts of the AFL fork server logic.
|
||||
|
||||
The resulting libunicorn binary is essentially a standalone instrumentation
|
||||
tool; for an example of how to leverage it for other purposes, you can
|
||||
have a look at afl-showmap.c.
|
||||
|
||||
*/
|
||||
|
||||
#include "../../config.h"
|
||||
|
||||
static void afl_gen_compcov(TCGContext *s, uint64_t cur_loc, TCGv_i64 arg1,
|
||||
TCGv_i64 arg2, TCGMemOp ot, int is_imm) {
|
||||
|
||||
if (!s->uc->afl_compcov_level || !s->uc->afl_area_ptr) return;
|
||||
|
||||
if (!is_imm && s->uc->afl_compcov_level < 2) return;
|
||||
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 7;
|
||||
|
||||
if (cur_loc >= s->uc->afl_inst_rms) return;
|
||||
|
||||
switch (ot) {
|
||||
|
||||
case MO_64: gen_afl_compcov_log_64(s, cur_loc, arg1, arg2); break;
|
||||
case MO_32: gen_afl_compcov_log_32(s, cur_loc, arg1, arg2); break;
|
||||
case MO_16: gen_afl_compcov_log_16(s, cur_loc, arg1, arg2); break;
|
||||
default: return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
59
unicorn_mode/patches/afl-unicorn-tcg-op-inl.h
Normal file
59
unicorn_mode/patches/afl-unicorn-tcg-op-inl.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
american fuzzy lop++ - unicorn instrumentation
|
||||
----------------------------------------------
|
||||
|
||||
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||
Michal Zalewski <lcamtuf@google.com>
|
||||
|
||||
Adapted for afl-unicorn by Dominik Maier <mail@dmnk.co>
|
||||
|
||||
CompareCoverage and NeverZero counters by Andrea Fioraldi
|
||||
<andreafioraldi@gmail.com>
|
||||
|
||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019 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
|
||||
|
||||
This code is a shim patched into the separately-distributed source
|
||||
code of Unicorn 1.0.1. It leverages the built-in QEMU tracing functionality
|
||||
to implement AFL-style instrumentation and to take care of the remaining
|
||||
parts of the AFL fork server logic.
|
||||
|
||||
The resulting libunicorn binary is essentially a standalone instrumentation
|
||||
tool; for an example of how to leverage it for other purposes, you can
|
||||
have a look at afl-showmap.c.
|
||||
|
||||
*/
|
||||
|
||||
static inline void gen_afl_compcov_log_16(TCGContext *tcg_ctx, uint64_t cur_loc,
|
||||
TCGv_i64 arg1, TCGv_i64 arg2) {
|
||||
|
||||
TCGv_ptr tuc = tcg_const_ptr(tcg_ctx, tcg_ctx->uc);
|
||||
TCGv_i64 tcur_loc = tcg_const_i64(tcg_ctx, cur_loc);
|
||||
gen_helper_afl_compcov_log_16(tcg_ctx, tuc, tcur_loc, arg1, arg2);
|
||||
|
||||
}
|
||||
|
||||
static inline void gen_afl_compcov_log_32(TCGContext *tcg_ctx, uint64_t cur_loc,
|
||||
TCGv_i64 arg1, TCGv_i64 arg2) {
|
||||
|
||||
TCGv_ptr tuc = tcg_const_ptr(tcg_ctx, tcg_ctx->uc);
|
||||
TCGv_i64 tcur_loc = tcg_const_i64(tcg_ctx, cur_loc);
|
||||
gen_helper_afl_compcov_log_32(tcg_ctx, tuc, tcur_loc, arg1, arg2);
|
||||
|
||||
}
|
||||
|
||||
static inline void gen_afl_compcov_log_64(TCGContext *tcg_ctx, uint64_t cur_loc,
|
||||
TCGv_i64 arg1, TCGv_i64 arg2) {
|
||||
|
||||
TCGv_ptr tuc = tcg_const_ptr(tcg_ctx, tcg_ctx->uc);
|
||||
TCGv_i64 tcur_loc = tcg_const_i64(tcg_ctx, cur_loc);
|
||||
gen_helper_afl_compcov_log_64(tcg_ctx, tuc, tcur_loc, arg1, arg2);
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user