mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-09 08:41: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
|
*.o
|
||||||
*.so
|
*.so
|
||||||
.gitignore
|
|
||||||
afl-analyze
|
afl-analyze
|
||||||
afl-as
|
afl-as
|
||||||
afl-clang
|
afl-clang
|
||||||
@ -21,3 +20,4 @@ qemu_mode/qemu-3.1.0
|
|||||||
qemu_mode/qemu-3.1.0.tar.xz
|
qemu_mode/qemu-3.1.0.tar.xz
|
||||||
unicorn_mode/unicorn
|
unicorn_mode/unicorn
|
||||||
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
|
#TEST_MMAP=1
|
||||||
|
|
||||||
PROGNAME = afl
|
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
|
PREFIX ?= /usr/local
|
||||||
BIN_PATH = $(PREFIX)/bin
|
BIN_PATH = $(PREFIX)/bin
|
||||||
HELPER_PATH = $(PREFIX)/lib/afl
|
HELPER_PATH = $(PREFIX)/lib/afl
|
||||||
DOC_PATH = $(PREFIX)/share/doc/afl
|
DOC_PATH = $(PREFIX)/share/doc/afl
|
||||||
MISC_PATH = $(PREFIX)/share/afl
|
MISC_PATH = $(PREFIX)/share/afl
|
||||||
|
MAN_PATH = $(PREFIX)/man/man8
|
||||||
|
|
||||||
# PROGS intentionally omit afl-as, which gets installed elsewhere.
|
# PROGS intentionally omit afl-as, which gets installed elsewhere.
|
||||||
|
|
||||||
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
|
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
|
||||||
SH_PROGS = afl-plot afl-cmin afl-whatsup afl-system-config
|
SH_PROGS = afl-plot afl-cmin afl-whatsup afl-system-config
|
||||||
|
MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8)
|
||||||
|
|
||||||
CFLAGS ?= -O3 -funroll-loops
|
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)\" \
|
-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
|
PYTHON_INCLUDE ?= /usr/include/python2.7
|
||||||
|
|
||||||
@ -47,7 +51,7 @@ else
|
|||||||
TEST_CC = afl-clang
|
TEST_CC = afl-clang
|
||||||
endif
|
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"
|
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:
|
ready:
|
||||||
@echo "[+] Everything seems to be working, ready to compile."
|
@echo "[+] Everything seems to be working, ready to compile."
|
||||||
|
|
||||||
afl-gcc: afl-gcc.c $(COMM_HDR) | test_x86
|
afl-gcc: src/afl-gcc.c $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||||
set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $$i; done
|
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
|
afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||||
ln -sf afl-as as
|
ln -sf afl-as as
|
||||||
|
|
||||||
afl-common.o : afl-common.c
|
afl-common.o : src/afl-common.c include/common.h
|
||||||
$(CC) $(CFLAGS) -c afl-common.c
|
$(CC) $(CFLAGS) -c src/afl-common.c
|
||||||
|
|
||||||
sharedmem.o : sharedmem.c
|
afl-forkserver.o : src/afl-forkserver.c include/forkserver.h
|
||||||
$(CC) $(CFLAGS) -c sharedmem.c
|
$(CC) $(CFLAGS) -c src/afl-forkserver.c
|
||||||
|
|
||||||
afl-fuzz: afl-fuzz.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86
|
afl-sharedmem.o : src/afl-sharedmem.c include/sharedmem.h
|
||||||
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS) $(PYFLAGS)
|
$(CC) $(CFLAGS) -c src/afl-sharedmem.c
|
||||||
|
|
||||||
afl-showmap: afl-showmap.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86
|
afl-fuzz: include/afl-fuzz.h $(AFL_FUZZ_FILES) afl-common.o afl-sharedmem.o afl-forkserver.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS)
|
$(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
|
afl-showmap: src/afl-showmap.c afl-common.o afl-sharedmem.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS)
|
$(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
|
afl-tmin: src/afl-tmin.c afl-common.o afl-sharedmem.o afl-forkserver.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) src/$@.c afl-common.o afl-sharedmem.o afl-forkserver.o -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
afl-gotcpu: afl-gotcpu.c $(COMM_HDR) | test_x86
|
afl-analyze: src/afl-analyze.c afl-common.o afl-sharedmem.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
$(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
|
ifndef AFL_NO_X86
|
||||||
|
|
||||||
test_build: afl-gcc afl-as afl-showmap
|
test_build: afl-gcc afl-as afl-showmap
|
||||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||||
unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS)
|
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)
|
||||||
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
|
echo 1 | ./afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||||
@rm -f test-instr
|
@rm -f test-instr
|
||||||
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please 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!"
|
@echo "[+] All right, the instrumentation seems to be working!"
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -181,13 +205,33 @@ all_done: test_build
|
|||||||
.NOTPARALLEL: clean
|
.NOTPARALLEL: clean
|
||||||
|
|
||||||
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 -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
|
rm -rf out_dir qemu_mode/qemu-3.1.0 unicorn_mode/unicorn
|
||||||
$(MAKE) -C llvm_mode clean
|
$(MAKE) -C llvm_mode clean
|
||||||
$(MAKE) -C libdislocator clean
|
$(MAKE) -C libdislocator clean
|
||||||
$(MAKE) -C libtokencap 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)
|
mkdir -p -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
|
||||||
rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh
|
rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh
|
||||||
install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH)
|
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 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-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 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; 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
|
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)
|
install -m 755 afl-as $${DESTDIR}$(HELPER_PATH)
|
||||||
ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as
|
ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as
|
||||||
install -m 644 docs/README.md docs/ChangeLog docs/*.txt $${DESTDIR}$(DOC_PATH)
|
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++)
|
# american fuzzy lop plus plus (afl++)
|
||||||
|
|
||||||
Release Version: 2.53c
|
Release Version: 2.53c
|
||||||
|
|
||||||
Github Version: 2.53d
|
Github Version: 2.53d
|
||||||
|
|
||||||
|
|
||||||
@ -8,32 +9,41 @@
|
|||||||
|
|
||||||
Repository: [https://github.com/vanhauser-thc/AFLplusplus](https://github.com/vanhauser-thc/AFLplusplus)
|
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
|
afl++ is maintained by Marc "van Hauser" Heuse <mh@mh-sec.de>,
|
||||||
<heiko.eissfeldt@hexco.de> and Andrea Fioraldi <andreafioraldi@gmail.com>.
|
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
|
## The enhancements compared to the original stock afl
|
||||||
|
|
||||||
Many improvements were made over the official afl release - which did not
|
Many improvements were made over the official afl release - which did not
|
||||||
get any improvements since November 2017.
|
get any improvements since November 2017.
|
||||||
|
|
||||||
Among others afl++ has, e.g. more performant llvm_mode, supporting
|
Among others afl++ has a more performant llvm_mode, supporting
|
||||||
llvm up to version 8, Qemu 3.1, more speed and crashfixes for Qemu,
|
llvm up to version 9, Qemu 3.1, more speed and crashfixes for Qemu,
|
||||||
laf-intel feature for Qemu (with libcompcov) and more.
|
better *BSD and Android support and much, much more.
|
||||||
|
|
||||||
Additionally the following patches have been integrated:
|
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)
|
* 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)
|
* 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)
|
* 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
|
* 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.
|
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 :-)
|
So all in all this is the best-of AFL that is currently out there :-)
|
||||||
@ -49,7 +59,6 @@
|
|||||||
|
|
||||||
|
|
||||||
## 1) Challenges of guided fuzzing
|
## 1) Challenges of guided fuzzing
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
Fuzzing is one of the most powerful and proven strategies for identifying
|
Fuzzing is one of the most powerful and proven strategies for identifying
|
||||||
security issues in real-world software; it is responsible for the vast
|
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++
|
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.
|
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.
|
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
|
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
|
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;
|
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,
|
clang users may also opt to leverage a higher-performance instrumentation mode,
|
||||||
as described in [llvm_mode/README.llvm](llvm_mode/README.llvm).
|
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
|
Using the LAF Intel performance enhancements are also recommended, see
|
||||||
[llvm_mode/README.laf-intel](llvm_mode/README.laf-intel)
|
[llvm_mode/README.laf-intel](llvm_mode/README.laf-intel)
|
||||||
@ -172,7 +181,6 @@ file for important caveats.
|
|||||||
|
|
||||||
|
|
||||||
## 4) Instrumenting binary-only apps
|
## 4) Instrumenting binary-only apps
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
When source code is *NOT* available, the fuzzer offers experimental support for
|
When source code is *NOT* available, the fuzzer offers experimental support for
|
||||||
fast, on-the-fly instrumentation of black-box binaries. This is accomplished
|
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
|
## 5) Power schedules
|
||||||
------------------
|
|
||||||
|
|
||||||
The power schedules were copied from Marcel Böhme's excellent AFLfast
|
The power schedules were copied from Marcel Böhme's excellent AFLfast
|
||||||
implementation and expands on the ability to discover new paths and
|
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
|
## 6) Choosing initial test cases
|
||||||
------------------------------
|
|
||||||
|
|
||||||
To operate correctly, the fuzzer requires one or more starting file that
|
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
|
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
|
## 7) Fuzzing binaries
|
||||||
-------------------
|
|
||||||
|
|
||||||
The fuzzing process itself is carried out by the afl-fuzz utility. This program
|
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
|
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
|
## 8) Interpreting output
|
||||||
----------------------
|
|
||||||
|
|
||||||
See the [docs/status_screen.txt](docs/status_screen.txt) file for information on
|
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
|
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
|
## 9) Parallelized fuzzing
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Every instance of afl-fuzz takes up roughly one core. This means that on
|
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.
|
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
|
## 10) Fuzzer dictionaries
|
||||||
----------------------
|
|
||||||
|
|
||||||
By default, afl-fuzz mutation engine is optimized for compact data formats -
|
By default, afl-fuzz mutation engine is optimized for compact data formats -
|
||||||
say, images, multimedia, compressed data, regular expression syntax, or shell
|
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
|
## 11) Crash triage
|
||||||
----------------
|
|
||||||
|
|
||||||
The coverage-based grouping of crashes usually produces a small data set that
|
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.
|
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
|
## 12) Going beyond crashes
|
||||||
------------------------
|
|
||||||
|
|
||||||
Fuzzing is a wonderful and underutilized technique for discovering non-crashing
|
Fuzzing is a wonderful and underutilized technique for discovering non-crashing
|
||||||
design and implementation errors, too. Quite a few interesting bugs have been
|
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
|
## 13) Common-sense risks
|
||||||
----------------------
|
|
||||||
|
|
||||||
Please keep in mind that, similarly to many other computationally-intensive
|
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:
|
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
|
## 14) Known limitations & areas for improvement
|
||||||
---------------------------------------------
|
|
||||||
|
|
||||||
Here are some of the most important caveats for AFL:
|
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
|
## 15) Special thanks
|
||||||
------------------
|
|
||||||
|
|
||||||
Many of the improvements to the original afl wouldn't be possible without
|
Many of the improvements to the original afl and afl++ wouldn't be possible
|
||||||
feedback, bug reports, or patches from:
|
without feedback, bug reports, or patches from:
|
||||||
|
|
||||||
```
|
```
|
||||||
Jann Horn Hanno Boeck
|
Jann Horn Hanno Boeck
|
||||||
@ -597,14 +594,15 @@ feedback, bug reports, or patches from:
|
|||||||
Rene Freingruber Sergey Davidoff
|
Rene Freingruber Sergey Davidoff
|
||||||
Sami Liedes Craig Young
|
Sami Liedes Craig Young
|
||||||
Andrzej Jackowski Daniel Hodson
|
Andrzej Jackowski Daniel Hodson
|
||||||
Nathan Voss Dominik Maier
|
Nathan Voss Dominik Maier
|
||||||
|
Andrea Biondo Vincent Le Garrec
|
||||||
|
Khaled Yakdan Kuang-che Wu
|
||||||
```
|
```
|
||||||
|
|
||||||
Thank you!
|
Thank you!
|
||||||
|
|
||||||
|
|
||||||
## 16) Contact
|
## 16) Contact
|
||||||
-----------
|
|
||||||
|
|
||||||
Questions? Concerns? Bug reports? The contributors can be reached via
|
Questions? Concerns? Bug reports? The contributors can be reached via
|
||||||
[https://github.com/vanhauser-thc/AFLplusplus](https://github.com/vanhauser-thc/AFLplusplus)
|
[https://github.com/vanhauser-thc/AFLplusplus](https://github.com/vanhauser-thc/AFLplusplus)
|
||||||
|
55
TODO
55
TODO
@ -1,34 +1,61 @@
|
|||||||
Roadmap 2.53d:
|
Roadmap 2.53d:
|
||||||
==============
|
==============
|
||||||
- indent all the code: clang-format -style=Google
|
|
||||||
|
|
||||||
- update docs/sister_projects.txt
|
|
||||||
|
|
||||||
afl-fuzz:
|
afl-fuzz:
|
||||||
- put mutator, scheduler, forkserver and input channels in individual files
|
- custom mutator lib: example and readme
|
||||||
- reuse forkserver for showmap, afl-cmin, etc.
|
|
||||||
|
man:
|
||||||
|
- man page for afl-clang-fast
|
||||||
|
|
||||||
|
|
||||||
|
Roadmap 2.54d:
|
||||||
|
==============
|
||||||
|
|
||||||
gcc_plugin:
|
gcc_plugin:
|
||||||
- needs to be rewritten
|
- needs to be rewritten
|
||||||
- fix crashes when compiling :(
|
|
||||||
- whitelist support
|
- whitelist support
|
||||||
- skip over uninteresting blocks
|
- skip over uninteresting blocks
|
||||||
- laf-intel
|
- laf-intel
|
||||||
- neverZero
|
- neverZero
|
||||||
|
|
||||||
qemu_mode:
|
qemu_mode:
|
||||||
- deferred mode with AFL_DEFERRED_QEMU=0xaddress
|
- update to 4.x (probably this will be skipped :( )
|
||||||
|
|
||||||
unit testing / or large testcase campaign
|
unit testing / or large testcase campaign
|
||||||
|
|
||||||
|
Problem: Average targets (tiff, jpeg, unrar) go through 1500 edges.
|
||||||
Roadmap 2.54d:
|
At afl's default map that means ~16 collisions and ~3 wrappings.
|
||||||
==============
|
Solution #1: increase map size.
|
||||||
- expand MAP size to 256k (current L2 cache size on processors)
|
every +1 decreases fuzzing speed by ~10% and halfs the collisions
|
||||||
-> 18 bit map
|
birthday paradox predicts collisions at this # of edges:
|
||||||
- llvm_mode: dynamic map size and collission free basic block IDs
|
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:
|
qemu_mode:
|
||||||
- persistent mode patching the return address (WinAFL style)
|
- 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 \
|
unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \
|
||||||
AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE
|
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
|
case "$opt" in
|
||||||
|
|
||||||
|
"h")
|
||||||
|
;;
|
||||||
|
|
||||||
"i")
|
"i")
|
||||||
IN_DIR="$OPTARG"
|
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
|
if [ ! "$#" = "2" ]; then
|
||||||
|
|
||||||
cat 1>&2 <<_EOF_
|
cat 1>&2 <<_EOF_
|
||||||
This program generates gnuplot images from afl-fuzz output data. Usage:
|
|
||||||
|
|
||||||
$0 afl_state_dir graph_output_dir
|
$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
|
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
|
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.
|
an empty directory where this tool can write the resulting plots to.
|
||||||
|
@ -1,9 +1,24 @@
|
|||||||
#!/bin/sh
|
#!/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
|
echo This reconfigures the system to have a better fuzzing performance
|
||||||
if [ '!' "$EUID" = 0 ] && [ '!' `id -u` = 0 ] ; then
|
if [ '!' "$EUID" = 0 ] && [ '!' `id -u` = 0 ] ; then
|
||||||
echo Error you need to be root to run this
|
echo Error you need to be root to run this
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if [ "$PLATFORM" = "Linux" ] ; then
|
||||||
sysctl -w kernel.core_pattern=core
|
sysctl -w kernel.core_pattern=core
|
||||||
sysctl -w kernel.randomize_va_space=0
|
sysctl -w kernel.randomize_va_space=0
|
||||||
sysctl -w kernel.sched_child_runs_first=1
|
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
|
||||||
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 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"'
|
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
|
||||||
echo Also use AFL_TMPDIR to use a tmpfs for the input file
|
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 "status check tool for afl-fuzz by <lcamtuf@google.com>"
|
||||||
echo
|
echo
|
||||||
|
test "$1" = "-h" && {
|
||||||
|
echo $0
|
||||||
|
echo
|
||||||
|
echo afl-whatsup has no command line options
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
if [ "$1" = "-s" ]; then
|
if [ "$1" = "-s" ]; then
|
||||||
|
|
||||||
@ -54,7 +61,7 @@ fi
|
|||||||
|
|
||||||
CUR_TIME=`date +%s`
|
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
|
ALIVE_CNT=0
|
||||||
DEAD_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):
|
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!)
|
- 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
|
- 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-showmap: new option -r now shows the real values in the buckets (stock
|
||||||
afl never did), plus shows tuple content summary information now
|
afl never did), plus shows tuple content summary information now
|
||||||
- fix building on *BSD (thanks to tobias.kortkamp for the patch)
|
|
||||||
- small docu updates
|
- 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
|
some basic stats. This behavior is also automatically triggered when the
|
||||||
output from afl-fuzz is redirected to a file or to a pipe.
|
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.
|
- If you are Jakub, you may need AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES.
|
||||||
Others need not apply.
|
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
|
- Setting AFL_INST_LIBS causes the translator to also instrument the code
|
||||||
inside any dynamically linked libraries (notably including glibc).
|
inside any dynamically linked libraries (notably including glibc).
|
||||||
|
|
||||||
|
- 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
|
- Setting AFL_QEMU_COMPCOV enables the CompareCoverage tracing of all
|
||||||
cmp and sub in x86 and x86_64. Support for other architectures and
|
cmp and sub in x86 and x86_64.
|
||||||
comparison functions (mem/strcmp et al.) is planned.
|
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
|
- The underlying QEMU binary will recognize any standard "user space
|
||||||
emulation" variables (e.g., QEMU_STACK_SIZE), but there should be no
|
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 this if you are unsure if the entrypoint might be wrong - but
|
||||||
use it directly, e.g. afl-qemu-trace ./program
|
use it directly, e.g. afl-qemu-trace ./program
|
||||||
|
|
||||||
- If you want to specify a specific entrypoint into the binary (this can
|
- AFL_ENTRYPOINT allows you to specify a specific entrypoint into the
|
||||||
be very good for the performance!), use AFL_ENTRYPOINT for this.
|
binary (this can be very good for the performance!).
|
||||||
The entrypoint is specified as hex address, e.g. 0x4004110
|
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
|
5) Settings for afl-cmin
|
||||||
------------------------
|
------------------------
|
||||||
|
@ -407,6 +407,9 @@ directory. This includes:
|
|||||||
- variable_paths - number of test cases showing variable behavior
|
- variable_paths - number of test cases showing variable behavior
|
||||||
- unique_crashes - number of unique crashes recorded
|
- unique_crashes - number of unique crashes recorded
|
||||||
- unique_hangs - number of unique hangs encountered
|
- 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.
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -106,47 +109,47 @@
|
|||||||
|
|
||||||
static const u8* trampoline_fmt_32 =
|
static const u8* trampoline_fmt_32 =
|
||||||
|
|
||||||
"\n"
|
"\n"
|
||||||
"/* --- AFL TRAMPOLINE (32-BIT) --- */\n"
|
"/* --- AFL TRAMPOLINE (32-BIT) --- */\n"
|
||||||
"\n"
|
"\n"
|
||||||
".align 4\n"
|
".align 4\n"
|
||||||
"\n"
|
"\n"
|
||||||
"leal -16(%%esp), %%esp\n"
|
"leal -16(%%esp), %%esp\n"
|
||||||
"movl %%edi, 0(%%esp)\n"
|
"movl %%edi, 0(%%esp)\n"
|
||||||
"movl %%edx, 4(%%esp)\n"
|
"movl %%edx, 4(%%esp)\n"
|
||||||
"movl %%ecx, 8(%%esp)\n"
|
"movl %%ecx, 8(%%esp)\n"
|
||||||
"movl %%eax, 12(%%esp)\n"
|
"movl %%eax, 12(%%esp)\n"
|
||||||
"movl $0x%08x, %%ecx\n"
|
"movl $0x%08x, %%ecx\n"
|
||||||
"call __afl_maybe_log\n"
|
"call __afl_maybe_log\n"
|
||||||
"movl 12(%%esp), %%eax\n"
|
"movl 12(%%esp), %%eax\n"
|
||||||
"movl 8(%%esp), %%ecx\n"
|
"movl 8(%%esp), %%ecx\n"
|
||||||
"movl 4(%%esp), %%edx\n"
|
"movl 4(%%esp), %%edx\n"
|
||||||
"movl 0(%%esp), %%edi\n"
|
"movl 0(%%esp), %%edi\n"
|
||||||
"leal 16(%%esp), %%esp\n"
|
"leal 16(%%esp), %%esp\n"
|
||||||
"\n"
|
"\n"
|
||||||
"/* --- END --- */\n"
|
"/* --- END --- */\n"
|
||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
static const u8* trampoline_fmt_64 =
|
static const u8* trampoline_fmt_64 =
|
||||||
|
|
||||||
"\n"
|
"\n"
|
||||||
"/* --- AFL TRAMPOLINE (64-BIT) --- */\n"
|
"/* --- AFL TRAMPOLINE (64-BIT) --- */\n"
|
||||||
"\n"
|
"\n"
|
||||||
".align 4\n"
|
".align 4\n"
|
||||||
"\n"
|
"\n"
|
||||||
"leaq -(128+24)(%%rsp), %%rsp\n"
|
"leaq -(128+24)(%%rsp), %%rsp\n"
|
||||||
"movq %%rdx, 0(%%rsp)\n"
|
"movq %%rdx, 0(%%rsp)\n"
|
||||||
"movq %%rcx, 8(%%rsp)\n"
|
"movq %%rcx, 8(%%rsp)\n"
|
||||||
"movq %%rax, 16(%%rsp)\n"
|
"movq %%rax, 16(%%rsp)\n"
|
||||||
"movq $0x%08x, %%rcx\n"
|
"movq $0x%08x, %%rcx\n"
|
||||||
"call __afl_maybe_log\n"
|
"call __afl_maybe_log\n"
|
||||||
"movq 16(%%rsp), %%rax\n"
|
"movq 16(%%rsp), %%rax\n"
|
||||||
"movq 8(%%rsp), %%rcx\n"
|
"movq 8(%%rsp), %%rcx\n"
|
||||||
"movq 0(%%rsp), %%rdx\n"
|
"movq 0(%%rsp), %%rdx\n"
|
||||||
"leaq (128+24)(%%rsp), %%rsp\n"
|
"leaq (128+24)(%%rsp), %%rsp\n"
|
||||||
"\n"
|
"\n"
|
||||||
"/* --- END --- */\n"
|
"/* --- END --- */\n"
|
||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
static const u8* main_payload_32 =
|
static const u8* main_payload_32 =
|
||||||
|
|
||||||
@ -183,14 +186,14 @@ static const u8* main_payload_32 =
|
|||||||
" movl %ecx, __afl_prev_loc\n"
|
" movl %ecx, __afl_prev_loc\n"
|
||||||
#else
|
#else
|
||||||
" movl %ecx, %edi\n"
|
" movl %ecx, %edi\n"
|
||||||
#endif /* ^!COVERAGE_ONLY */
|
#endif /* ^!COVERAGE_ONLY */
|
||||||
"\n"
|
"\n"
|
||||||
#ifdef SKIP_COUNTS
|
#ifdef SKIP_COUNTS
|
||||||
" orb $1, (%edx, %edi, 1)\n"
|
" orb $1, (%edx, %edi, 1)\n"
|
||||||
#else
|
#else
|
||||||
" incb (%edx, %edi, 1)\n"
|
" incb (%edx, %edi, 1)\n"
|
||||||
" adcb $0, (%edx, %edi, 1)\n" // never zero counter implementation. slightly better path discovery and little performance impact
|
" 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"
|
"\n"
|
||||||
"__afl_return:\n"
|
"__afl_return:\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -379,7 +382,7 @@ static const u8* main_payload_32 =
|
|||||||
" .comm __afl_setup_failure, 1, 32\n"
|
" .comm __afl_setup_failure, 1, 32\n"
|
||||||
#ifndef COVERAGE_ONLY
|
#ifndef COVERAGE_ONLY
|
||||||
" .comm __afl_prev_loc, 4, 32\n"
|
" .comm __afl_prev_loc, 4, 32\n"
|
||||||
#endif /* !COVERAGE_ONLY */
|
#endif /* !COVERAGE_ONLY */
|
||||||
" .comm __afl_fork_pid, 4, 32\n"
|
" .comm __afl_fork_pid, 4, 32\n"
|
||||||
" .comm __afl_temp, 4, 32\n"
|
" .comm __afl_temp, 4, 32\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -398,10 +401,10 @@ static const u8* main_payload_32 =
|
|||||||
recognize .string. */
|
recognize .string. */
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
# define CALL_L64(str) "call _" str "\n"
|
#define CALL_L64(str) "call _" str "\n"
|
||||||
#else
|
#else
|
||||||
# define CALL_L64(str) "call " str "@PLT\n"
|
#define CALL_L64(str) "call " str "@PLT\n"
|
||||||
#endif /* ^__APPLE__ */
|
#endif /* ^__APPLE__ */
|
||||||
|
|
||||||
static const u8* main_payload_64 =
|
static const u8* main_payload_64 =
|
||||||
|
|
||||||
@ -415,11 +418,11 @@ static const u8* main_payload_64 =
|
|||||||
"\n"
|
"\n"
|
||||||
"__afl_maybe_log:\n"
|
"__afl_maybe_log:\n"
|
||||||
"\n"
|
"\n"
|
||||||
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9))
|
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9))
|
||||||
" .byte 0x9f /* lahf */\n"
|
" .byte 0x9f /* lahf */\n"
|
||||||
#else
|
#else
|
||||||
" lahf\n"
|
" lahf\n"
|
||||||
#endif /* ^__OpenBSD__, etc */
|
#endif /* ^__OpenBSD__, etc */
|
||||||
" seto %al\n"
|
" seto %al\n"
|
||||||
"\n"
|
"\n"
|
||||||
" /* Check if SHM region is already mapped. */\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 __afl_prev_loc(%rip), %rcx\n"
|
||||||
" xorq %rcx, __afl_prev_loc(%rip)\n"
|
" xorq %rcx, __afl_prev_loc(%rip)\n"
|
||||||
" shrq $1, __afl_prev_loc(%rip)\n"
|
" shrq $1, __afl_prev_loc(%rip)\n"
|
||||||
#endif /* ^!COVERAGE_ONLY */
|
#endif /* ^!COVERAGE_ONLY */
|
||||||
"\n"
|
"\n"
|
||||||
#ifdef SKIP_COUNTS
|
#ifdef SKIP_COUNTS
|
||||||
" orb $1, (%rdx, %rcx, 1)\n"
|
" orb $1, (%rdx, %rcx, 1)\n"
|
||||||
#else
|
#else
|
||||||
" incb (%rdx, %rcx, 1)\n"
|
" incb (%rdx, %rcx, 1)\n"
|
||||||
" adcb $0, (%rdx, %rcx, 1)\n" // never zero counter implementation. slightly better path discovery and little performance impact
|
" 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"
|
"\n"
|
||||||
"__afl_return:\n"
|
"__afl_return:\n"
|
||||||
"\n"
|
"\n"
|
||||||
" addb $127, %al\n"
|
" addb $127, %al\n"
|
||||||
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9))
|
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9))
|
||||||
" .byte 0x9e /* sahf */\n"
|
" .byte 0x9e /* sahf */\n"
|
||||||
#else
|
#else
|
||||||
" sahf\n"
|
" sahf\n"
|
||||||
#endif /* ^__OpenBSD__, etc */
|
#endif /* ^__OpenBSD__, etc */
|
||||||
" ret\n"
|
" ret\n"
|
||||||
"\n"
|
"\n"
|
||||||
".align 8\n"
|
".align 8\n"
|
||||||
@ -471,7 +474,7 @@ static const u8* main_payload_64 =
|
|||||||
" movq (%rdx), %rdx\n"
|
" movq (%rdx), %rdx\n"
|
||||||
#else
|
#else
|
||||||
" movq __afl_global_area_ptr(%rip), %rdx\n"
|
" movq __afl_global_area_ptr(%rip), %rdx\n"
|
||||||
#endif /* !^__APPLE__ */
|
#endif /* !^__APPLE__ */
|
||||||
" testq %rdx, %rdx\n"
|
" testq %rdx, %rdx\n"
|
||||||
" je __afl_setup_first\n"
|
" je __afl_setup_first\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -570,7 +573,7 @@ static const u8* main_payload_64 =
|
|||||||
#else
|
#else
|
||||||
" movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n"
|
" movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n"
|
||||||
" movq %rax, (%rdx)\n"
|
" movq %rax, (%rdx)\n"
|
||||||
#endif /* ^__APPLE__ */
|
#endif /* ^__APPLE__ */
|
||||||
" movq %rax, %rdx\n"
|
" movq %rax, %rdx\n"
|
||||||
"\n"
|
"\n"
|
||||||
"__afl_forkserver:\n"
|
"__afl_forkserver:\n"
|
||||||
@ -739,7 +742,7 @@ static const u8* main_payload_64 =
|
|||||||
" .comm __afl_area_ptr, 8\n"
|
" .comm __afl_area_ptr, 8\n"
|
||||||
#ifndef COVERAGE_ONLY
|
#ifndef COVERAGE_ONLY
|
||||||
" .comm __afl_prev_loc, 8\n"
|
" .comm __afl_prev_loc, 8\n"
|
||||||
#endif /* !COVERAGE_ONLY */
|
#endif /* !COVERAGE_ONLY */
|
||||||
" .comm __afl_fork_pid, 4\n"
|
" .comm __afl_fork_pid, 4\n"
|
||||||
" .comm __afl_temp, 4\n"
|
" .comm __afl_temp, 4\n"
|
||||||
" .comm __afl_setup_failure, 1\n"
|
" .comm __afl_setup_failure, 1\n"
|
||||||
@ -749,12 +752,12 @@ static const u8* main_payload_64 =
|
|||||||
" .lcomm __afl_area_ptr, 8\n"
|
" .lcomm __afl_area_ptr, 8\n"
|
||||||
#ifndef COVERAGE_ONLY
|
#ifndef COVERAGE_ONLY
|
||||||
" .lcomm __afl_prev_loc, 8\n"
|
" .lcomm __afl_prev_loc, 8\n"
|
||||||
#endif /* !COVERAGE_ONLY */
|
#endif /* !COVERAGE_ONLY */
|
||||||
" .lcomm __afl_fork_pid, 4\n"
|
" .lcomm __afl_fork_pid, 4\n"
|
||||||
" .lcomm __afl_temp, 4\n"
|
" .lcomm __afl_temp, 4\n"
|
||||||
" .lcomm __afl_setup_failure, 1\n"
|
" .lcomm __afl_setup_failure, 1\n"
|
||||||
|
|
||||||
#endif /* ^__APPLE__ */
|
#endif /* ^__APPLE__ */
|
||||||
|
|
||||||
" .comm __afl_global_area_ptr, 8, 8\n"
|
" .comm __afl_global_area_ptr, 8, 8\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -764,4 +767,5 @@ static const u8* main_payload_64 =
|
|||||||
"/* --- END --- */\n"
|
"/* --- END --- */\n"
|
||||||
"\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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -31,88 +36,128 @@
|
|||||||
|
|
||||||
/* User-facing macro to sprintf() to a dynamically allocated buffer. */
|
/* User-facing macro to sprintf() to a dynamically allocated buffer. */
|
||||||
|
|
||||||
#define alloc_printf(_str...) ({ \
|
#define alloc_printf(_str...) \
|
||||||
u8* _tmp; \
|
({ \
|
||||||
s32 _len = snprintf(NULL, 0, _str); \
|
\
|
||||||
|
u8* _tmp; \
|
||||||
|
s32 _len = snprintf(NULL, 0, _str); \
|
||||||
if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \
|
if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \
|
||||||
_tmp = ck_alloc(_len + 1); \
|
_tmp = ck_alloc(_len + 1); \
|
||||||
snprintf((char*)_tmp, _len + 1, _str); \
|
snprintf((char*)_tmp, _len + 1, _str); \
|
||||||
_tmp; \
|
_tmp; \
|
||||||
|
\
|
||||||
})
|
})
|
||||||
|
|
||||||
/* Macro to enforce allocation limits as a last-resort defense against
|
/* Macro to enforce allocation limits as a last-resort defense against
|
||||||
integer overflows. */
|
integer overflows. */
|
||||||
|
|
||||||
#define ALLOC_CHECK_SIZE(_s) do { \
|
#define ALLOC_CHECK_SIZE(_s) \
|
||||||
if ((_s) > MAX_ALLOC) \
|
do { \
|
||||||
ABORT("Bad alloc request: %u bytes", (_s)); \
|
\
|
||||||
|
if ((_s) > MAX_ALLOC) ABORT("Bad alloc request: %u bytes", (_s)); \
|
||||||
|
\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* Macro to check malloc() failures and the like. */
|
/* Macro to check malloc() failures and the like. */
|
||||||
|
|
||||||
#define ALLOC_CHECK_RESULT(_r, _s) do { \
|
#define ALLOC_CHECK_RESULT(_r, _s) \
|
||||||
if (!(_r)) \
|
do { \
|
||||||
ABORT("Out of memory: can't allocate %u bytes", (_s)); \
|
\
|
||||||
|
if (!(_r)) ABORT("Out of memory: can't allocate %u bytes", (_s)); \
|
||||||
|
\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* Magic tokens used to mark used / freed chunks. */
|
/* Magic tokens used to mark used / freed chunks. */
|
||||||
|
|
||||||
#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */
|
#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */
|
||||||
#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */
|
#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */
|
||||||
#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */
|
#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */
|
||||||
|
|
||||||
/* Positions of guard tokens in relation to the user-visible pointer. */
|
/* Positions of guard tokens in relation to the user-visible pointer. */
|
||||||
|
|
||||||
#define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2])
|
#define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2])
|
||||||
#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1])
|
#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1])
|
||||||
#define ALLOC_C2(_ptr) (((u8*)(_ptr))[ALLOC_S(_ptr)])
|
#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)
|
#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1)
|
||||||
|
|
||||||
/* Allocator increments for ck_realloc_block(). */
|
/* Allocator increments for ck_realloc_block(). */
|
||||||
|
|
||||||
#define ALLOC_BLK_INC 256
|
#define ALLOC_BLK_INC 256
|
||||||
|
|
||||||
/* Sanity-checking macros for pointers. */
|
/* Sanity-checking macros for pointers. */
|
||||||
|
|
||||||
#define CHECK_PTR(_p) do { \
|
#define CHECK_PTR(_p) \
|
||||||
if (_p) { \
|
do { \
|
||||||
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\
|
\
|
||||||
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
|
if (_p) { \
|
||||||
ABORT("Use after free."); \
|
\
|
||||||
else ABORT("Corrupted head alloc canary."); \
|
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)
|
} while (0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#define CHECK_PTR(_p) do { \
|
#define CHECK_PTR(_p) do { \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
\
|
||||||
|
\
|
||||||
if (_p) { \
|
if (_p) { \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
\
|
||||||
|
\
|
||||||
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\
|
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\
|
||||||
|
\
|
||||||
|
\
|
||||||
|
\
|
||||||
|
\
|
||||||
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
|
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
|
||||||
ABORT("Use after free."); \
|
ABORT("Use after free."); \
|
||||||
else ABORT("Corrupted head alloc canary."); \
|
else ABORT("Corrupted head alloc canary."); \
|
||||||
|
\
|
||||||
} \
|
} \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
\
|
||||||
if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \
|
if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \
|
||||||
ABORT("Corrupted tail alloc canary."); \
|
ABORT("Corrupted tail alloc canary."); \
|
||||||
|
\
|
||||||
} \
|
} \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
\
|
||||||
|
\
|
||||||
|
\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CHECK_PTR_EXPR(_p) ({ \
|
#define CHECK_PTR_EXPR(_p) \
|
||||||
typeof (_p) _tmp = (_p); \
|
({ \
|
||||||
CHECK_PTR(_tmp); \
|
\
|
||||||
_tmp; \
|
typeof(_p) _tmp = (_p); \
|
||||||
|
CHECK_PTR(_tmp); \
|
||||||
|
_tmp; \
|
||||||
|
\
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
/* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized
|
/* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized
|
||||||
requests. */
|
requests. */
|
||||||
|
|
||||||
static inline void* DFL_ck_alloc_nozero(u32 size) {
|
static inline void* DFL_ck_alloc_nozero(u32 size) {
|
||||||
|
|
||||||
void* ret;
|
u8* ret;
|
||||||
|
|
||||||
if (!size) return NULL;
|
if (!size) return NULL;
|
||||||
|
|
||||||
@ -123,14 +168,13 @@ static inline void* DFL_ck_alloc_nozero(u32 size) {
|
|||||||
ret += ALLOC_OFF_HEAD;
|
ret += ALLOC_OFF_HEAD;
|
||||||
|
|
||||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||||
ALLOC_S(ret) = size;
|
ALLOC_S(ret) = size;
|
||||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||||
|
|
||||||
return ret;
|
return (void*)ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Allocate a buffer, returning zeroed memory. */
|
/* Allocate a buffer, returning zeroed memory. */
|
||||||
|
|
||||||
static inline void* DFL_ck_alloc(u32 size) {
|
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
|
/* Free memory, checking for double free and corrupted heap. When DEBUG_BUILD
|
||||||
is set, the old memory will be also clobbered with 0xFF. */
|
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. */
|
/* Catch pointer issues sooner. */
|
||||||
memset(mem, 0xFF, ALLOC_S(mem));
|
memset(mem, 0xFF, ALLOC_S(mem));
|
||||||
|
|
||||||
#endif /* DEBUG_BUILD */
|
#endif /* DEBUG_BUILD */
|
||||||
|
|
||||||
ALLOC_C1(mem) = ALLOC_MAGIC_F;
|
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.
|
/* 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
|
With DEBUG_BUILD, the buffer is always reallocated to a new addresses and the
|
||||||
old memory is clobbered with 0xFF. */
|
old memory is clobbered with 0xFF. */
|
||||||
|
|
||||||
static inline void* DFL_ck_realloc(void* orig, u32 size) {
|
static inline void* DFL_ck_realloc(void* orig, u32 size) {
|
||||||
|
|
||||||
void* ret;
|
u8* ret;
|
||||||
u32 old_size = 0;
|
u32 old_size = 0;
|
||||||
|
|
||||||
if (!size) {
|
if (!size) {
|
||||||
|
|
||||||
@ -190,10 +233,12 @@ static inline void* DFL_ck_realloc(void* orig, u32 size) {
|
|||||||
|
|
||||||
#ifndef DEBUG_BUILD
|
#ifndef DEBUG_BUILD
|
||||||
ALLOC_C1(orig) = ALLOC_MAGIC_F;
|
ALLOC_C1(orig) = ALLOC_MAGIC_F;
|
||||||
#endif /* !DEBUG_BUILD */
|
#endif /* !DEBUG_BUILD */
|
||||||
|
|
||||||
old_size = ALLOC_S(orig);
|
old_size = ALLOC_S(orig);
|
||||||
orig -= ALLOC_OFF_HEAD;
|
u8* origu8 = orig;
|
||||||
|
origu8 -= ALLOC_OFF_HEAD;
|
||||||
|
orig = origu8;
|
||||||
|
|
||||||
ALLOC_CHECK_SIZE(old_size);
|
ALLOC_CHECK_SIZE(old_size);
|
||||||
|
|
||||||
@ -216,31 +261,30 @@ static inline void* DFL_ck_realloc(void* orig, u32 size) {
|
|||||||
|
|
||||||
if (orig) {
|
if (orig) {
|
||||||
|
|
||||||
memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size));
|
u8* origu8 = orig;
|
||||||
memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size);
|
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);
|
free(orig);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* ^!DEBUG_BUILD */
|
#endif /* ^!DEBUG_BUILD */
|
||||||
|
|
||||||
ret += ALLOC_OFF_HEAD;
|
ret += ALLOC_OFF_HEAD;
|
||||||
|
|
||||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||||
ALLOC_S(ret) = size;
|
ALLOC_S(ret) = size;
|
||||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||||
|
|
||||||
if (size > old_size)
|
if (size > old_size) memset(ret + old_size, 0, 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
|
/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up
|
||||||
repeated small reallocs without complicating the user code). */
|
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);
|
return DFL_ck_realloc(orig, size);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */
|
/* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */
|
||||||
|
|
||||||
static inline u8* DFL_ck_strdup(u8* str) {
|
static inline u8* DFL_ck_strdup(u8* str) {
|
||||||
|
|
||||||
void* ret;
|
u8* ret;
|
||||||
u32 size;
|
u32 size;
|
||||||
|
|
||||||
if (!str) return NULL;
|
if (!str) return NULL;
|
||||||
|
|
||||||
@ -283,20 +326,19 @@ static inline u8* DFL_ck_strdup(u8* str) {
|
|||||||
ret += ALLOC_OFF_HEAD;
|
ret += ALLOC_OFF_HEAD;
|
||||||
|
|
||||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||||
ALLOC_S(ret) = size;
|
ALLOC_S(ret) = size;
|
||||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||||
|
|
||||||
return memcpy(ret, str, size);
|
return memcpy(ret, str, size);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized
|
/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized
|
||||||
or NULL inputs. */
|
or NULL inputs. */
|
||||||
|
|
||||||
static inline void* DFL_ck_memdup(void* mem, u32 size) {
|
static inline void* DFL_ck_memdup(void* mem, u32 size) {
|
||||||
|
|
||||||
void* ret;
|
u8* ret;
|
||||||
|
|
||||||
if (!mem || !size) return NULL;
|
if (!mem || !size) return NULL;
|
||||||
|
|
||||||
@ -307,14 +349,13 @@ static inline void* DFL_ck_memdup(void* mem, u32 size) {
|
|||||||
ret += ALLOC_OFF_HEAD;
|
ret += ALLOC_OFF_HEAD;
|
||||||
|
|
||||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||||
ALLOC_S(ret) = size;
|
ALLOC_S(ret) = size;
|
||||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||||
|
|
||||||
return memcpy(ret, mem, size);
|
return memcpy(ret, mem, size);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Create a buffer with a block of text, appending a NUL terminator at the end.
|
/* Create a buffer with a block of text, appending a NUL terminator at the end.
|
||||||
Returns NULL for zero-sized or NULL inputs. */
|
Returns NULL for zero-sized or NULL inputs. */
|
||||||
|
|
||||||
@ -331,7 +372,7 @@ static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) {
|
|||||||
ret += ALLOC_OFF_HEAD;
|
ret += ALLOC_OFF_HEAD;
|
||||||
|
|
||||||
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
|
||||||
ALLOC_S(ret) = size;
|
ALLOC_S(ret) = size;
|
||||||
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
|
||||||
|
|
||||||
memcpy(ret, mem, size);
|
memcpy(ret, mem, size);
|
||||||
@ -341,20 +382,19 @@ static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifndef DEBUG_BUILD
|
#ifndef DEBUG_BUILD
|
||||||
|
|
||||||
/* In non-debug mode, we just do straightforward aliasing of the above functions
|
/* In non-debug mode, we just do straightforward aliasing of the above functions
|
||||||
to user-visible names such as ck_alloc(). */
|
to user-visible names such as ck_alloc(). */
|
||||||
|
|
||||||
#define ck_alloc DFL_ck_alloc
|
#define ck_alloc DFL_ck_alloc
|
||||||
#define ck_alloc_nozero DFL_ck_alloc_nozero
|
#define ck_alloc_nozero DFL_ck_alloc_nozero
|
||||||
#define ck_realloc DFL_ck_realloc
|
#define ck_realloc DFL_ck_realloc
|
||||||
#define ck_realloc_block DFL_ck_realloc_block
|
#define ck_realloc_block DFL_ck_realloc_block
|
||||||
#define ck_strdup DFL_ck_strdup
|
#define ck_strdup DFL_ck_strdup
|
||||||
#define ck_memdup DFL_ck_memdup
|
#define ck_memdup DFL_ck_memdup
|
||||||
#define ck_memdup_str DFL_ck_memdup_str
|
#define ck_memdup_str DFL_ck_memdup_str
|
||||||
#define ck_free DFL_ck_free
|
#define ck_free DFL_ck_free
|
||||||
|
|
||||||
#define alloc_report()
|
#define alloc_report()
|
||||||
|
|
||||||
@ -365,12 +405,14 @@ static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) {
|
|||||||
|
|
||||||
/* Alloc tracking data structures: */
|
/* Alloc tracking data structures: */
|
||||||
|
|
||||||
#define ALLOC_BUCKETS 4096
|
#define ALLOC_BUCKETS 4096
|
||||||
|
|
||||||
struct TRK_obj {
|
struct TRK_obj {
|
||||||
void *ptr;
|
|
||||||
|
void* ptr;
|
||||||
char *file, *func;
|
char *file, *func;
|
||||||
u32 line;
|
u32 line;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef AFL_MAIN
|
#ifdef AFL_MAIN
|
||||||
@ -378,22 +420,21 @@ struct TRK_obj {
|
|||||||
struct TRK_obj* TRK[ALLOC_BUCKETS];
|
struct TRK_obj* TRK[ALLOC_BUCKETS];
|
||||||
u32 TRK_cnt[ALLOC_BUCKETS];
|
u32 TRK_cnt[ALLOC_BUCKETS];
|
||||||
|
|
||||||
# define alloc_report() TRK_report()
|
#define alloc_report() TRK_report()
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
extern struct TRK_obj* TRK[ALLOC_BUCKETS];
|
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: */
|
/* Bucket-assigning function for a given pointer: */
|
||||||
|
|
||||||
#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS)
|
#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS)
|
||||||
|
|
||||||
|
|
||||||
/* Add a new entry to the list of allocated objects. */
|
/* Add a new entry to the list of allocated objects. */
|
||||||
|
|
||||||
static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func,
|
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) {
|
if (!TRK[bucket][i].ptr) {
|
||||||
|
|
||||||
TRK[bucket][i].ptr = ptr;
|
TRK[bucket][i].ptr = ptr;
|
||||||
TRK[bucket][i].file = (char*)file;
|
TRK[bucket][i].file = (char*)file;
|
||||||
TRK[bucket][i].func = (char*)func;
|
TRK[bucket][i].func = (char*)func;
|
||||||
TRK[bucket][i].line = line;
|
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. */
|
/* No space available - allocate more. */
|
||||||
|
|
||||||
TRK[bucket] = DFL_ck_realloc_block(TRK[bucket],
|
TRK[bucket] = DFL_ck_realloc_block(
|
||||||
(TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
|
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].file = (char*)file;
|
||||||
TRK[bucket][i].func = (char*)func;
|
TRK[bucket][i].func = (char*)func;
|
||||||
TRK[bucket][i].line = line;
|
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. */
|
/* Remove entry from the list of allocated objects. */
|
||||||
|
|
||||||
static inline void TRK_free_buf(void* ptr, const char* file, const char* func,
|
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)",
|
WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)", func, file,
|
||||||
func, file, line);
|
line);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Do a final report on all non-deallocated objects. */
|
/* Do a final report on all non-deallocated objects. */
|
||||||
|
|
||||||
static inline void TRK_report(void) {
|
static inline void TRK_report(void) {
|
||||||
@ -478,7 +517,6 @@ static inline void TRK_report(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Simple wrappers for non-debugging functions: */
|
/* Simple wrappers for non-debugging functions: */
|
||||||
|
|
||||||
static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func,
|
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,
|
static inline void* TRK_ck_realloc(void* orig, u32 size, const char* file,
|
||||||
const char* func, u32 line) {
|
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,
|
static inline void* TRK_ck_realloc_block(void* orig, u32 size, const char* file,
|
||||||
const char* func, u32 line) {
|
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,
|
static inline void* TRK_ck_strdup(u8* str, const char* file, const char* func,
|
||||||
u32 line) {
|
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,
|
static inline void* TRK_ck_memdup(void* mem, u32 size, const char* file,
|
||||||
const char* func, u32 line) {
|
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,
|
static inline void* TRK_ck_memdup_str(void* mem, u32 size, const char* file,
|
||||||
const char* func, u32 line) {
|
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,
|
||||||
static inline void TRK_ck_free(void* ptr, const char* file,
|
u32 line) {
|
||||||
const char* func, u32 line) {
|
|
||||||
|
|
||||||
TRK_free_buf(ptr, file, func, line);
|
TRK_free_buf(ptr, file, func, line);
|
||||||
DFL_ck_free(ptr);
|
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: */
|
/* Aliasing user-facing names to tracking functions: */
|
||||||
|
|
||||||
#define ck_alloc(_p1) \
|
#define ck_alloc(_p1) TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||||
TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
|
||||||
|
|
||||||
#define ck_alloc_nozero(_p1) \
|
#define ck_alloc_nozero(_p1) TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||||
TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
|
|
||||||
|
|
||||||
#define ck_realloc(_p1, _p2) \
|
#define ck_realloc(_p1, _p2) \
|
||||||
TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
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) \
|
#define ck_realloc_block(_p1, _p2) \
|
||||||
TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||||
|
|
||||||
#define ck_strdup(_p1) \
|
#define ck_strdup(_p1) TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||||
TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__)
|
|
||||||
|
|
||||||
#define ck_memdup(_p1, _p2) \
|
#define ck_memdup(_p1, _p2) \
|
||||||
TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
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) \
|
#define ck_memdup_str(_p1, _p2) \
|
||||||
TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
|
||||||
|
|
||||||
#define ck_free(_p1) \
|
#define ck_free(_p1) TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__)
|
||||||
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
|
The hash32() function is a variant of MurmurHash3, a good
|
||||||
non-cryptosafe hashing function developed by Austin Appleby.
|
non-cryptosafe hashing function developed by Austin Appleby.
|
||||||
@ -31,12 +31,12 @@
|
|||||||
|
|
||||||
#ifdef __x86_64__
|
#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) {
|
static inline u32 hash32(const void* key, u32 len, u32 seed) {
|
||||||
|
|
||||||
const u64* data = (u64*)key;
|
const u64* data = (u64*)key;
|
||||||
u64 h1 = seed ^ len;
|
u64 h1 = seed ^ len;
|
||||||
|
|
||||||
len >>= 3;
|
len >>= 3;
|
||||||
|
|
||||||
@ -45,12 +45,12 @@ static inline u32 hash32(const void* key, u32 len, u32 seed) {
|
|||||||
u64 k1 = *data++;
|
u64 k1 = *data++;
|
||||||
|
|
||||||
k1 *= 0x87c37b91114253d5ULL;
|
k1 *= 0x87c37b91114253d5ULL;
|
||||||
k1 = ROL64(k1, 31);
|
k1 = ROL64(k1, 31);
|
||||||
k1 *= 0x4cf5ad432745937fULL;
|
k1 *= 0x4cf5ad432745937fULL;
|
||||||
|
|
||||||
h1 ^= k1;
|
h1 ^= k1;
|
||||||
h1 = ROL64(h1, 27);
|
h1 = ROL64(h1, 27);
|
||||||
h1 = h1 * 5 + 0x52dce729;
|
h1 = h1 * 5 + 0x52dce729;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,12 +66,12 @@ 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) {
|
static inline u32 hash32(const void* key, u32 len, u32 seed) {
|
||||||
|
|
||||||
const u32* data = (u32*)key;
|
const u32* data = (u32*)key;
|
||||||
u32 h1 = seed ^ len;
|
u32 h1 = seed ^ len;
|
||||||
|
|
||||||
len >>= 2;
|
len >>= 2;
|
||||||
|
|
||||||
@ -80,12 +80,12 @@ static inline u32 hash32(const void* key, u32 len, u32 seed) {
|
|||||||
u32 k1 = *data++;
|
u32 k1 = *data++;
|
||||||
|
|
||||||
k1 *= 0xcc9e2d51;
|
k1 *= 0xcc9e2d51;
|
||||||
k1 = ROL32(k1, 15);
|
k1 = ROL32(k1, 15);
|
||||||
k1 *= 0x1b873593;
|
k1 *= 0x1b873593;
|
||||||
|
|
||||||
h1 ^= k1;
|
h1 ^= k1;
|
||||||
h1 = ROL32(h1, 13);
|
h1 = ROL32(h1, 13);
|
||||||
h1 = h1 * 5 + 0xe6546b64;
|
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)
|
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
|
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
|
||||||
|
|
||||||
all: libdislocator.so
|
all: libdislocator.so
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
===================================
|
# libdislocator, an abusive allocator
|
||||||
libdislocator, an abusive allocator
|
|
||||||
===================================
|
|
||||||
|
|
||||||
(See ../docs/README for the general instruction manual.)
|
(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:
|
To use this library, run AFL like so:
|
||||||
|
|
||||||
|
```
|
||||||
AFL_PRELOAD=/path/to/libdislocator.so ./afl-fuzz [...other params...]
|
AFL_PRELOAD=/path/to/libdislocator.so ./afl-fuzz [...other params...]
|
||||||
|
```
|
||||||
|
|
||||||
You *have* to specify path, even if it's just ./libdislocator.so or
|
You *have* to specify path, even if it's just ./libdislocator.so or
|
||||||
$PWD/libdislocator.so.
|
$PWD/libdislocator.so.
|
@ -25,36 +25,48 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include "../config.h"
|
#include "config.h"
|
||||||
#include "../types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#ifndef PAGE_SIZE
|
#ifndef PAGE_SIZE
|
||||||
# define PAGE_SIZE 4096
|
#define PAGE_SIZE 4096
|
||||||
#endif /* !PAGE_SIZE */
|
#endif /* !PAGE_SIZE */
|
||||||
|
|
||||||
#ifndef MAP_ANONYMOUS
|
#ifndef MAP_ANONYMOUS
|
||||||
# define MAP_ANONYMOUS MAP_ANON
|
#define MAP_ANONYMOUS MAP_ANON
|
||||||
#endif /* !MAP_ANONYMOUS */
|
#endif /* !MAP_ANONYMOUS */
|
||||||
|
|
||||||
/* Error / message handling: */
|
/* Error / message handling: */
|
||||||
|
|
||||||
#define DEBUGF(_x...) do { \
|
#define DEBUGF(_x...) \
|
||||||
if (alloc_verbose) { \
|
do { \
|
||||||
if (++call_depth == 1) { \
|
\
|
||||||
|
if (alloc_verbose) { \
|
||||||
|
\
|
||||||
|
if (++call_depth == 1) { \
|
||||||
|
\
|
||||||
fprintf(stderr, "[AFL] " _x); \
|
fprintf(stderr, "[AFL] " _x); \
|
||||||
fprintf(stderr, "\n"); \
|
fprintf(stderr, "\n"); \
|
||||||
} \
|
\
|
||||||
call_depth--; \
|
} \
|
||||||
} \
|
call_depth--; \
|
||||||
|
\
|
||||||
|
} \
|
||||||
|
\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define FATAL(_x...) do { \
|
#define FATAL(_x...) \
|
||||||
if (++call_depth == 1) { \
|
do { \
|
||||||
|
\
|
||||||
|
if (++call_depth == 1) { \
|
||||||
|
\
|
||||||
fprintf(stderr, "*** [AFL] " _x); \
|
fprintf(stderr, "*** [AFL] " _x); \
|
||||||
fprintf(stderr, " ***\n"); \
|
fprintf(stderr, " ***\n"); \
|
||||||
abort(); \
|
abort(); \
|
||||||
} \
|
\
|
||||||
call_depth--; \
|
} \
|
||||||
|
call_depth--; \
|
||||||
|
\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* Macro to count the number of pages needed to store a buffer: */
|
/* Macro to count the number of pages needed to store a buffer: */
|
||||||
@ -63,7 +75,7 @@
|
|||||||
|
|
||||||
/* Canary & clobber bytes: */
|
/* Canary & clobber bytes: */
|
||||||
|
|
||||||
#define ALLOC_CANARY 0xAACCAACC
|
#define ALLOC_CANARY 0xAACCAACC
|
||||||
#define ALLOC_CLOBBER 0xCC
|
#define ALLOC_CLOBBER 0xCC
|
||||||
|
|
||||||
#define PTR_C(_p) (((u32*)(_p))[-1])
|
#define PTR_C(_p) (((u32*)(_p))[-1])
|
||||||
@ -73,14 +85,13 @@
|
|||||||
|
|
||||||
static u32 max_mem = MAX_ALLOC; /* Max heap usage to permit */
|
static u32 max_mem = MAX_ALLOC; /* Max heap usage to permit */
|
||||||
static u8 alloc_verbose, /* Additional debug messages */
|
static u8 alloc_verbose, /* Additional debug messages */
|
||||||
hard_fail, /* abort() when max_mem exceeded? */
|
hard_fail, /* abort() when max_mem exceeded? */
|
||||||
no_calloc_over; /* abort() on calloc() overflows? */
|
no_calloc_over; /* abort() on calloc() overflows? */
|
||||||
|
|
||||||
static __thread size_t total_mem; /* Currently allocated mem */
|
static __thread size_t total_mem; /* Currently allocated mem */
|
||||||
|
|
||||||
static __thread u32 call_depth; /* To avoid recursion via fprintf() */
|
static __thread u32 call_depth; /* To avoid recursion via fprintf() */
|
||||||
|
|
||||||
|
|
||||||
/* This is the main alloc function. It allocates one page more than necessary,
|
/* 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
|
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(),
|
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;
|
void* ret;
|
||||||
|
|
||||||
|
|
||||||
if (total_mem + len > max_mem || total_mem + len < total_mem) {
|
if (total_mem + len > max_mem || total_mem + len < total_mem) {
|
||||||
|
|
||||||
if (hard_fail)
|
if (hard_fail) FATAL("total allocs exceed %u MB", max_mem / 1024 / 1024);
|
||||||
FATAL("total allocs exceed %u MB", max_mem / 1024 / 1024);
|
|
||||||
|
|
||||||
DEBUGF("total allocs exceed %u MB, returning NULL",
|
DEBUGF("total allocs exceed %u MB, returning NULL", max_mem / 1024 / 1024);
|
||||||
max_mem / 1024 / 1024);
|
|
||||||
|
|
||||||
return NULL;
|
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
|
/* The "user-facing" wrapper for calloc(). This just checks for overflows and
|
||||||
displays debug messages if requested. */
|
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 (elem_cnt && len / elem_cnt != elem_len) {
|
||||||
|
|
||||||
if (no_calloc_over) {
|
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;
|
return NULL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FATAL("calloc(%zu, %zu) would overflow", elem_len, elem_cnt);
|
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
|
/* The wrapper for malloc(). Roughly the same, also clobbers the returned
|
||||||
memory (unlike calloc(), malloc() is not guaranteed to return zeroed
|
memory (unlike calloc(), malloc() is not guaranteed to return zeroed
|
||||||
memory). */
|
memory). */
|
||||||
@ -193,7 +202,6 @@ void* malloc(size_t len) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* The wrapper for free(). This simply marks the entire region as PROT_NONE.
|
/* 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
|
If the region is already freed, the code will segfault during the attempt to
|
||||||
read the canary. Not very graceful, but works, right? */
|
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,
|
/* Realloc is pretty straightforward, too. We forcibly reallocate the buffer,
|
||||||
move data, and then free (aka mprotect()) the original one. */
|
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) {
|
__attribute__((constructor)) void __dislocator_init(void) {
|
||||||
|
|
||||||
u8* tmp = getenv("AFL_LD_LIMIT_MB");
|
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");
|
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)
|
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
|
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
|
||||||
|
|
||||||
all: libtokencap.so
|
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.)
|
(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
|
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
|
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
|
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
|
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:
|
when using afl-gcc. This setting specifically adds the following flags:
|
||||||
|
|
||||||
|
```
|
||||||
-fno-builtin-strcmp -fno-builtin-strncmp -fno-builtin-strcasecmp
|
-fno-builtin-strcmp -fno-builtin-strncmp -fno-builtin-strcasecmp
|
||||||
-fno-builtin-strcasencmp -fno-builtin-memcmp -fno-builtin-strstr
|
-fno-builtin-strcasencmp -fno-builtin-memcmp -fno-builtin-strstr
|
||||||
-fno-builtin-strcasestr
|
-fno-builtin-strcasestr
|
||||||
|
```
|
||||||
|
|
||||||
The next step is simply loading this library via LD_PRELOAD. The optimal usage
|
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,
|
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
|
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:
|
found by AFL in that earlier run. This demonstrates the basic principle:
|
||||||
|
|
||||||
|
```
|
||||||
export AFL_TOKEN_FILE=$PWD/temp_output.txt
|
export AFL_TOKEN_FILE=$PWD/temp_output.txt
|
||||||
|
|
||||||
for i in <out_dir>/queue/id*; do
|
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
|
done
|
||||||
|
|
||||||
sort -u temp_output.txt >afl_dictionary.txt
|
sort -u temp_output.txt >afl_dictionary.txt
|
||||||
|
```
|
||||||
|
|
||||||
If you don't get any results, the target library is probably not using strcmp()
|
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
|
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
|
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
|
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
|
need to be changed for other OSes. Porting to platforms with /proc/<pid>/maps
|
||||||
(e.g., FreeBSD) should be trivial.
|
(e.g., FreeBSD) should be trivial.
|
||||||
|
|
@ -27,30 +27,26 @@
|
|||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
#ifndef __linux__
|
#ifndef __linux__
|
||||||
# error "Sorry, this library is Linux-specific for now!"
|
#error "Sorry, this library is Linux-specific for now!"
|
||||||
#endif /* !__linux__ */
|
#endif /* !__linux__ */
|
||||||
|
|
||||||
|
|
||||||
/* Mapping data and such */
|
/* Mapping data and such */
|
||||||
|
|
||||||
#define MAX_MAPPINGS 1024
|
#define MAX_MAPPINGS 1024
|
||||||
|
|
||||||
static struct mapping {
|
static struct mapping { void *st, *en; } __tokencap_ro[MAX_MAPPINGS];
|
||||||
void *st, *en;
|
|
||||||
} __tokencap_ro[MAX_MAPPINGS];
|
|
||||||
|
|
||||||
static u32 __tokencap_ro_cnt;
|
static u32 __tokencap_ro_cnt;
|
||||||
static u8 __tokencap_ro_loaded;
|
static u8 __tokencap_ro_loaded;
|
||||||
static FILE* __tokencap_out_file;
|
static FILE* __tokencap_out_file;
|
||||||
|
|
||||||
|
|
||||||
/* Identify read-only regions in memory. Only parameters that fall into these
|
/* 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
|
ranges are worth dumping when passed to strcmp() and so on. Read-write
|
||||||
regions are far more likely to contain user input instead. */
|
regions are far more likely to contain user input instead. */
|
||||||
|
|
||||||
static void __tokencap_load_mappings(void) {
|
static void __tokencap_load_mappings(void) {
|
||||||
|
|
||||||
u8 buf[MAX_LINE];
|
u8 buf[MAX_LINE];
|
||||||
FILE* f = fopen("/proc/self/maps", "r");
|
FILE* f = fopen("/proc/self/maps", "r");
|
||||||
|
|
||||||
__tokencap_ro_loaded = 1;
|
__tokencap_ro_loaded = 1;
|
||||||
@ -59,8 +55,8 @@ static void __tokencap_load_mappings(void) {
|
|||||||
|
|
||||||
while (fgets(buf, MAX_LINE, f)) {
|
while (fgets(buf, MAX_LINE, f)) {
|
||||||
|
|
||||||
u8 rf, wf;
|
u8 rf, wf;
|
||||||
void* st, *en;
|
void *st, *en;
|
||||||
|
|
||||||
if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue;
|
if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue;
|
||||||
if (wf == 'w' || rf != 'r') 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. */
|
/* Check an address against the list of read-only mappings. */
|
||||||
|
|
||||||
static u8 __tokencap_is_ro(const void* ptr) {
|
static u8 __tokencap_is_ro(const void* ptr) {
|
||||||
@ -92,13 +87,12 @@ static u8 __tokencap_is_ro(const void* ptr) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Dump an interesting token to output file, quoting and escaping it
|
/* Dump an interesting token to output file, quoting and escaping it
|
||||||
properly. */
|
properly. */
|
||||||
|
|
||||||
static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
|
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 i;
|
||||||
u32 pos = 0;
|
u32 pos = 0;
|
||||||
|
|
||||||
@ -120,9 +114,7 @@ static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
|
|||||||
pos += 4;
|
pos += 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default: buf[pos++] = ptr[i];
|
||||||
|
|
||||||
buf[pos++] = ptr[i];
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +126,6 @@ static void __tokencap_dump(const u8* ptr, size_t len, u8 is_text) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Replacements for strcmp(), memcmp(), and so on. Note that these will be used
|
/* 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. */
|
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 != c2) return (c1 > c2) ? 1 : -1;
|
||||||
if (!c1) return 0;
|
if (!c1) return 0;
|
||||||
str1++; str2++;
|
str1++;
|
||||||
|
str2++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#undef strncmp
|
#undef strncmp
|
||||||
|
|
||||||
int strncmp(const char* str1, const char* str2, size_t len) {
|
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) return 0;
|
||||||
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
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
|
#undef strcasecmp
|
||||||
|
|
||||||
int strcasecmp(const char* str1, const char* str2) {
|
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 != c2) return (c1 > c2) ? 1 : -1;
|
||||||
if (!c1) return 0;
|
if (!c1) return 0;
|
||||||
str1++; str2++;
|
str1++;
|
||||||
|
str2++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#undef strncasecmp
|
#undef strncasecmp
|
||||||
|
|
||||||
int strncasecmp(const char* str1, const char* str2, size_t len) {
|
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) return 0;
|
||||||
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
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
|
#undef memcmp
|
||||||
|
|
||||||
int memcmp(const void* mem1, const void* mem2, size_t len) {
|
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;
|
unsigned char c1 = *(const char*)mem1, c2 = *(const char*)mem2;
|
||||||
if (c1 != c2) return (c1 > c2) ? 1 : -1;
|
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
|
#undef strstr
|
||||||
|
|
||||||
char* strstr(const char* haystack, const char* needle) {
|
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))
|
if (__tokencap_is_ro(haystack))
|
||||||
__tokencap_dump(haystack, strlen(haystack), 1);
|
__tokencap_dump(haystack, strlen(haystack), 1);
|
||||||
|
|
||||||
if (__tokencap_is_ro(needle))
|
if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1);
|
||||||
__tokencap_dump(needle, strlen(needle), 1);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
const char* n = needle;
|
const char* n = needle;
|
||||||
const char* h = haystack;
|
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++));
|
} while (*(haystack++));
|
||||||
|
|
||||||
@ -266,7 +258,6 @@ char* strstr(const char* haystack, const char* needle) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#undef strcasestr
|
#undef strcasestr
|
||||||
|
|
||||||
char* strcasestr(const char* haystack, const char* needle) {
|
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))
|
if (__tokencap_is_ro(haystack))
|
||||||
__tokencap_dump(haystack, strlen(haystack), 1);
|
__tokencap_dump(haystack, strlen(haystack), 1);
|
||||||
|
|
||||||
if (__tokencap_is_ro(needle))
|
if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1);
|
||||||
__tokencap_dump(needle, strlen(needle), 1);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
const char* n = needle;
|
const char* n = needle;
|
||||||
const char* h = haystack;
|
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;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Init code to open the output file (or default to stderr). */
|
/* Init code to open the output file (or default to stderr). */
|
||||||
|
|
||||||
__attribute__((constructor)) void __tokencap_init(void) {
|
__attribute__((constructor)) void __tokencap_init(void) {
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include "../config.h"
|
#include "config.h"
|
||||||
#include "../debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
#include "MarkNodes.h"
|
#include "MarkNodes.h"
|
||||||
|
|
||||||
@ -37,267 +37,349 @@ static cl::opt<bool> LoopHeadOpt("loophead", cl::desc("LoopHead"),
|
|||||||
cl::init(false));
|
cl::init(false));
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct InsTrim : public ModulePass {
|
|
||||||
|
|
||||||
protected:
|
struct InsTrim : public ModulePass {
|
||||||
std::list<std::string> myWhitelist;
|
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
std::mt19937 generator;
|
std::list<std::string> myWhitelist;
|
||||||
int total_instr = 0;
|
|
||||||
|
|
||||||
unsigned int genLabel() {
|
private:
|
||||||
return generator() & (MAP_SIZE - 1);
|
std::mt19937 generator;
|
||||||
}
|
int total_instr = 0;
|
||||||
|
|
||||||
public:
|
unsigned int genLabel() {
|
||||||
static char ID;
|
|
||||||
InsTrim() : ModulePass(ID), generator(0) {
|
return generator() & (MAP_SIZE - 1);
|
||||||
char* instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
|
||||||
if (instWhiteListFilename) {
|
}
|
||||||
std::string line;
|
|
||||||
std::ifstream fileStream;
|
public:
|
||||||
fileStream.open(instWhiteListFilename);
|
static char ID;
|
||||||
if (!fileStream)
|
InsTrim() : ModulePass(ID), generator(0) {
|
||||||
report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
|
||||||
|
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);
|
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
|
#if LLVM_VERSION_MAJOR < 4
|
||||||
const char *
|
const char *
|
||||||
#else
|
#else
|
||||||
StringRef
|
StringRef
|
||||||
#endif
|
#endif
|
||||||
getPassName() const override {
|
getPassName() const override {
|
||||||
return "InstTrim Instrumentation";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool runOnModule(Module &M) override {
|
return "InstTrim Instrumentation";
|
||||||
char be_quiet = 0;
|
|
||||||
|
|
||||||
if (isatty(2) && !getenv("AFL_QUIET")) {
|
}
|
||||||
SAYF(cCYA "LLVMInsTrim" VERSION cRST " by csienslab\n");
|
|
||||||
} else be_quiet = 1;
|
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
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
char* neverZero_counters_str;
|
char *neverZero_counters_str;
|
||||||
if ((neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO")) != NULL)
|
if ((neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO")) != NULL)
|
||||||
OKF("LLVM neverZero activated (by hexcoder)\n");
|
OKF("LLVM neverZero activated (by hexcoder)\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL || getenv("LOOPHEAD") != NULL) {
|
if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL ||
|
||||||
LoopHeadOpt = true;
|
getenv("LOOPHEAD") != NULL) {
|
||||||
}
|
|
||||||
|
|
||||||
// this is our default
|
LoopHeadOpt = true;
|
||||||
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)");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
LLVMContext &C = M.getContext();
|
// this is our default
|
||||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
MarkSetOpt = true;
|
||||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
|
||||||
|
|
||||||
GlobalVariable *CovMapPtr = new GlobalVariable(
|
/* // 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)");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
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,
|
M, PointerType::getUnqual(Int8Ty), false, GlobalValue::ExternalLinkage,
|
||||||
nullptr, "__afl_area_ptr");
|
nullptr, "__afl_area_ptr");
|
||||||
|
|
||||||
GlobalVariable *OldPrev = new GlobalVariable(
|
GlobalVariable *OldPrev = new GlobalVariable(
|
||||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc",
|
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0,
|
||||||
0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||||
|
|
||||||
u64 total_rs = 0;
|
u64 total_rs = 0;
|
||||||
u64 total_hs = 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()) {
|
if (Loc) {
|
||||||
bool instrumentBlock = false;
|
|
||||||
DebugLoc Loc;
|
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||||
StringRef instFilename;
|
|
||||||
|
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 ) {
|
/* Continue only if we know where we actually are */
|
||||||
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
if (!instFilename.str().empty()) {
|
||||||
|
|
||||||
unsigned int instLine = cDILoc->getLine();
|
for (std::list<std::string>::iterator it = myWhitelist.begin();
|
||||||
instFilename = cDILoc->getFilename();
|
it != myWhitelist.end(); ++it) {
|
||||||
|
|
||||||
if (instFilename.str().empty()) {
|
if (instFilename.str().length() >= it->length()) {
|
||||||
/* 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().compare(
|
||||||
if (!instFilename.str().empty()) {
|
instFilename.str().length() - it->length(),
|
||||||
for (std::list<std::string>::iterator it = myWhitelist.begin(); it != myWhitelist.end(); ++it) {
|
it->length(), *it) == 0) {
|
||||||
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
|
instrumentBlock = true;
|
||||||
* 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;
|
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
|
#if LLVM_VERSION_MAJOR >= 8
|
||||||
nullptr,
|
nullptr,
|
||||||
#endif
|
#endif
|
||||||
false);
|
false);
|
||||||
MS.insert(NewBB);
|
MS.insert(NewBB);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *EBB = &F.getEntryBlock();
|
}
|
||||||
if (succ_begin(EBB) == succ_end(EBB)) {
|
|
||||||
MS.insert(EBB);
|
auto *EBB = &F.getEntryBlock();
|
||||||
total_rs += 1;
|
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) {
|
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());
|
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||||
Value *L = NULL;
|
IRB.CreateStore(ConstantInt::get(Int32Ty, genLabel()), OldPrev);
|
||||||
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);
|
for (BasicBlock &BB : F) {
|
||||||
PI != PE; ++PI
|
|
||||||
) {
|
auto PI = pred_begin(&BB);
|
||||||
BasicBlock *PBB = *PI;
|
auto PE = pred_end(&BB);
|
||||||
auto It = PredMap.insert({PBB, genLabel()});
|
if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; }
|
||||||
unsigned Label = It.first->second;
|
|
||||||
PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB);
|
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||||
}
|
Value * L = NULL;
|
||||||
L = PN;
|
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 */
|
L = PN;
|
||||||
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 */
|
/* Load prev_loc */
|
||||||
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
|
LoadInst *PrevLoc = IRB.CreateLoad(OldPrev);
|
||||||
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
|
||||||
|
|
||||||
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
|
/* 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 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
|
#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
|
#endif
|
||||||
|
{
|
||||||
|
|
||||||
/* hexcoder: Realize a counter that skips zero during overflow.
|
/* 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
|
* Instead of
|
||||||
* Counter + 1 -> Counter
|
* Counter + 1 -> Counter
|
||||||
@ -305,40 +387,52 @@ namespace {
|
|||||||
* Counter + 1 -> {Counter, OverflowFlag}
|
* Counter + 1 -> {Counter, OverflowFlag}
|
||||||
* Counter + OverflowFlag -> Counter
|
* Counter + OverflowFlag -> Counter
|
||||||
*/
|
*/
|
||||||
auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
|
auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
|
||||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||||
Incr = IRB.CreateAdd(Incr, carry);
|
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));
|
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
} // end of anonymous namespace
|
||||||
|
|
||||||
char InsTrim::ID = 0;
|
char InsTrim::ID = 0;
|
||||||
|
|
||||||
static void registerAFLPass(const PassManagerBuilder &,
|
static void registerAFLPass(const PassManagerBuilder &,
|
||||||
legacy::PassManagerBase &PM) {
|
legacy::PassManagerBase &PM) {
|
||||||
|
|
||||||
PM.add(new InsTrim());
|
PM.add(new InsTrim());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterStandardPasses RegisterAFLPass(
|
static RegisterStandardPasses RegisterAFLPass(
|
||||||
@ -346,3 +440,4 @@ static RegisterStandardPasses RegisterAFLPass(
|
|||||||
|
|
||||||
static RegisterStandardPasses RegisterAFLPass0(
|
static RegisterStandardPasses RegisterAFLPass0(
|
||||||
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
|
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
|
||||||
|
|
||||||
|
@ -25,13 +25,22 @@ BIN_PATH = $(PREFIX)/bin
|
|||||||
|
|
||||||
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
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)
|
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/\..*//')
|
LLVM_MAJOR = ($shell $(LLVM_CONFIG) --version | sed 's/\..*//')
|
||||||
|
|
||||||
ifeq "$(LLVM_UNSUPPORTED)" "1"
|
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
|
endif
|
||||||
|
|
||||||
# this is not visible yet:
|
# this is not visible yet:
|
||||||
@ -40,7 +49,7 @@ ifeq "$(LLVM_MAJOR)" "9"
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
CFLAGS ?= -O3 -funroll-loops
|
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)\" \
|
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
||||||
-DVERSION=\"$(VERSION)\"
|
-DVERSION=\"$(VERSION)\"
|
||||||
ifdef AFL_TRACE_PC
|
ifdef AFL_TRACE_PC
|
||||||
@ -48,7 +57,7 @@ ifdef AFL_TRACE_PC
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
CXXFLAGS ?= -O3 -funroll-loops
|
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
|
-DVERSION=\"$(VERSION)\" -Wno-variadic-macros
|
||||||
|
|
||||||
CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -Wl,-znodelete -fno-rtti -fpic $(CXXFLAGS)
|
CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -Wl,-znodelete -fno-rtti -fpic $(CXXFLAGS)
|
||||||
@ -61,7 +70,7 @@ ifeq "$(shell uname)" "Darwin"
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq "$(shell uname)" "OpenBSD"
|
ifeq "$(shell uname)" "OpenBSD"
|
||||||
CLANG_LFL += `$(LLVM_CONFIG) --libdir`/libLLVM.so.0.0
|
CLANG_LFL += `$(LLVM_CONFIG) --libdir`/libLLVM.so
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# We were using llvm-config --bindir to get the location of clang, but
|
# We were using llvm-config --bindir to get the location of clang, but
|
||||||
@ -69,13 +78,18 @@ endif
|
|||||||
# probably better.
|
# probably better.
|
||||||
|
|
||||||
ifeq "$(origin CC)" "default"
|
ifeq "$(origin CC)" "default"
|
||||||
CC = clang
|
ifeq "$(shell uname)" "OpenBSD"
|
||||||
CXX = clang++
|
CC = $(BIN_PATH)/clang
|
||||||
|
CXX = $(BIN_PATH)/clang++
|
||||||
|
else
|
||||||
|
CC = clang
|
||||||
|
CXX = clang++
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# sanity check.
|
# sanity check.
|
||||||
# Are versions of clang --version and llvm-config --version equal?
|
# 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"
|
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)
|
test_build: $(PROGS)
|
||||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||||
unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_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)
|
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
|
echo 1 | ../afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||||
@rm -f test-instr
|
@rm -f test-instr
|
||||||
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please 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!"
|
@echo "[+] All right, the instrumentation seems to be working!"
|
||||||
|
|
||||||
all_done: test_build
|
all_done: test_build
|
||||||
|
@ -19,207 +19,267 @@
|
|||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
DenseMap<BasicBlock *, uint32_t> LMap;
|
DenseMap<BasicBlock *, uint32_t> LMap;
|
||||||
std::vector<BasicBlock *> Blocks;
|
std::vector<BasicBlock *> Blocks;
|
||||||
std::set<uint32_t> Marked , Markabove;
|
std::set<uint32_t> Marked, Markabove;
|
||||||
std::vector< std::vector<uint32_t> > Succs , Preds;
|
std::vector<std::vector<uint32_t> > Succs, Preds;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
|
||||||
void reset(){
|
|
||||||
LMap.clear();
|
LMap.clear();
|
||||||
Blocks.clear();
|
Blocks.clear();
|
||||||
Marked.clear();
|
Marked.clear();
|
||||||
Markabove.clear();
|
Markabove.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t start_point;
|
uint32_t start_point;
|
||||||
|
|
||||||
void labelEachBlock(Function *F) {
|
void labelEachBlock(Function *F) {
|
||||||
|
|
||||||
// Fake single endpoint;
|
// Fake single endpoint;
|
||||||
LMap[NULL] = Blocks.size();
|
LMap[NULL] = Blocks.size();
|
||||||
Blocks.push_back(NULL);
|
Blocks.push_back(NULL);
|
||||||
|
|
||||||
// Assign the unique LabelID to each block;
|
// Assign the unique LabelID to each block;
|
||||||
for (auto I = F->begin(), E = F->end(); I != E; ++I) {
|
for (auto I = F->begin(), E = F->end(); I != E; ++I) {
|
||||||
|
|
||||||
BasicBlock *BB = &*I;
|
BasicBlock *BB = &*I;
|
||||||
LMap[BB] = Blocks.size();
|
LMap[BB] = Blocks.size();
|
||||||
Blocks.push_back(BB);
|
Blocks.push_back(BB);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start_point = LMap[&F->getEntryBlock()];
|
start_point = LMap[&F->getEntryBlock()];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void buildCFG(Function *F) {
|
void buildCFG(Function *F) {
|
||||||
Succs.resize( Blocks.size() );
|
|
||||||
Preds.resize( Blocks.size() );
|
Succs.resize(Blocks.size());
|
||||||
for( size_t i = 0 ; i < Succs.size() ; i ++ ){
|
Preds.resize(Blocks.size());
|
||||||
Succs[ i ].clear();
|
for (size_t i = 0; i < Succs.size(); i++) {
|
||||||
Preds[ i ].clear();
|
|
||||||
|
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) {
|
for (auto S = F->begin(), E = F->end(); S != E; ++S) {
|
||||||
|
|
||||||
BasicBlock *BB = &*S;
|
BasicBlock *BB = &*S;
|
||||||
uint32_t MyID = LMap[BB];
|
uint32_t MyID = LMap[BB];
|
||||||
//if (succ_begin(BB) == succ_end(BB)) {
|
// if (succ_begin(BB) == succ_end(BB)) {
|
||||||
//Succs[MyID].push_back(FakeID);
|
|
||||||
//Marked.insert(MyID);
|
// Succs[MyID].push_back(FakeID);
|
||||||
|
// Marked.insert(MyID);
|
||||||
//}
|
//}
|
||||||
for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) {
|
for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) {
|
||||||
|
|
||||||
Succs[MyID].push_back(LMap[*I]);
|
Succs[MyID].push_back(LMap[*I]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector< std::vector<uint32_t> > tSuccs;
|
std::vector<std::vector<uint32_t> > tSuccs;
|
||||||
std::vector<bool> tag , indfs;
|
std::vector<bool> tag, indfs;
|
||||||
|
|
||||||
void DFStree(size_t now_id) {
|
void DFStree(size_t now_id) {
|
||||||
if(tag[now_id]) return;
|
|
||||||
tag[now_id]=true;
|
if (tag[now_id]) return;
|
||||||
indfs[now_id]=true;
|
tag[now_id] = true;
|
||||||
for (auto succ: tSuccs[now_id]) {
|
indfs[now_id] = true;
|
||||||
if(tag[succ] and indfs[succ]) {
|
for (auto succ : tSuccs[now_id]) {
|
||||||
|
|
||||||
|
if (tag[succ] and indfs[succ]) {
|
||||||
|
|
||||||
Marked.insert(succ);
|
Marked.insert(succ);
|
||||||
Markabove.insert(succ);
|
Markabove.insert(succ);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Succs[now_id].push_back(succ);
|
Succs[now_id].push_back(succ);
|
||||||
Preds[succ].push_back(now_id);
|
Preds[succ].push_back(now_id);
|
||||||
DFStree(succ);
|
DFStree(succ);
|
||||||
|
|
||||||
}
|
}
|
||||||
indfs[now_id]=false;
|
|
||||||
|
indfs[now_id] = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void turnCFGintoDAG(Function *F) {
|
void turnCFGintoDAG(Function *F) {
|
||||||
|
|
||||||
tSuccs = Succs;
|
tSuccs = Succs;
|
||||||
tag.resize(Blocks.size());
|
tag.resize(Blocks.size());
|
||||||
indfs.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();
|
Succs[i].clear();
|
||||||
tag[i]=false;
|
tag[i] = false;
|
||||||
indfs[i]=false;
|
indfs[i] = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DFStree(start_point);
|
DFStree(start_point);
|
||||||
for (size_t i = 0; i < Blocks.size(); ++ i)
|
for (size_t i = 0; i < Blocks.size(); ++i)
|
||||||
if( Succs[i].empty() ){
|
if (Succs[i].empty()) {
|
||||||
|
|
||||||
Succs[i].push_back(0);
|
Succs[i].push_back(0);
|
||||||
Preds[0].push_back(i);
|
Preds[0].push_back(i);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t timeStamp;
|
uint32_t timeStamp;
|
||||||
namespace DominatorTree{
|
namespace DominatorTree {
|
||||||
std::vector< std::vector<uint32_t> > cov;
|
|
||||||
std::vector<uint32_t> dfn, nfd, par, sdom, idom, mom, mn;
|
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;
|
void DominatorTree(Function *F) {
|
||||||
nfd[timeStamp - 1] = now;
|
|
||||||
for( auto succ : Succs[now] ) {
|
if (Blocks.empty()) return;
|
||||||
if( dfn[succ] == 0 ) {
|
uint32_t s = start_point;
|
||||||
par[succ] = now;
|
|
||||||
DFS(succ);
|
// 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) {
|
timeStamp = 0;
|
||||||
if( Blocks.empty() ) return;
|
DFS(s);
|
||||||
uint32_t s = start_point;
|
|
||||||
|
|
||||||
// Initialization
|
for (uint32_t i = Blocks.size() - 1; i >= 1u; i--) {
|
||||||
mn.resize(Blocks.size());
|
|
||||||
cov.resize(Blocks.size());
|
uint32_t now = nfd[i];
|
||||||
dfn.resize(Blocks.size());
|
if (now == Blocks.size()) { continue; }
|
||||||
nfd.resize(Blocks.size());
|
for (uint32_t pre : Preds[now]) {
|
||||||
par.resize(Blocks.size());
|
|
||||||
mom.resize(Blocks.size());
|
if (dfn[pre]) {
|
||||||
sdom.resize(Blocks.size());
|
|
||||||
idom.resize(Blocks.size());
|
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;
|
cov[sdom[now]].push_back(now);
|
||||||
DFS(s);
|
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;
|
for (uint32_t i = 1; i < Blocks.size(); i += 1) {
|
||||||
std::vector<uint32_t> TopoOrder, InDeg;
|
|
||||||
std::vector< std::vector<uint32_t> > t_Succ , t_Pred;
|
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) {
|
void Go(uint32_t now, uint32_t tt) {
|
||||||
if( now == tt ) return;
|
|
||||||
|
if (now == tt) return;
|
||||||
Visited[now] = InStack[now] = timeStamp;
|
Visited[now] = InStack[now] = timeStamp;
|
||||||
|
|
||||||
for(uint32_t nxt : Succs[now]) {
|
for (uint32_t nxt : Succs[now]) {
|
||||||
if(Visited[nxt] == timeStamp and InStack[nxt] == timeStamp) {
|
|
||||||
|
if (Visited[nxt] == timeStamp and InStack[nxt] == timeStamp) {
|
||||||
|
|
||||||
Marked.insert(nxt);
|
Marked.insert(nxt);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t_Succ[now].push_back(nxt);
|
t_Succ[now].push_back(nxt);
|
||||||
t_Pred[nxt].push_back(now);
|
t_Pred[nxt].push_back(now);
|
||||||
InDeg[nxt] += 1;
|
InDeg[nxt] += 1;
|
||||||
if(Visited[nxt] == timeStamp) {
|
if (Visited[nxt] == timeStamp) { continue; }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Go(nxt, tt);
|
Go(nxt, tt);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InStack[now] = 0;
|
InStack[now] = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TopologicalSort(uint32_t ss, uint32_t tt) {
|
void TopologicalSort(uint32_t ss, uint32_t tt) {
|
||||||
|
|
||||||
timeStamp += 1;
|
timeStamp += 1;
|
||||||
|
|
||||||
Go(ss, tt);
|
Go(ss, tt);
|
||||||
@ -227,76 +287,111 @@ void TopologicalSort(uint32_t ss, uint32_t tt) {
|
|||||||
TopoOrder.clear();
|
TopoOrder.clear();
|
||||||
std::queue<uint32_t> wait;
|
std::queue<uint32_t> wait;
|
||||||
wait.push(ss);
|
wait.push(ss);
|
||||||
while( not wait.empty() ) {
|
while (not wait.empty()) {
|
||||||
uint32_t now = wait.front(); wait.pop();
|
|
||||||
|
uint32_t now = wait.front();
|
||||||
|
wait.pop();
|
||||||
TopoOrder.push_back(now);
|
TopoOrder.push_back(now);
|
||||||
for(uint32_t nxt : t_Succ[now]) {
|
for (uint32_t nxt : t_Succ[now]) {
|
||||||
|
|
||||||
InDeg[nxt] -= 1;
|
InDeg[nxt] -= 1;
|
||||||
if(InDeg[nxt] == 0u) {
|
if (InDeg[nxt] == 0u) { wait.push(nxt); }
|
||||||
wait.push(nxt);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector< std::set<uint32_t> > NextMarked;
|
std::vector<std::set<uint32_t> > NextMarked;
|
||||||
bool Indistinguish(uint32_t node1, uint32_t node2) {
|
bool Indistinguish(uint32_t node1, uint32_t node2) {
|
||||||
if(NextMarked[node1].size() > NextMarked[node2].size()){
|
|
||||||
|
if (NextMarked[node1].size() > NextMarked[node2].size()) {
|
||||||
|
|
||||||
uint32_t _swap = node1;
|
uint32_t _swap = node1;
|
||||||
node1 = node2;
|
node1 = node2;
|
||||||
node2 = _swap;
|
node2 = _swap;
|
||||||
|
|
||||||
}
|
}
|
||||||
for(uint32_t x : NextMarked[node1]) {
|
|
||||||
if( NextMarked[node2].find(x) != NextMarked[node2].end() ) {
|
for (uint32_t x : NextMarked[node1]) {
|
||||||
return true;
|
|
||||||
}
|
if (NextMarked[node2].find(x) != NextMarked[node2].end()) { return true; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MakeUniq(uint32_t now) {
|
void MakeUniq(uint32_t now) {
|
||||||
|
|
||||||
bool StopFlag = false;
|
bool StopFlag = false;
|
||||||
if (Marked.find(now) == Marked.end()) {
|
if (Marked.find(now) == Marked.end()) {
|
||||||
for(uint32_t pred1 : t_Pred[now]) {
|
|
||||||
for(uint32_t pred2 : t_Pred[now]) {
|
for (uint32_t pred1 : t_Pred[now]) {
|
||||||
if(pred1 == pred2) continue;
|
|
||||||
if(Indistinguish(pred1, pred2)) {
|
for (uint32_t pred2 : t_Pred[now]) {
|
||||||
|
|
||||||
|
if (pred1 == pred2) continue;
|
||||||
|
if (Indistinguish(pred1, pred2)) {
|
||||||
|
|
||||||
Marked.insert(now);
|
Marked.insert(now);
|
||||||
StopFlag = true;
|
StopFlag = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (StopFlag) {
|
|
||||||
break;
|
if (StopFlag) { break; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if(Marked.find(now) != Marked.end()) {
|
|
||||||
|
if (Marked.find(now) != Marked.end()) {
|
||||||
|
|
||||||
NextMarked[now].insert(now);
|
NextMarked[now].insert(now);
|
||||||
|
|
||||||
} else {
|
} 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);
|
NextMarked[now].insert(x);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkSubGraph(uint32_t ss, uint32_t tt) {
|
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[i].clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NextMarked[TopoOrder[0]].insert(TopoOrder[0]);
|
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]);
|
MakeUniq(TopoOrder[i]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkVertice(Function *F) {
|
void MarkVertice(Function *F) {
|
||||||
|
|
||||||
uint32_t s = start_point;
|
uint32_t s = start_point;
|
||||||
|
|
||||||
InDeg.resize(Blocks.size());
|
InDeg.resize(Blocks.size());
|
||||||
@ -306,26 +401,32 @@ void MarkVertice(Function *F) {
|
|||||||
t_Pred.resize(Blocks.size());
|
t_Pred.resize(Blocks.size());
|
||||||
NextMarked.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;
|
Visited[i] = InStack[i] = InDeg[i] = 0;
|
||||||
t_Succ[i].clear();
|
t_Succ[i].clear();
|
||||||
t_Pred[i].clear();
|
t_Pred[i].clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timeStamp = 0;
|
timeStamp = 0;
|
||||||
uint32_t t = 0;
|
uint32_t t = 0;
|
||||||
//MarkSubGraph(s, t);
|
// MarkSubGraph(s, t);
|
||||||
//return;
|
// return;
|
||||||
|
|
||||||
|
while (s != t) {
|
||||||
|
|
||||||
while( s != t ) {
|
|
||||||
MarkSubGraph(DominatorTree::idom[t], t);
|
MarkSubGraph(DominatorTree::idom[t], t);
|
||||||
t = DominatorTree::idom[t];
|
t = DominatorTree::idom[t];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return {marked nodes}
|
// return {marked nodes}
|
||||||
std::pair<std::vector<BasicBlock *>,
|
std::pair<std::vector<BasicBlock *>, std::vector<BasicBlock *> > markNodes(
|
||||||
std::vector<BasicBlock *> >markNodes(Function *F) {
|
Function *F) {
|
||||||
|
|
||||||
assert(F->size() > 0 && "Function can not be empty");
|
assert(F->size() > 0 && "Function can not be empty");
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
@ -335,21 +436,30 @@ std::pair<std::vector<BasicBlock *>,
|
|||||||
DominatorTree::DominatorTree(F);
|
DominatorTree::DominatorTree(F);
|
||||||
MarkVertice(F);
|
MarkVertice(F);
|
||||||
|
|
||||||
std::vector<BasicBlock *> Result , ResultAbove;
|
std::vector<BasicBlock *> Result, ResultAbove;
|
||||||
for( uint32_t x : Markabove ) {
|
for (uint32_t x : Markabove) {
|
||||||
auto it = Marked.find( x );
|
|
||||||
if( it != Marked.end() )
|
auto it = Marked.find(x);
|
||||||
Marked.erase( it );
|
if (it != Marked.end()) Marked.erase(it);
|
||||||
if( x )
|
if (x) ResultAbove.push_back(Blocks[x]);
|
||||||
ResultAbove.push_back(Blocks[x]);
|
|
||||||
}
|
|
||||||
for( uint32_t x : Marked ) {
|
|
||||||
if (x == 0) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
Result.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/BasicBlock.h"
|
||||||
#include "llvm/IR/Function.h"
|
#include "llvm/IR/Function.h"
|
||||||
#include<vector>
|
#include <vector>
|
||||||
|
|
||||||
std::pair<std::vector<llvm::BasicBlock *>,
|
std::pair<std::vector<llvm::BasicBlock *>, std::vector<llvm::BasicBlock *>>
|
||||||
std::vector<llvm::BasicBlock *>> markNodes(llvm::Function *F);
|
markNodes(llvm::Function *F);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# InsTrim
|
# InsTrim
|
||||||
InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing
|
|
||||||
|
|
||||||
|
InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing
|
||||||
|
|
||||||
## Introduction
|
## 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
|
binary in llvm_mode. It is about 20-25% faster but as a cost has a lower
|
||||||
path discovery.
|
path discovery.
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## 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
|
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
|
afl-fuzz can see which loop path has been selected but not being able to
|
||||||
see how often the loop has been rerun.
|
see how often the loop has been rerun.
|
||||||
This again is a tradeoff for speed for less path information.
|
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
|
## 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 ../docs/README for the general instruction manual.)
|
||||||
(See ../gcc_plugin/README.gcc for the GCC-based instrumentation.)
|
(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_mode works with llvm versions 3.8.0 up to 9 !
|
||||||
! llvm version 9 does not work yet !
|
|
||||||
|
|
||||||
The code in this directory allows you to instrument programs for AFL using
|
The code in this directory allows you to instrument programs for AFL using
|
||||||
true compiler-level instrumentation, instead of the more crude
|
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.
|
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
|
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
|
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
|
is done, you can instrument third-party code in a way similar to the standard
|
||||||
operating mode of AFL, e.g.:
|
operating mode of AFL, e.g.:
|
||||||
|
|
||||||
|
```
|
||||||
CC=/path/to/afl/afl-clang-fast ./configure [...options...]
|
CC=/path/to/afl/afl-clang-fast ./configure [...options...]
|
||||||
make
|
make
|
||||||
|
```
|
||||||
|
|
||||||
Be sure to also include CXX set to afl-clang-fast++ for C++ code.
|
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
|
users, you need to build it before issuing 'make install' in the parent
|
||||||
directory.
|
directory.
|
||||||
|
|
||||||
3) Options
|
## 3) Options
|
||||||
|
|
||||||
Several options are present to make llvm_mode faster or help it rearrange
|
Several options are present to make llvm_mode faster or help it rearrange
|
||||||
the code to make afl-fuzz path discovery easier.
|
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
|
You can set this with AFL_LLVM_NOT_ZERO=1
|
||||||
See README.neverzero
|
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
|
This is an early-stage mechanism, so field reports are welcome. You can send bug
|
||||||
reports to <afl-users@googlegroups.com>.
|
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,
|
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
|
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:
|
With the location selected, add this code in the appropriate spot:
|
||||||
|
|
||||||
|
```c
|
||||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||||
__AFL_INIT();
|
__AFL_INIT();
|
||||||
#endif
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
You don't need the #ifdef guards, but including them ensures that the program
|
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.
|
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
|
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!
|
*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
|
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
|
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:
|
The basic structure of the program that does this would be:
|
||||||
|
|
||||||
|
```c
|
||||||
while (__AFL_LOOP(1000)) {
|
while (__AFL_LOOP(1000)) {
|
||||||
|
|
||||||
/* Read input data. */
|
/* Read input data. */
|
||||||
@ -175,6 +171,7 @@ The basic structure of the program that does this would be:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Exit normally */
|
/* Exit normally */
|
||||||
|
```
|
||||||
|
|
||||||
The numerical value specified within the loop controls the maximum number
|
The numerical value specified within the loop controls the maximum number
|
||||||
of iterations before AFL will restart the process from scratch. This minimizes
|
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.
|
any real performance benefits.
|
||||||
|
|
||||||
A more detailed template is shown in ../experimental/persistent_demo/.
|
A more detailed template is shown in ../experimental/persistent_demo/.
|
||||||
Similarly to the previous mode, the feature works only with afl-clang-fast;
|
Similarly to the previous mode, the feature works only with afl-clang-fast; #ifdef
|
||||||
#ifdef guards can be used to suppress it when using other compilers.
|
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
|
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
|
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,
|
faster than the normal fork() model, and compared to in-process fuzzing,
|
||||||
should be a lot more robust.
|
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
|
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
|
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
|
If you have a sufficiently recent compiler and want to give it a try, build
|
||||||
afl-clang-fast this way:
|
afl-clang-fast this way:
|
||||||
|
|
||||||
|
```
|
||||||
AFL_TRACE_PC=1 make clean all
|
AFL_TRACE_PC=1 make clean all
|
||||||
|
```
|
||||||
|
|
||||||
Note that this mode is currently about 20% slower than "vanilla" afl-clang-fast,
|
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
|
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
|
In larger, complex or reiterative programs the map that collects the edge pairs
|
||||||
can easily fill up and wrap.
|
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
|
If you want to enable this for llvm < 9 then set
|
||||||
|
|
||||||
|
```
|
||||||
export AFL_LLVM_NOT_ZERO=1
|
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
|
This file describes how you can selectively instrument only the source files
|
||||||
that are interesting to you using the LLVM instrumentation provided by
|
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>.
|
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
|
When building and testing complex programs where only a part of the program is
|
||||||
the fuzzing target, it often helps to only instrument the necessary parts of
|
the 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.
|
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/
|
The new code is part of the existing afl++ LLVM module in the llvm_mode/
|
||||||
subdirectory. There is nothing specifically to do :)
|
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
|
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
|
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:
|
For example if your source tree looks like this:
|
||||||
|
|
||||||
|
```
|
||||||
project/
|
project/
|
||||||
project/feature_a/a1.cpp
|
project/feature_a/a1.cpp
|
||||||
project/feature_a/a2.cpp
|
project/feature_a/a2.cpp
|
||||||
project/feature_b/b1.cpp
|
project/feature_b/b1.cpp
|
||||||
project/feature_b/b2.cpp
|
project/feature_b/b2.cpp
|
||||||
|
```
|
||||||
|
|
||||||
And you only want to test feature_a, then create a whitelist file containing:
|
And you only want to test feature_a, then create a whitelist file containing:
|
||||||
|
|
||||||
|
```
|
||||||
feature_a/a1.cpp
|
feature_a/a1.cpp
|
||||||
feature_a/a2.cpp
|
feature_a/a2.cpp
|
||||||
|
```
|
||||||
|
|
||||||
However if the whitelist file contains this, it works as well:
|
However if the whitelist file contains this, it works as well:
|
||||||
|
|
||||||
|
```
|
||||||
a1.cpp
|
a1.cpp
|
||||||
a2.cpp
|
a2.cpp
|
||||||
|
```
|
||||||
|
|
||||||
but it might lead to files being unwantedly instrumented if the same filename
|
but it might lead to files being unwantedly instrumented if the same filename
|
||||||
exists somewhere else in the project.
|
exists somewhere else in the project.
|
@ -23,10 +23,10 @@
|
|||||||
|
|
||||||
#define AFL_MAIN
|
#define AFL_MAIN
|
||||||
|
|
||||||
#include "../config.h"
|
#include "config.h"
|
||||||
#include "../types.h"
|
#include "types.h"
|
||||||
#include "../debug.h"
|
#include "debug.h"
|
||||||
#include "../alloc-inl.h"
|
#include "alloc-inl.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -34,16 +34,15 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
static u8* obj_path; /* Path to runtime libraries */
|
static u8* obj_path; /* Path to runtime libraries */
|
||||||
static u8** cc_params; /* Parameters passed to the real CC */
|
static u8** cc_params; /* Parameters passed to the real CC */
|
||||||
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
|
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
|
||||||
|
|
||||||
|
|
||||||
/* Try to find the runtime libraries. If that fails, abort. */
|
/* Try to find the runtime libraries. If that fails, abort. */
|
||||||
|
|
||||||
static void find_obj(u8* argv0) {
|
static void find_obj(u8* argv0) {
|
||||||
|
|
||||||
u8 *afl_path = getenv("AFL_PATH");
|
u8* afl_path = getenv("AFL_PATH");
|
||||||
u8 *slash, *tmp;
|
u8 *slash, *tmp;
|
||||||
|
|
||||||
if (afl_path) {
|
if (afl_path) {
|
||||||
@ -51,9 +50,11 @@ static void find_obj(u8* argv0) {
|
|||||||
tmp = alloc_printf("%s/afl-llvm-rt.o", afl_path);
|
tmp = alloc_printf("%s/afl-llvm-rt.o", afl_path);
|
||||||
|
|
||||||
if (!access(tmp, R_OK)) {
|
if (!access(tmp, R_OK)) {
|
||||||
|
|
||||||
obj_path = afl_path;
|
obj_path = afl_path;
|
||||||
ck_free(tmp);
|
ck_free(tmp);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ck_free(tmp);
|
ck_free(tmp);
|
||||||
@ -64,7 +65,7 @@ static void find_obj(u8* argv0) {
|
|||||||
|
|
||||||
if (slash) {
|
if (slash) {
|
||||||
|
|
||||||
u8 *dir;
|
u8* dir;
|
||||||
|
|
||||||
*slash = 0;
|
*slash = 0;
|
||||||
dir = ck_strdup(argv0);
|
dir = ck_strdup(argv0);
|
||||||
@ -73,9 +74,11 @@ static void find_obj(u8* argv0) {
|
|||||||
tmp = alloc_printf("%s/afl-llvm-rt.o", dir);
|
tmp = alloc_printf("%s/afl-llvm-rt.o", dir);
|
||||||
|
|
||||||
if (!access(tmp, R_OK)) {
|
if (!access(tmp, R_OK)) {
|
||||||
|
|
||||||
obj_path = dir;
|
obj_path = dir;
|
||||||
ck_free(tmp);
|
ck_free(tmp);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ck_free(tmp);
|
ck_free(tmp);
|
||||||
@ -84,33 +87,43 @@ static void find_obj(u8* argv0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) {
|
if (!access(AFL_PATH "/afl-llvm-rt.o", R_OK)) {
|
||||||
|
|
||||||
obj_path = AFL_PATH;
|
obj_path = AFL_PATH;
|
||||||
return;
|
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. */
|
/* Copy argv to cc_params, making the necessary edits. */
|
||||||
|
|
||||||
static void edit_params(u32 argc, char** argv) {
|
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 fortify_set = 0, asan_set = 0, x_set = 0, maybe_linking = 1, bit_mode = 0;
|
||||||
u8 *name;
|
u8* name;
|
||||||
|
|
||||||
cc_params = ck_alloc((argc + 128) * sizeof(u8*));
|
cc_params = ck_alloc((argc + 128) * sizeof(u8*));
|
||||||
|
|
||||||
name = strrchr(argv[0], '/');
|
name = strrchr(argv[0], '/');
|
||||||
if (!name) name = argv[0]; else name++;
|
if (!name)
|
||||||
|
name = argv[0];
|
||||||
|
else
|
||||||
|
name++;
|
||||||
|
|
||||||
if (!strcmp(name, "afl-clang-fast++")) {
|
if (!strcmp(name, "afl-clang-fast++")) {
|
||||||
|
|
||||||
u8* alt_cxx = getenv("AFL_CXX");
|
u8* alt_cxx = getenv("AFL_CXX");
|
||||||
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
|
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
u8* alt_cc = getenv("AFL_CC");
|
u8* alt_cc = getenv("AFL_CC");
|
||||||
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
|
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There are three ways to compile with afl-clang-fast. In the traditional
|
/* 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
|
much faster but has less coverage. Finally tere is the experimental
|
||||||
'trace-pc-guard' mode, we use native LLVM instrumentation callbacks
|
'trace-pc-guard' mode, we use native LLVM instrumentation callbacks
|
||||||
instead. For trace-pc-guard see:
|
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
|
// 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++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = "-load";
|
cc_params[cc_par_cnt++] = "-load";
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
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++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = "-load";
|
cc_params[cc_par_cnt++] = "-load";
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
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++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = "-load";
|
cc_params[cc_par_cnt++] = "-load";
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
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
|
// /laf
|
||||||
|
|
||||||
#ifdef USE_TRACE_PC
|
#ifdef USE_TRACE_PC
|
||||||
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default
|
cc_params[cc_par_cnt++] =
|
||||||
//cc_params[cc_par_cnt++] = "-mllvm";
|
"-fsanitize-coverage=trace-pc-guard"; // edge coverage by default
|
||||||
//cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-cmp,trace-div,trace-gep";
|
// cc_params[cc_par_cnt++] = "-mllvm";
|
||||||
//cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
|
// cc_params[cc_par_cnt++] =
|
||||||
|
// "-fsanitize-coverage=trace-cmp,trace-div,trace-gep";
|
||||||
|
// cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
|
||||||
#else
|
#else
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
cc_params[cc_par_cnt++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = "-load";
|
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);
|
cc_params[cc_par_cnt++] = alloc_printf("%s/libLLVMInsTrim.so", obj_path);
|
||||||
else
|
else
|
||||||
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
|
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";
|
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;
|
if (argc == 1 && !strcmp(argv[1], "-v")) maybe_linking = 0;
|
||||||
|
|
||||||
while (--argc) {
|
while (--argc) {
|
||||||
|
|
||||||
u8* cur = *(++argv);
|
u8* cur = *(++argv);
|
||||||
|
|
||||||
if (!strcmp(cur, "-m32")) bit_mode = 32;
|
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"))
|
if (!strcmp(cur, "-c") || !strcmp(cur, "-S") || !strcmp(cur, "-E"))
|
||||||
maybe_linking = 0;
|
maybe_linking = 0;
|
||||||
|
|
||||||
if (!strcmp(cur, "-fsanitize=address") ||
|
if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
|
||||||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;
|
asan_set = 1;
|
||||||
|
|
||||||
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
|
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
|
||||||
|
|
||||||
if (!strcmp(cur, "-shared")) maybe_linking = 0;
|
if (!strcmp(cur, "-shared")) maybe_linking = 0;
|
||||||
|
|
||||||
if (!strcmp(cur, "-Wl,-z,defs") ||
|
if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined"))
|
||||||
!strcmp(cur, "-Wl,--no-undefined")) continue;
|
continue;
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = cur;
|
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";
|
cc_params[cc_par_cnt++] = "-fstack-protector-all";
|
||||||
|
|
||||||
if (!fortify_set)
|
if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
|
||||||
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_ASAN")) {
|
||||||
|
|
||||||
if (getenv("AFL_USE_MSAN"))
|
if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive");
|
||||||
FATAL("ASAN and MSAN are mutually exclusive");
|
|
||||||
|
|
||||||
if (getenv("AFL_HARDEN"))
|
if (getenv("AFL_HARDEN"))
|
||||||
FATAL("ASAN and AFL_HARDEN are mutually exclusive");
|
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")) {
|
} else if (getenv("AFL_USE_MSAN")) {
|
||||||
|
|
||||||
if (getenv("AFL_USE_ASAN"))
|
if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive");
|
||||||
FATAL("ASAN and MSAN are mutually exclusive");
|
|
||||||
|
|
||||||
if (getenv("AFL_HARDEN"))
|
if (getenv("AFL_HARDEN"))
|
||||||
FATAL("MSAN and AFL_HARDEN are mutually exclusive");
|
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"))
|
if (getenv("AFL_INST_RATIO"))
|
||||||
FATAL("AFL_INST_RATIO not available at compile time with 'trace-pc'.");
|
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")) {
|
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)="
|
cc_params[cc_par_cnt++] =
|
||||||
"({ static volatile char *_B __attribute__((used)); "
|
"-D__AFL_LOOP(_A)="
|
||||||
" _B = (char*)\"" PERSIST_SIG "\"; "
|
"({ static volatile char *_B __attribute__((used)); "
|
||||||
|
" _B = (char*)\"" PERSIST_SIG
|
||||||
|
"\"; "
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
"__attribute__((visibility(\"default\"))) "
|
"__attribute__((visibility(\"default\"))) "
|
||||||
"int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
|
"int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
|
||||||
#else
|
#else
|
||||||
"__attribute__((visibility(\"default\"))) "
|
"__attribute__((visibility(\"default\"))) "
|
||||||
"int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
|
"int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
|
||||||
#endif /* ^__APPLE__ */
|
#endif /* ^__APPLE__ */
|
||||||
"_L(_A); })";
|
"_L(_A); })";
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-D__AFL_INIT()="
|
cc_params[cc_par_cnt++] =
|
||||||
"do { static volatile char *_A __attribute__((used)); "
|
"-D__AFL_INIT()="
|
||||||
" _A = (char*)\"" DEFER_SIG "\"; "
|
"do { static volatile char *_A __attribute__((used)); "
|
||||||
|
" _A = (char*)\"" DEFER_SIG
|
||||||
|
"\"; "
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
"__attribute__((visibility(\"default\"))) "
|
"__attribute__((visibility(\"default\"))) "
|
||||||
"void _I(void) __asm__(\"___afl_manual_init\"); "
|
"void _I(void) __asm__(\"___afl_manual_init\"); "
|
||||||
#else
|
#else
|
||||||
"__attribute__((visibility(\"default\"))) "
|
"__attribute__((visibility(\"default\"))) "
|
||||||
"void _I(void) __asm__(\"__afl_manual_init\"); "
|
"void _I(void) __asm__(\"__afl_manual_init\"); "
|
||||||
#endif /* ^__APPLE__ */
|
#endif /* ^__APPLE__ */
|
||||||
"_I(); } while (0)";
|
"_I(); } while (0)";
|
||||||
|
|
||||||
if (maybe_linking) {
|
if (maybe_linking) {
|
||||||
|
|
||||||
if (x_set) {
|
if (x_set) {
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-x";
|
cc_params[cc_par_cnt++] = "-x";
|
||||||
cc_params[cc_par_cnt++] = "none";
|
cc_params[cc_par_cnt++] = "none";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (bit_mode) {
|
switch (bit_mode) {
|
||||||
@ -340,7 +371,6 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Main entry point */
|
/* Main entry point */
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
@ -348,46 +378,53 @@ int main(int argc, char** argv) {
|
|||||||
if (isatty(2) && !getenv("AFL_QUIET")) {
|
if (isatty(2) && !getenv("AFL_QUIET")) {
|
||||||
|
|
||||||
#ifdef USE_TRACE_PC
|
#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
|
#else
|
||||||
SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n");
|
SAYF(cCYA "afl-clang-fast" VERSION cRST " by <lszekeres@google.com>\n");
|
||||||
#endif /* ^USE_TRACE_PC */
|
#endif /* ^USE_TRACE_PC */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
|
|
||||||
SAYF("\n"
|
SAYF(
|
||||||
"This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
|
"\n"
|
||||||
"for clang, letting you recompile third-party code with the required runtime\n"
|
"This is a helper application for afl-fuzz. It serves as a drop-in "
|
||||||
"instrumentation. A common use pattern would be one of the following:\n\n"
|
"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"
|
" CC=%s/afl-clang-fast ./configure\n"
|
||||||
" CXX=%s/afl-clang-fast++ ./configure\n\n"
|
" CXX=%s/afl-clang-fast++ ./configure\n\n"
|
||||||
|
|
||||||
"In contrast to the traditional afl-clang tool, this version is implemented as\n"
|
"In contrast to the traditional afl-clang tool, this version is "
|
||||||
"an LLVM pass and tends to offer improved performance with slow programs.\n\n"
|
"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"
|
"You can specify custom next-stage toolchain via AFL_CC and AFL_CXX. "
|
||||||
"AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
|
"Setting\n"
|
||||||
BIN_PATH, BIN_PATH);
|
"AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
|
||||||
|
BIN_PATH, BIN_PATH);
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
find_obj(argv[0]);
|
find_obj(argv[0]);
|
||||||
|
|
||||||
edit_params(argc, argv);
|
edit_params(argc, argv);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
int i = 0;
|
int i = 0;
|
||||||
printf("EXEC:");
|
printf("EXEC:");
|
||||||
while (cc_params[i] != NULL)
|
while (cc_params[i] != NULL)
|
||||||
printf(" %s", cc_params[i++]);
|
printf(" %s", cc_params[i++]);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
*/
|
*/
|
||||||
|
|
||||||
execvp(cc_params[0], (char**)cc_params);
|
execvp(cc_params[0], (char**)cc_params);
|
||||||
|
|
||||||
@ -396,3 +433,4 @@ int main(int argc, char** argv) {
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
|
|
||||||
#define AFL_LLVM_PASS
|
#define AFL_LLVM_PASS
|
||||||
|
|
||||||
#include "../config.h"
|
#include "config.h"
|
||||||
#include "../debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -48,50 +48,52 @@ using namespace llvm;
|
|||||||
|
|
||||||
namespace {
|
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;
|
char AFLCoverage::ID = 0;
|
||||||
|
|
||||||
|
|
||||||
bool AFLCoverage::runOnModule(Module &M) {
|
bool AFLCoverage::runOnModule(Module &M) {
|
||||||
|
|
||||||
LLVMContext &C = M.getContext();
|
LLVMContext &C = M.getContext();
|
||||||
|
|
||||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||||
unsigned int cur_loc = 0;
|
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");
|
SAYF(cCYA "afl-llvm-pass" VERSION cRST " by <lszekeres@google.com>\n");
|
||||||
|
|
||||||
} else be_quiet = 1;
|
} else
|
||||||
|
|
||||||
|
be_quiet = 1;
|
||||||
|
|
||||||
/* Decide instrumentation ratio */
|
/* Decide instrumentation ratio */
|
||||||
|
|
||||||
char* inst_ratio_str = getenv("AFL_INST_RATIO");
|
char * inst_ratio_str = getenv("AFL_INST_RATIO");
|
||||||
unsigned int inst_ratio = 100;
|
unsigned int inst_ratio = 100;
|
||||||
|
|
||||||
if (inst_ratio_str) {
|
if (inst_ratio_str) {
|
||||||
@ -119,7 +123,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if LLVM_VERSION_MAJOR < 9
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
char* neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO");
|
char *neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Get globals for the SHM region and the previous location. Note that
|
/* 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,
|
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
|
||||||
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
GlobalVariable *AFLPrevLoc = new GlobalVariable(
|
GlobalVariable *AFLPrevLoc = new GlobalVariable(
|
||||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc",
|
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc");
|
||||||
0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
#else
|
||||||
|
GlobalVariable *AFLPrevLoc = new GlobalVariable(
|
||||||
|
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0,
|
||||||
|
GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Instrument all the things! */
|
/* Instrument all the things! */
|
||||||
|
|
||||||
@ -141,58 +150,77 @@ bool AFLCoverage::runOnModule(Module &M) {
|
|||||||
for (auto &BB : F) {
|
for (auto &BB : F) {
|
||||||
|
|
||||||
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||||
IRBuilder<> IRB(&(*IP));
|
IRBuilder<> IRB(&(*IP));
|
||||||
|
|
||||||
if (!myWhitelist.empty()) {
|
if (!myWhitelist.empty()) {
|
||||||
bool instrumentBlock = false;
|
|
||||||
|
|
||||||
/* Get the current location using debug information.
|
bool instrumentBlock = false;
|
||||||
* 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());
|
|
||||||
|
|
||||||
unsigned int instLine = cDILoc->getLine();
|
/* Get the current location using debug information.
|
||||||
StringRef instFilename = cDILoc->getFilename();
|
* 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()) {
|
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||||
/* If the original location is empty, try using the inlined location */
|
|
||||||
DILocation *oDILoc = cDILoc->getInlinedAt();
|
unsigned int instLine = cDILoc->getLine();
|
||||||
if (oDILoc) {
|
StringRef instFilename = cDILoc->getFilename();
|
||||||
instFilename = oDILoc->getFilename();
|
|
||||||
instLine = oDILoc->getLine();
|
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
|
/* Continue only if we know where we actually are */
|
||||||
* not whitelisted, so we skip instrumentation. */
|
if (!instFilename.str().empty()) {
|
||||||
if (!instrumentBlock) continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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;
|
if (AFL_R(100) >= inst_ratio) continue;
|
||||||
|
|
||||||
/* Make up cur_loc */
|
/* Make up cur_loc */
|
||||||
|
|
||||||
//cur_loc++;
|
// cur_loc++;
|
||||||
cur_loc = AFL_R(MAP_SIZE);
|
cur_loc = AFL_R(MAP_SIZE);
|
||||||
|
|
||||||
// only instrument if this basic block is the destination of a previous
|
// 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
|
// this gets rid of ~5-10% of instrumentations that are unnecessary
|
||||||
// result: a little more speed and less map pollution
|
// result: a little more speed and less map pollution
|
||||||
int more_than_one = -1;
|
int more_than_one = -1;
|
||||||
//fprintf(stderr, "BB %u: ", cur_loc);
|
// fprintf(stderr, "BB %u: ", cur_loc);
|
||||||
for (BasicBlock *Pred : predecessors(&BB)) {
|
for (BasicBlock *Pred : predecessors(&BB)) {
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
if (more_than_one == -1)
|
if (more_than_one == -1) more_than_one = 0;
|
||||||
more_than_one = 0;
|
// fprintf(stderr, " %p=>", Pred);
|
||||||
//fprintf(stderr, " %p=>", Pred);
|
|
||||||
for (BasicBlock *Succ : successors(Pred)) {
|
for (BasicBlock *Succ : successors(Pred)) {
|
||||||
//if (count > 0)
|
|
||||||
|
// if (count > 0)
|
||||||
// fprintf(stderr, "|");
|
// fprintf(stderr, "|");
|
||||||
if (Succ != NULL) count++;
|
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)
|
// fprintf(stderr, " == %d\n", more_than_one);
|
||||||
continue;
|
if (more_than_one != 1) continue;
|
||||||
|
|
||||||
ConstantInt *CurLoc = ConstantInt::get(Int32Ty, cur_loc);
|
ConstantInt *CurLoc = ConstantInt::get(Int32Ty, cur_loc);
|
||||||
|
|
||||||
@ -231,7 +262,8 @@ bool AFLCoverage::runOnModule(Module &M) {
|
|||||||
|
|
||||||
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
|
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
|
||||||
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
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 */
|
/* Update bitmap */
|
||||||
|
|
||||||
@ -241,7 +273,9 @@ bool AFLCoverage::runOnModule(Module &M) {
|
|||||||
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
|
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
|
||||||
|
|
||||||
#if LLVM_VERSION_MAJOR < 9
|
#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
|
#endif
|
||||||
/* hexcoder: Realize a counter that skips zero during overflow.
|
/* 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
|
||||||
@ -252,48 +286,67 @@ bool AFLCoverage::runOnModule(Module &M) {
|
|||||||
* Counter + 1 -> {Counter, OverflowFlag}
|
* Counter + 1 -> {Counter, OverflowFlag}
|
||||||
* Counter + OverflowFlag -> Counter
|
* Counter + OverflowFlag -> Counter
|
||||||
*/
|
*/
|
||||||
/* // we keep the old solutions just in case
|
/* // we keep the old solutions just in case
|
||||||
// Solution #1
|
// Solution #1
|
||||||
if (neverZero_counters_str[0] == '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));
|
CallInst *AddOv =
|
||||||
Value *SumWithOverflowBit = AddOv;
|
IRB.CreateBinaryIntrinsic(Intrinsic::uadd_with_overflow, Counter,
|
||||||
Incr = IRB.CreateAdd(IRB.CreateExtractValue(SumWithOverflowBit, 0), // sum
|
ConstantInt::get(Int8Ty, 1));
|
||||||
IRB.CreateZExt( // convert from one bit type to 8 bits type
|
AddOv->setMetadata(M.getMDKindID("nosanitize"),
|
||||||
IRB.CreateExtractValue(SumWithOverflowBit, 1), // overflow
|
MDNode::get(C, None)); Value *SumWithOverflowBit = AddOv; Incr =
|
||||||
Int8Ty));
|
IRB.CreateAdd(IRB.CreateExtractValue(SumWithOverflowBit, 0), // sum
|
||||||
// Solution #2
|
IRB.CreateZExt( // convert from one bit
|
||||||
} else if (neverZero_counters_str[0] == '2') {
|
type to 8 bits type IRB.CreateExtractValue(SumWithOverflowBit, 1), //
|
||||||
auto cf = IRB.CreateICmpEQ(Counter, ConstantInt::get(Int8Ty, 255));
|
overflow Int8Ty));
|
||||||
Value *HowMuch = IRB.CreateAdd(ConstantInt::get(Int8Ty, 1), cf);
|
// Solution #2
|
||||||
Incr = IRB.CreateAdd(Counter, HowMuch);
|
|
||||||
// Solution #3
|
} else if (neverZero_counters_str[0] == '2') {
|
||||||
} else if (neverZero_counters_str[0] == '3') {
|
|
||||||
*/
|
auto cf = IRB.CreateICmpEQ(Counter,
|
||||||
// this is the solution we choose because llvm9 should do the right thing here
|
ConstantInt::get(Int8Ty, 255)); Value *HowMuch =
|
||||||
auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
|
IRB.CreateAdd(ConstantInt::get(Int8Ty, 1), cf); Incr =
|
||||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
IRB.CreateAdd(Counter, HowMuch);
|
||||||
Incr = IRB.CreateAdd(Incr, carry);
|
// 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
|
// Solution #4
|
||||||
|
|
||||||
} else if (neverZero_counters_str[0] == '4') {
|
} else if (neverZero_counters_str[0] == '4') {
|
||||||
|
|
||||||
auto cf = IRB.CreateICmpULT(Incr, ConstantInt::get(Int8Ty, 1));
|
auto cf = IRB.CreateICmpULT(Incr, ConstantInt::get(Int8Ty, 1));
|
||||||
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||||
Incr = IRB.CreateAdd(Incr, carry);
|
Incr = IRB.CreateAdd(Incr, carry);
|
||||||
|
|
||||||
} else {
|
} 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
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#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 */
|
/* 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));
|
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
|
||||||
inst_blocks++;
|
inst_blocks++;
|
||||||
@ -304,11 +357,16 @@ bool AFLCoverage::runOnModule(Module &M) {
|
|||||||
|
|
||||||
if (!be_quiet) {
|
if (!be_quiet) {
|
||||||
|
|
||||||
if (!inst_blocks) WARNF("No instrumentation targets found.");
|
if (!inst_blocks)
|
||||||
else OKF("Instrumented %u locations (%s mode, ratio %u%%).",
|
WARNF("No instrumentation targets found.");
|
||||||
inst_blocks, getenv("AFL_HARDEN") ? "hardened" :
|
else
|
||||||
((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) ?
|
OKF("Instrumented %u locations (%s mode, ratio %u%%).", inst_blocks,
|
||||||
"ASAN/MSAN" : "non-hardened"), inst_ratio);
|
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 &,
|
static void registerAFLPass(const PassManagerBuilder &,
|
||||||
legacy::PassManagerBase &PM) {
|
legacy::PassManagerBase &PM) {
|
||||||
|
|
||||||
@ -324,9 +381,9 @@ static void registerAFLPass(const PassManagerBuilder &,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static RegisterStandardPasses RegisterAFLPass(
|
static RegisterStandardPasses RegisterAFLPass(
|
||||||
PassManagerBuilder::EP_OptimizerLast, registerAFLPass);
|
PassManagerBuilder::EP_OptimizerLast, registerAFLPass);
|
||||||
|
|
||||||
static RegisterStandardPasses RegisterAFLPass0(
|
static RegisterStandardPasses RegisterAFLPass0(
|
||||||
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
|
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
|
||||||
|
|
||||||
|
@ -19,8 +19,11 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../config.h"
|
#ifdef __ANDROID__
|
||||||
#include "../types.h"
|
#include "android-ashmem.h"
|
||||||
|
#endif
|
||||||
|
#include "config.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -39,61 +42,67 @@
|
|||||||
the LLVM-generated runtime initialization pass, not before. */
|
the LLVM-generated runtime initialization pass, not before. */
|
||||||
|
|
||||||
#ifdef USE_TRACE_PC
|
#ifdef USE_TRACE_PC
|
||||||
# define CONST_PRIO 5
|
#define CONST_PRIO 5
|
||||||
#else
|
#else
|
||||||
# define CONST_PRIO 0
|
#define CONST_PRIO 0
|
||||||
#endif /* ^USE_TRACE_PC */
|
#endif /* ^USE_TRACE_PC */
|
||||||
|
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
|
||||||
/* Globals needed by the injected instrumentation. The __afl_area_initial region
|
/* 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.
|
is used for instrumentation output before __afl_map_shm() has a chance to
|
||||||
It will end up as .comm, so it shouldn't be too wasteful. */
|
run. It will end up as .comm, so it shouldn't be too wasteful. */
|
||||||
|
|
||||||
u8 __afl_area_initial[MAP_SIZE];
|
u8 __afl_area_initial[MAP_SIZE];
|
||||||
u8* __afl_area_ptr = __afl_area_initial;
|
u8* __afl_area_ptr = __afl_area_initial;
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
u32 __afl_prev_loc;
|
||||||
|
#else
|
||||||
__thread u32 __afl_prev_loc;
|
__thread u32 __afl_prev_loc;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Running in persistent mode? */
|
/* Running in persistent mode? */
|
||||||
|
|
||||||
static u8 is_persistent;
|
static u8 is_persistent;
|
||||||
|
|
||||||
|
|
||||||
/* SHM setup. */
|
/* SHM setup. */
|
||||||
|
|
||||||
static void __afl_map_shm(void) {
|
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
|
/* 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
|
early-stage __afl_area_initial region that is needed to allow some really
|
||||||
hacky .init code to work correctly in projects such as OpenSSL. */
|
hacky .init code to work correctly in projects such as OpenSSL. */
|
||||||
|
|
||||||
if (id_str) {
|
if (id_str) {
|
||||||
|
|
||||||
#ifdef USEMMAP
|
#ifdef USEMMAP
|
||||||
const char *shm_file_path = id_str;
|
const char* shm_file_path = id_str;
|
||||||
int shm_fd = -1;
|
int shm_fd = -1;
|
||||||
unsigned char *shm_base = NULL;
|
unsigned char* shm_base = NULL;
|
||||||
|
|
||||||
/* create the shared memory segment as if it was a file */
|
/* create the shared memory segment as if it was a file */
|
||||||
shm_fd = shm_open(shm_file_path, O_RDWR, 0600);
|
shm_fd = shm_open(shm_file_path, O_RDWR, 0600);
|
||||||
if (shm_fd == -1) {
|
if (shm_fd == -1) {
|
||||||
|
|
||||||
printf("shm_open() failed\n");
|
printf("shm_open() failed\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* map the shared memory segment to the address space of the process */
|
/* 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);
|
shm_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
|
||||||
if (shm_base == MAP_FAILED) {
|
if (shm_base == MAP_FAILED) {
|
||||||
|
|
||||||
close(shm_fd);
|
close(shm_fd);
|
||||||
shm_fd = -1;
|
shm_fd = -1;
|
||||||
|
|
||||||
printf("mmap() failed\n");
|
printf("mmap() failed\n");
|
||||||
exit(2);
|
exit(2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__afl_area_ptr = shm_base;
|
__afl_area_ptr = shm_base;
|
||||||
@ -105,7 +114,7 @@ static void __afl_map_shm(void) {
|
|||||||
|
|
||||||
/* Whooooops. */
|
/* 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,
|
/* Write something into the bitmap so that even with low AFL_INST_RATIO,
|
||||||
our parent doesn't give up on us. */
|
our parent doesn't give up on us. */
|
||||||
@ -116,15 +125,14 @@ static void __afl_map_shm(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Fork server logic. */
|
/* Fork server logic. */
|
||||||
|
|
||||||
static void __afl_start_forkserver(void) {
|
static void __afl_start_forkserver(void) {
|
||||||
|
|
||||||
static u8 tmp[4];
|
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);
|
void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL);
|
||||||
|
|
||||||
@ -147,8 +155,10 @@ static void __afl_start_forkserver(void) {
|
|||||||
process. */
|
process. */
|
||||||
|
|
||||||
if (child_stopped && was_killed) {
|
if (child_stopped && was_killed) {
|
||||||
|
|
||||||
child_stopped = 0;
|
child_stopped = 0;
|
||||||
if (waitpid(child_pid, &status, 0) < 0) _exit(1);
|
if (waitpid(child_pid, &status, 0) < 0) _exit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!child_stopped) {
|
if (!child_stopped) {
|
||||||
@ -161,6 +171,7 @@ static void __afl_start_forkserver(void) {
|
|||||||
/* In child process: close fds, resume execution. */
|
/* In child process: close fds, resume execution. */
|
||||||
|
|
||||||
if (!child_pid) {
|
if (!child_pid) {
|
||||||
|
|
||||||
signal(SIGCHLD, old_sigchld_handler);
|
signal(SIGCHLD, old_sigchld_handler);
|
||||||
|
|
||||||
close(FORKSRV_FD);
|
close(FORKSRV_FD);
|
||||||
@ -200,7 +211,6 @@ static void __afl_start_forkserver(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* A simplified persistent mode handler, used as explained in README.llvm. */
|
/* A simplified persistent mode handler, used as explained in README.llvm. */
|
||||||
|
|
||||||
int __afl_persistent_loop(unsigned int max_cnt) {
|
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);
|
memset(__afl_area_ptr, 0, MAP_SIZE);
|
||||||
__afl_area_ptr[0] = 1;
|
__afl_area_ptr[0] = 1;
|
||||||
__afl_prev_loc = 0;
|
__afl_prev_loc = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cycle_cnt = max_cnt;
|
cycle_cnt = max_cnt;
|
||||||
first_pass = 0;
|
first_pass = 0;
|
||||||
return 1;
|
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
|
/* This one can be called from user code when deferred forkserver mode
|
||||||
is enabled. */
|
is enabled. */
|
||||||
|
|
||||||
@ -273,7 +283,6 @@ void __afl_manual_init(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Proper initialization routine. */
|
/* Proper initialization routine. */
|
||||||
|
|
||||||
__attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) {
|
__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.
|
/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard.
|
||||||
It remains non-operational in the traditional, plugin-backed LLVM mode.
|
It remains non-operational in the traditional, plugin-backed LLVM mode.
|
||||||
For more info about 'trace-pc-guard', see README.llvm.
|
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). */
|
edge (as opposed to every basic block). */
|
||||||
|
|
||||||
void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
|
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
|
/* Init callback. Populates instrumentation IDs. Note that we're using
|
||||||
ID of 0 as a special value to indicate non-instrumented bits. That may
|
ID of 0 as a special value to indicate non-instrumented bits. That may
|
||||||
@ -314,8 +323,10 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) {
|
|||||||
if (x) inst_ratio = atoi(x);
|
if (x) inst_ratio = atoi(x);
|
||||||
|
|
||||||
if (!inst_ratio || inst_ratio > 100) {
|
if (!inst_ratio || inst_ratio > 100) {
|
||||||
|
|
||||||
fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n");
|
fprintf(stderr, "[-] ERROR: Invalid AFL_INST_RATIO (must be 1-100).\n");
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure that the first element in the range is always set - we use that
|
/* 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) {
|
while (start < stop) {
|
||||||
|
|
||||||
if (R(100) < inst_ratio) *start = R(MAP_SIZE - 1) + 1;
|
if (R(100) < inst_ratio)
|
||||||
else *start = 0;
|
*start = R(MAP_SIZE - 1) + 1;
|
||||||
|
else
|
||||||
|
*start = 0;
|
||||||
|
|
||||||
start++;
|
start++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,198 +36,236 @@ using namespace llvm;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class CompareTransform : public ModulePass {
|
class CompareTransform : public ModulePass {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static char ID;
|
static char ID;
|
||||||
CompareTransform() : ModulePass(ID) {
|
CompareTransform() : ModulePass(ID) {
|
||||||
}
|
|
||||||
|
|
||||||
bool runOnModule(Module &M) override;
|
}
|
||||||
|
|
||||||
|
bool runOnModule(Module &M) override;
|
||||||
|
|
||||||
#if LLVM_VERSION_MAJOR < 4
|
#if LLVM_VERSION_MAJOR < 4
|
||||||
const char * getPassName() const override {
|
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);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#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;
|
char CompareTransform::ID = 0;
|
||||||
|
|
||||||
bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const bool processMemcmp
|
bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||||
, const bool processStrncmp, const bool processStrcasecmp, const bool processStrncasecmp) {
|
const bool processMemcmp,
|
||||||
|
const bool processStrncmp,
|
||||||
|
const bool processStrcasecmp,
|
||||||
|
const bool processStrncasecmp) {
|
||||||
|
|
||||||
std::vector<CallInst*> calls;
|
std::vector<CallInst *> calls;
|
||||||
LLVMContext &C = M.getContext();
|
LLVMContext & C = M.getContext();
|
||||||
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
IntegerType * Int8Ty = IntegerType::getInt8Ty(C);
|
||||||
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
IntegerType * Int32Ty = IntegerType::getInt32Ty(C);
|
||||||
IntegerType *Int64Ty = IntegerType::getInt64Ty(C);
|
IntegerType * Int64Ty = IntegerType::getInt64Ty(C);
|
||||||
|
|
||||||
#if LLVM_VERSION_MAJOR < 9
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
Constant*
|
Constant *
|
||||||
#else
|
#else
|
||||||
FunctionCallee
|
FunctionCallee
|
||||||
#endif
|
#endif
|
||||||
c = M.getOrInsertFunction("tolower",
|
c = M.getOrInsertFunction("tolower", Int32Ty, Int32Ty
|
||||||
Int32Ty,
|
|
||||||
Int32Ty
|
|
||||||
#if LLVM_VERSION_MAJOR < 5
|
#if LLVM_VERSION_MAJOR < 5
|
||||||
, nullptr
|
,
|
||||||
|
nullptr
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
|
Function *tolowerFn = cast<Function>(c);
|
||||||
|
#else
|
||||||
|
FunctionCallee tolowerFn = c;
|
||||||
#endif
|
#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 &F : M) {
|
||||||
|
|
||||||
for (auto &BB : F) {
|
for (auto &BB : F) {
|
||||||
for(auto &IN: BB) {
|
|
||||||
CallInst* callInst = nullptr;
|
for (auto &IN : BB) {
|
||||||
|
|
||||||
|
CallInst *callInst = nullptr;
|
||||||
|
|
||||||
if ((callInst = dyn_cast<CallInst>(&IN))) {
|
if ((callInst = dyn_cast<CallInst>(&IN))) {
|
||||||
|
|
||||||
bool isStrcmp = processStrcmp;
|
bool isStrcmp = processStrcmp;
|
||||||
bool isMemcmp = processMemcmp;
|
bool isMemcmp = processMemcmp;
|
||||||
bool isStrncmp = processStrncmp;
|
bool isStrncmp = processStrncmp;
|
||||||
bool isStrcasecmp = processStrcasecmp;
|
bool isStrcasecmp = processStrcasecmp;
|
||||||
bool isStrncasecmp = processStrncasecmp;
|
bool isStrncasecmp = processStrncasecmp;
|
||||||
|
|
||||||
Function *Callee = callInst->getCalledFunction();
|
Function *Callee = callInst->getCalledFunction();
|
||||||
if (!Callee)
|
if (!Callee) continue;
|
||||||
continue;
|
if (callInst->getCallingConv() != llvm::CallingConv::C) continue;
|
||||||
if (callInst->getCallingConv() != llvm::CallingConv::C)
|
|
||||||
continue;
|
|
||||||
StringRef FuncName = Callee->getName();
|
StringRef FuncName = Callee->getName();
|
||||||
isStrcmp &= !FuncName.compare(StringRef("strcmp"));
|
isStrcmp &= !FuncName.compare(StringRef("strcmp"));
|
||||||
isMemcmp &= !FuncName.compare(StringRef("memcmp"));
|
isMemcmp &= !FuncName.compare(StringRef("memcmp"));
|
||||||
isStrncmp &= !FuncName.compare(StringRef("strncmp"));
|
isStrncmp &= !FuncName.compare(StringRef("strncmp"));
|
||||||
isStrcasecmp &= !FuncName.compare(StringRef("strcasecmp"));
|
isStrcasecmp &= !FuncName.compare(StringRef("strcasecmp"));
|
||||||
isStrncasecmp &= !FuncName.compare(StringRef("strncasecmp"));
|
isStrncasecmp &= !FuncName.compare(StringRef("strncasecmp"));
|
||||||
|
|
||||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && !isStrncasecmp)
|
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
|
||||||
|
!isStrncasecmp)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function prototype */
|
/* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function
|
||||||
|
* prototype */
|
||||||
FunctionType *FT = Callee->getFunctionType();
|
FunctionType *FT = Callee->getFunctionType();
|
||||||
|
|
||||||
|
isStrcmp &=
|
||||||
isStrcmp &= FT->getNumParams() == 2 &&
|
FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) &&
|
||||||
FT->getReturnType()->isIntegerTy(32) &&
|
FT->getParamType(0) == FT->getParamType(1) &&
|
||||||
FT->getParamType(0) == FT->getParamType(1) &&
|
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext());
|
||||||
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext());
|
isStrcasecmp &=
|
||||||
isStrcasecmp &= FT->getNumParams() == 2 &&
|
FT->getNumParams() == 2 && FT->getReturnType()->isIntegerTy(32) &&
|
||||||
FT->getReturnType()->isIntegerTy(32) &&
|
FT->getParamType(0) == FT->getParamType(1) &&
|
||||||
FT->getParamType(0) == FT->getParamType(1) &&
|
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext());
|
||||||
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext());
|
isMemcmp &= FT->getNumParams() == 3 &&
|
||||||
isMemcmp &= FT->getNumParams() == 3 &&
|
|
||||||
FT->getReturnType()->isIntegerTy(32) &&
|
FT->getReturnType()->isIntegerTy(32) &&
|
||||||
FT->getParamType(0)->isPointerTy() &&
|
FT->getParamType(0)->isPointerTy() &&
|
||||||
FT->getParamType(1)->isPointerTy() &&
|
FT->getParamType(1)->isPointerTy() &&
|
||||||
FT->getParamType(2)->isIntegerTy();
|
FT->getParamType(2)->isIntegerTy();
|
||||||
isStrncmp &= FT->getNumParams() == 3 &&
|
isStrncmp &= FT->getNumParams() == 3 &&
|
||||||
FT->getReturnType()->isIntegerTy(32) &&
|
FT->getReturnType()->isIntegerTy(32) &&
|
||||||
FT->getParamType(0) == FT->getParamType(1) &&
|
FT->getParamType(0) == FT->getParamType(1) &&
|
||||||
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()) &&
|
FT->getParamType(0) ==
|
||||||
FT->getParamType(2)->isIntegerTy();
|
IntegerType::getInt8PtrTy(M.getContext()) &&
|
||||||
|
FT->getParamType(2)->isIntegerTy();
|
||||||
isStrncasecmp &= FT->getNumParams() == 3 &&
|
isStrncasecmp &= FT->getNumParams() == 3 &&
|
||||||
FT->getReturnType()->isIntegerTy(32) &&
|
FT->getReturnType()->isIntegerTy(32) &&
|
||||||
FT->getParamType(0) == FT->getParamType(1) &&
|
FT->getParamType(0) == FT->getParamType(1) &&
|
||||||
FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()) &&
|
FT->getParamType(0) ==
|
||||||
FT->getParamType(2)->isIntegerTy();
|
IntegerType::getInt8PtrTy(M.getContext()) &&
|
||||||
|
FT->getParamType(2)->isIntegerTy();
|
||||||
|
|
||||||
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && !isStrncasecmp)
|
if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp &&
|
||||||
|
!isStrncasecmp)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* is a str{n,}{case,}cmp/memcmp, check if we have
|
/* is a str{n,}{case,}cmp/memcmp, check if we have
|
||||||
* str{case,}cmp(x, "const") or str{case,}cmp("const", x)
|
* str{case,}cmp(x, "const") or str{case,}cmp("const", x)
|
||||||
* strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..)
|
* strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..)
|
||||||
* memcmp(x, "const", ..) or memcmp("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;
|
StringRef Str1, Str2;
|
||||||
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
||||||
bool HasStr2 = getConstantStringInfo(Str2P, Str2);
|
bool HasStr2 = getConstantStringInfo(Str2P, Str2);
|
||||||
|
|
||||||
/* handle cases of one string is const, one string is variable */
|
/* handle cases of one string is const, one string is variable */
|
||||||
if (!(HasStr1 ^ HasStr2))
|
if (!(HasStr1 ^ HasStr2)) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (isMemcmp || isStrncmp || isStrncasecmp) {
|
if (isMemcmp || isStrncmp || isStrncasecmp) {
|
||||||
|
|
||||||
/* check if third operand is a constant integer
|
/* check if third operand is a constant integer
|
||||||
* strlen("constStr") and sizeof() are treated as constant */
|
* strlen("constStr") and sizeof() are treated as constant */
|
||||||
Value *op2 = callInst->getArgOperand(2);
|
Value * op2 = callInst->getArgOperand(2);
|
||||||
ConstantInt* ilen = dyn_cast<ConstantInt>(op2);
|
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
|
||||||
if (!ilen)
|
if (!ilen) continue;
|
||||||
continue;
|
/* final precaution: if size of compare is larger than constant
|
||||||
/* final precaution: if size of compare is larger than constant string skip it*/
|
* string skip it*/
|
||||||
uint64_t literalLength = HasStr1 ? GetStringLength(Str1P) : GetStringLength(Str2P);
|
uint64_t literalLength =
|
||||||
if (literalLength < ilen->getZExtValue())
|
HasStr1 ? GetStringLength(Str1P) : GetStringLength(Str2P);
|
||||||
continue;
|
if (literalLength < ilen->getZExtValue()) continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
calls.push_back(callInst);
|
calls.push_back(callInst);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!calls.size())
|
if (!calls.size()) return false;
|
||||||
return false;
|
errs() << "Replacing " << calls.size()
|
||||||
errs() << "Replacing " << calls.size() << " calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp\n";
|
<< " 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);
|
Value *Str1P = callInst->getArgOperand(0),
|
||||||
StringRef Str1, Str2, ConstStr;
|
*Str2P = callInst->getArgOperand(1);
|
||||||
|
StringRef Str1, Str2, ConstStr;
|
||||||
std::string TmpConstStr;
|
std::string TmpConstStr;
|
||||||
Value *VarStr;
|
Value * VarStr;
|
||||||
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
||||||
getConstantStringInfo(Str2P, Str2);
|
getConstantStringInfo(Str2P, Str2);
|
||||||
uint64_t constLen, sizedLen;
|
uint64_t constLen, sizedLen;
|
||||||
bool isMemcmp = !callInst->getCalledFunction()->getName().compare(StringRef("memcmp"));
|
bool isMemcmp =
|
||||||
bool isSizedcmp = isMemcmp
|
!callInst->getCalledFunction()->getName().compare(StringRef("memcmp"));
|
||||||
|| !callInst->getCalledFunction()->getName().compare(StringRef("strncmp"))
|
bool isSizedcmp = isMemcmp ||
|
||||||
|| !callInst->getCalledFunction()->getName().compare(StringRef("strncasecmp"));
|
!callInst->getCalledFunction()->getName().compare(
|
||||||
bool isCaseInsensitive = !callInst->getCalledFunction()->getName().compare(StringRef("strcasecmp"))
|
StringRef("strncmp")) ||
|
||||||
|| !callInst->getCalledFunction()->getName().compare(StringRef("strncasecmp"));
|
!callInst->getCalledFunction()->getName().compare(
|
||||||
|
StringRef("strncasecmp"));
|
||||||
|
bool isCaseInsensitive = !callInst->getCalledFunction()->getName().compare(
|
||||||
|
StringRef("strcasecmp")) ||
|
||||||
|
!callInst->getCalledFunction()->getName().compare(
|
||||||
|
StringRef("strncasecmp"));
|
||||||
|
|
||||||
if (isSizedcmp) {
|
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();
|
sizedLen = ilen->getZExtValue();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasStr1) {
|
if (HasStr1) {
|
||||||
|
|
||||||
TmpConstStr = Str1.str();
|
TmpConstStr = Str1.str();
|
||||||
VarStr = Str2P;
|
VarStr = Str2P;
|
||||||
constLen = isMemcmp ? sizedLen : GetStringLength(Str1P);
|
constLen = isMemcmp ? sizedLen : GetStringLength(Str1P);
|
||||||
}
|
|
||||||
else {
|
} else {
|
||||||
|
|
||||||
TmpConstStr = Str2.str();
|
TmpConstStr = Str2.str();
|
||||||
VarStr = Str1P;
|
VarStr = Str1P;
|
||||||
constLen = isMemcmp ? sizedLen : GetStringLength(Str2P);
|
constLen = isMemcmp ? sizedLen : GetStringLength(Str2P);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* properly handle zero terminated C strings by adding the terminating 0 to
|
/* properly handle zero terminated C strings by adding the terminating 0 to
|
||||||
* the StringRef (in comparison to std::string a StringRef has built-in
|
* the StringRef (in comparison to std::string a StringRef has built-in
|
||||||
* runtime bounds checking, which makes debugging easier) */
|
* 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) {
|
if (isSizedcmp && constLen > sizedLen) { 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 */
|
/* split before the call instruction */
|
||||||
BasicBlock *bb = callInst->getParent();
|
BasicBlock *bb = callInst->getParent();
|
||||||
BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(callInst));
|
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);
|
BranchInst::Create(end_bb, next_bb);
|
||||||
PHINode *PN = PHINode::Create(Int32Ty, constLen + 1, "cmp_phi");
|
PHINode *PN = PHINode::Create(Int32Ty, constLen + 1, "cmp_phi");
|
||||||
|
|
||||||
@ -245,20 +283,22 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const
|
|||||||
|
|
||||||
char c = isCaseInsensitive ? tolower(ConstStr[i]) : ConstStr[i];
|
char c = isCaseInsensitive ? tolower(ConstStr[i]) : ConstStr[i];
|
||||||
|
|
||||||
|
|
||||||
BasicBlock::iterator IP = next_bb->getFirstInsertionPt();
|
BasicBlock::iterator IP = next_bb->getFirstInsertionPt();
|
||||||
IRBuilder<> IRB(&*IP);
|
IRBuilder<> IRB(&*IP);
|
||||||
|
|
||||||
Value* v = ConstantInt::get(Int64Ty, i);
|
Value *v = ConstantInt::get(Int64Ty, i);
|
||||||
Value *ele = IRB.CreateInBoundsGEP(VarStr, v, "empty");
|
Value *ele = IRB.CreateInBoundsGEP(VarStr, v, "empty");
|
||||||
Value *load = IRB.CreateLoad(ele);
|
Value *load = IRB.CreateLoad(ele);
|
||||||
if (isCaseInsensitive) {
|
if (isCaseInsensitive) {
|
||||||
|
|
||||||
// load >= 'A' && load <= 'Z' ? load | 0x020 : load
|
// load >= 'A' && load <= 'Z' ? load | 0x020 : load
|
||||||
std::vector<Value *> args;
|
std::vector<Value *> args;
|
||||||
args.push_back(load);
|
args.push_back(load);
|
||||||
load = IRB.CreateCall(tolowerFn, args, "tmp");
|
load = IRB.CreateCall(tolowerFn, args, "tmp");
|
||||||
load = IRB.CreateTrunc(load, Int8Ty);
|
load = IRB.CreateTrunc(load, Int8Ty);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Value *isub;
|
Value *isub;
|
||||||
if (HasStr1)
|
if (HasStr1)
|
||||||
isub = IRB.CreateSub(ConstantInt::get(Int8Ty, c), load);
|
isub = IRB.CreateSub(ConstantInt::get(Int8Ty, c), load);
|
||||||
@ -268,48 +308,56 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const
|
|||||||
Value *sext = IRB.CreateSExt(isub, Int32Ty);
|
Value *sext = IRB.CreateSExt(isub, Int32Ty);
|
||||||
PN->addIncoming(sext, cur_bb);
|
PN->addIncoming(sext, cur_bb);
|
||||||
|
|
||||||
|
|
||||||
if (i < constLen - 1) {
|
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);
|
BranchInst::Create(end_bb, next_bb);
|
||||||
|
|
||||||
Value *icmp = IRB.CreateICmpEQ(isub, ConstantInt::get(Int8Ty, 0));
|
Value *icmp = IRB.CreateICmpEQ(isub, ConstantInt::get(Int8Ty, 0));
|
||||||
IRB.CreateCondBr(icmp, next_bb, end_bb);
|
IRB.CreateCondBr(icmp, next_bb, end_bb);
|
||||||
cur_bb->getTerminator()->eraseFromParent();
|
cur_bb->getTerminator()->eraseFromParent();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
//IRB.CreateBr(end_bb);
|
|
||||||
|
// IRB.CreateBr(end_bb);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//add offset to varstr
|
// add offset to varstr
|
||||||
//create load
|
// create load
|
||||||
//create signed isub
|
// create signed isub
|
||||||
//create icmp
|
// create icmp
|
||||||
//create jcc
|
// create jcc
|
||||||
//create next_bb
|
// create next_bb
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* since the call is the first instruction of the bb it is safe to
|
/* since the call is the first instruction of the bb it is safe to
|
||||||
* replace it with a phi instruction */
|
* replace it with a phi instruction */
|
||||||
BasicBlock::iterator ii(callInst);
|
BasicBlock::iterator ii(callInst);
|
||||||
ReplaceInstWithInst(callInst->getParent()->getInstList(), ii, PN);
|
ReplaceInstWithInst(callInst->getParent()->getInstList(), ii, PN);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompareTransform::runOnModule(Module &M) {
|
bool CompareTransform::runOnModule(Module &M) {
|
||||||
|
|
||||||
if (getenv("AFL_QUIET") == NULL)
|
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);
|
transformCmps(M, true, true, true, true, true);
|
||||||
verifyModule(M);
|
verifyModule(M);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registerCompTransPass(const PassManagerBuilder &,
|
static void registerCompTransPass(const PassManagerBuilder &,
|
||||||
legacy::PassManagerBase &PM) {
|
legacy::PassManagerBase &PM) {
|
||||||
|
|
||||||
auto p = new CompareTransform();
|
auto p = new CompareTransform();
|
||||||
PM.add(p);
|
PM.add(p);
|
||||||
|
@ -27,117 +27,126 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
namespace {
|
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
|
#if LLVM_VERSION_MAJOR >= 4
|
||||||
StringRef getPassName() const override {
|
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);
|
|
||||||
|
|
||||||
};
|
#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;
|
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 */
|
* ICMP instructions with predicate xGT or xLT and EQ */
|
||||||
bool SplitComparesTransform::simplifyCompares(Module &M) {
|
bool SplitComparesTransform::simplifyCompares(Module &M) {
|
||||||
LLVMContext &C = M.getContext();
|
|
||||||
std::vector<Instruction*> icomps;
|
LLVMContext & C = M.getContext();
|
||||||
IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
|
std::vector<Instruction *> icomps;
|
||||||
|
IntegerType * Int1Ty = IntegerType::getInt1Ty(C);
|
||||||
|
|
||||||
/* iterate over all functions, bbs and instruction and add
|
/* iterate over all functions, bbs and instruction and add
|
||||||
* all integer comparisons with >= and <= predicates to the icomps vector */
|
* all integer comparisons with >= and <= predicates to the icomps vector */
|
||||||
for (auto &F : M) {
|
for (auto &F : M) {
|
||||||
|
|
||||||
for (auto &BB : F) {
|
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 = dyn_cast<CmpInst>(&IN))) {
|
||||||
|
|
||||||
if (selectcmpInst->getPredicate() != CmpInst::ICMP_UGE &&
|
if (selectcmpInst->getPredicate() != CmpInst::ICMP_UGE &&
|
||||||
selectcmpInst->getPredicate() != CmpInst::ICMP_SGE &&
|
selectcmpInst->getPredicate() != CmpInst::ICMP_SGE &&
|
||||||
selectcmpInst->getPredicate() != CmpInst::ICMP_ULE &&
|
selectcmpInst->getPredicate() != CmpInst::ICMP_ULE &&
|
||||||
selectcmpInst->getPredicate() != CmpInst::ICMP_SLE ) {
|
selectcmpInst->getPredicate() != CmpInst::ICMP_SLE) {
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto op0 = selectcmpInst->getOperand(0);
|
auto op0 = selectcmpInst->getOperand(0);
|
||||||
auto op1 = selectcmpInst->getOperand(1);
|
auto op1 = selectcmpInst->getOperand(1);
|
||||||
|
|
||||||
IntegerType* intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||||
IntegerType* intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
||||||
|
|
||||||
/* this is probably not needed but we do it anyway */
|
/* this is probably not needed but we do it anyway */
|
||||||
if (!intTyOp0 || !intTyOp1) {
|
if (!intTyOp0 || !intTyOp1) { continue; }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
icomps.push_back(selectcmpInst);
|
icomps.push_back(selectcmpInst);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!icomps.size()) {
|
if (!icomps.size()) { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (auto &IcmpInst : icomps) {
|
||||||
|
|
||||||
for (auto &IcmpInst: icomps) {
|
BasicBlock *bb = IcmpInst->getParent();
|
||||||
BasicBlock* bb = IcmpInst->getParent();
|
|
||||||
|
|
||||||
auto op0 = IcmpInst->getOperand(0);
|
auto op0 = IcmpInst->getOperand(0);
|
||||||
auto op1 = IcmpInst->getOperand(1);
|
auto op1 = IcmpInst->getOperand(1);
|
||||||
|
|
||||||
/* find out what the new predicate is going to be */
|
/* 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;
|
CmpInst::Predicate new_pred;
|
||||||
switch(pred) {
|
switch (pred) {
|
||||||
case CmpInst::ICMP_UGE:
|
|
||||||
new_pred = CmpInst::ICMP_UGT;
|
case CmpInst::ICMP_UGE: new_pred = CmpInst::ICMP_UGT; break;
|
||||||
break;
|
case CmpInst::ICMP_SGE: new_pred = CmpInst::ICMP_SGT; break;
|
||||||
case CmpInst::ICMP_SGE:
|
case CmpInst::ICMP_ULE: new_pred = CmpInst::ICMP_ULT; break;
|
||||||
new_pred = CmpInst::ICMP_SGT;
|
case CmpInst::ICMP_SLE: new_pred = CmpInst::ICMP_SLT; break;
|
||||||
break;
|
default: // keep the compiler happy
|
||||||
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;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* split before the icmp instruction */
|
/* 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)
|
/* the old bb now contains a unconditional jump to the new one (end_bb)
|
||||||
* we need to delete it later */
|
* we need to delete it later */
|
||||||
|
|
||||||
/* create the ICMP instruction with new_pred and add it to the old basic
|
/* 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 */
|
* 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);
|
icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1);
|
||||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_np);
|
bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_np);
|
||||||
|
|
||||||
/* create a new basic block which holds the new EQ icmp */
|
/* create a new basic block which holds the new EQ icmp */
|
||||||
Instruction *icmp_eq;
|
Instruction *icmp_eq;
|
||||||
/* insert middle_bb before end_bb */
|
/* insert middle_bb before end_bb */
|
||||||
BasicBlock* middle_bb = BasicBlock::Create(C, "injected",
|
BasicBlock *middle_bb =
|
||||||
end_bb->getParent(), end_bb);
|
BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
|
||||||
icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1);
|
icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1);
|
||||||
middle_bb->getInstList().push_back(icmp_eq);
|
middle_bb->getInstList().push_back(icmp_eq);
|
||||||
/* add an unconditional branch to the end of middle_bb with destination
|
/* 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);
|
BranchInst::Create(end_bb, middle_bb, icmp_np, bb);
|
||||||
term->eraseFromParent();
|
term->eraseFromParent();
|
||||||
|
|
||||||
|
|
||||||
/* replace the old IcmpInst (which is the first inst in end_bb) with a PHI
|
/* replace the old IcmpInst (which is the first inst in end_bb) with a PHI
|
||||||
* inst to wire up the loose ends */
|
* inst to wire up the loose ends */
|
||||||
PHINode *PN = PHINode::Create(Int1Ty, 2, "");
|
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 */
|
/* replace the old IcmpInst with our new and shiny PHI inst */
|
||||||
BasicBlock::iterator ii(IcmpInst);
|
BasicBlock::iterator ii(IcmpInst);
|
||||||
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this function transforms signed compares to equivalent unsigned compares */
|
/* this function transforms signed compares to equivalent unsigned compares */
|
||||||
bool SplitComparesTransform::simplifySignedness(Module &M) {
|
bool SplitComparesTransform::simplifySignedness(Module &M) {
|
||||||
LLVMContext &C = M.getContext();
|
|
||||||
std::vector<Instruction*> icomps;
|
LLVMContext & C = M.getContext();
|
||||||
IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
|
std::vector<Instruction *> icomps;
|
||||||
|
IntegerType * Int1Ty = IntegerType::getInt1Ty(C);
|
||||||
|
|
||||||
/* iterate over all functions, bbs and instruction and add
|
/* iterate over all functions, bbs and instruction and add
|
||||||
* all signed compares to icomps vector */
|
* all signed compares to icomps vector */
|
||||||
for (auto &F : M) {
|
for (auto &F : M) {
|
||||||
|
|
||||||
for (auto &BB : F) {
|
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 = dyn_cast<CmpInst>(&IN))) {
|
||||||
|
|
||||||
if (selectcmpInst->getPredicate() != CmpInst::ICMP_SGT &&
|
if (selectcmpInst->getPredicate() != CmpInst::ICMP_SGT &&
|
||||||
selectcmpInst->getPredicate() != CmpInst::ICMP_SLT
|
selectcmpInst->getPredicate() != CmpInst::ICMP_SLT) {
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto op0 = selectcmpInst->getOperand(0);
|
auto op0 = selectcmpInst->getOperand(0);
|
||||||
auto op1 = selectcmpInst->getOperand(1);
|
auto op1 = selectcmpInst->getOperand(1);
|
||||||
|
|
||||||
IntegerType* intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||||
IntegerType* intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
||||||
|
|
||||||
/* see above */
|
/* see above */
|
||||||
if (!intTyOp0 || !intTyOp1) {
|
if (!intTyOp0 || !intTyOp1) { continue; }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* i think this is not possible but to lazy to look it up */
|
/* i think this is not possible but to lazy to look it up */
|
||||||
if (intTyOp0->getBitWidth() != intTyOp1->getBitWidth()) {
|
if (intTyOp0->getBitWidth() != intTyOp1->getBitWidth()) { continue; }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
icomps.push_back(selectcmpInst);
|
icomps.push_back(selectcmpInst);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!icomps.size()) {
|
if (!icomps.size()) { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &IcmpInst: icomps) {
|
for (auto &IcmpInst : icomps) {
|
||||||
BasicBlock* bb = IcmpInst->getParent();
|
|
||||||
|
BasicBlock *bb = IcmpInst->getParent();
|
||||||
|
|
||||||
auto op0 = IcmpInst->getOperand(0);
|
auto op0 = IcmpInst->getOperand(0);
|
||||||
auto op1 = IcmpInst->getOperand(1);
|
auto op1 = IcmpInst->getOperand(1);
|
||||||
|
|
||||||
IntegerType* intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||||
unsigned bitw = intTyOp0->getBitWidth();
|
unsigned bitw = intTyOp0->getBitWidth();
|
||||||
IntegerType *IntType = IntegerType::get(C, bitw);
|
IntegerType *IntType = IntegerType::get(C, bitw);
|
||||||
|
|
||||||
|
|
||||||
/* get the new predicate */
|
/* get the new predicate */
|
||||||
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
||||||
CmpInst::Predicate new_pred;
|
CmpInst::Predicate new_pred;
|
||||||
if (pred == CmpInst::ICMP_SGT) {
|
if (pred == CmpInst::ICMP_SGT) {
|
||||||
|
|
||||||
new_pred = CmpInst::ICMP_UGT;
|
new_pred = CmpInst::ICMP_UGT;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
new_pred = CmpInst::ICMP_ULT;
|
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
|
/* create a 1 bit compare for the sign bit. to do this shift and trunc
|
||||||
* the original operands so only the first bit remains.*/
|
* the original operands so only the first bit remains.*/
|
||||||
Instruction *s_op0, *t_op0, *s_op1, *t_op1, *icmp_sign_bit;
|
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);
|
bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op0);
|
||||||
t_op0 = new TruncInst(s_op0, Int1Ty);
|
t_op0 = new TruncInst(s_op0, Int1Ty);
|
||||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), t_op0);
|
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);
|
bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op1);
|
||||||
t_op1 = new TruncInst(s_op1, Int1Ty);
|
t_op1 = new TruncInst(s_op1, Int1Ty);
|
||||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), t_op1);
|
bb->getInstList().insert(bb->getTerminator()->getIterator(), t_op1);
|
||||||
|
|
||||||
/* compare of the sign bits */
|
/* 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);
|
bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_sign_bit);
|
||||||
|
|
||||||
/* create a new basic block which is executed if the signedness bit is
|
/* create a new basic block which is executed if the signedness bit is
|
||||||
* different */
|
* different */
|
||||||
Instruction *icmp_inv_sig_cmp;
|
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 (pred == CmpInst::ICMP_SGT) {
|
||||||
|
|
||||||
/* if we check for > and the op0 positive and op1 negative then the final
|
/* 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
|
* result is true. if op0 negative and op1 pos, the cmp must result
|
||||||
* in false
|
* 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 {
|
} else {
|
||||||
|
|
||||||
/* just the inverse of the above statement */
|
/* 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);
|
sign_bb->getInstList().push_back(icmp_inv_sig_cmp);
|
||||||
BranchInst::Create(end_bb, sign_bb);
|
BranchInst::Create(end_bb, sign_bb);
|
||||||
|
|
||||||
/* create a new bb which is executed if signedness is equal */
|
/* create a new bb which is executed if signedness is equal */
|
||||||
Instruction *icmp_usign_cmp;
|
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 */
|
/* we can do a normal unsigned compare now */
|
||||||
icmp_usign_cmp = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1);
|
icmp_usign_cmp = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1);
|
||||||
middle_bb->getInstList().push_back(icmp_usign_cmp);
|
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);
|
BranchInst::Create(middle_bb, sign_bb, icmp_sign_bit, bb);
|
||||||
term->eraseFromParent();
|
term->eraseFromParent();
|
||||||
|
|
||||||
|
|
||||||
PHINode *PN = PHINode::Create(Int1Ty, 2, "");
|
PHINode *PN = PHINode::Create(Int1Ty, 2, "");
|
||||||
|
|
||||||
PN->addIncoming(icmp_usign_cmp, middle_bb);
|
PN->addIncoming(icmp_usign_cmp, middle_bb);
|
||||||
@ -293,91 +321,100 @@ bool SplitComparesTransform::simplifySignedness(Module &M) {
|
|||||||
|
|
||||||
BasicBlock::iterator ii(IcmpInst);
|
BasicBlock::iterator ii(IcmpInst);
|
||||||
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* splits icmps of size bitw into two nested icmps with bitw/2 size each */
|
/* splits icmps of size bitw into two nested icmps with bitw/2 size each */
|
||||||
bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
||||||
|
|
||||||
LLVMContext &C = M.getContext();
|
LLVMContext &C = M.getContext();
|
||||||
|
|
||||||
IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
|
IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
|
||||||
IntegerType *OldIntType = IntegerType::get(C, bitw);
|
IntegerType *OldIntType = IntegerType::get(C, bitw);
|
||||||
IntegerType *NewIntType = IntegerType::get(C, bitw / 2);
|
IntegerType *NewIntType = IntegerType::get(C, bitw / 2);
|
||||||
|
|
||||||
std::vector<Instruction*> icomps;
|
std::vector<Instruction *> icomps;
|
||||||
|
|
||||||
if (bitw % 2) {
|
if (bitw % 2) { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* not supported yet */
|
/* not supported yet */
|
||||||
if (bitw > 64) {
|
if (bitw > 64) { return false; }
|
||||||
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 */
|
* unctions were executed only these four predicates should exist */
|
||||||
for (auto &F : M) {
|
for (auto &F : M) {
|
||||||
|
|
||||||
for (auto &BB : F) {
|
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 = dyn_cast<CmpInst>(&IN))) {
|
||||||
|
|
||||||
if(selectcmpInst->getPredicate() != CmpInst::ICMP_EQ &&
|
if (selectcmpInst->getPredicate() != CmpInst::ICMP_EQ &&
|
||||||
selectcmpInst->getPredicate() != CmpInst::ICMP_NE &&
|
selectcmpInst->getPredicate() != CmpInst::ICMP_NE &&
|
||||||
selectcmpInst->getPredicate() != CmpInst::ICMP_UGT &&
|
selectcmpInst->getPredicate() != CmpInst::ICMP_UGT &&
|
||||||
selectcmpInst->getPredicate() != CmpInst::ICMP_ULT
|
selectcmpInst->getPredicate() != CmpInst::ICMP_ULT) {
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto op0 = selectcmpInst->getOperand(0);
|
auto op0 = selectcmpInst->getOperand(0);
|
||||||
auto op1 = selectcmpInst->getOperand(1);
|
auto op1 = selectcmpInst->getOperand(1);
|
||||||
|
|
||||||
IntegerType* intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
|
||||||
IntegerType* intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
|
||||||
|
|
||||||
if (!intTyOp0 || !intTyOp1) {
|
if (!intTyOp0 || !intTyOp1) { continue; }
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if the bitwidths are the one we are looking for */
|
/* 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;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
icomps.push_back(selectcmpInst);
|
icomps.push_back(selectcmpInst);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!icomps.size()) {
|
if (!icomps.size()) { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &IcmpInst: icomps) {
|
for (auto &IcmpInst : icomps) {
|
||||||
BasicBlock* bb = IcmpInst->getParent();
|
|
||||||
|
BasicBlock *bb = IcmpInst->getParent();
|
||||||
|
|
||||||
auto op0 = IcmpInst->getOperand(0);
|
auto op0 = IcmpInst->getOperand(0);
|
||||||
auto op1 = IcmpInst->getOperand(1);
|
auto op1 = IcmpInst->getOperand(1);
|
||||||
|
|
||||||
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
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 */
|
/* create the comparison of the top halves of the original operands */
|
||||||
Instruction *s_op0, *op0_high, *s_op1, *op1_high, *icmp_high;
|
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);
|
bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op0);
|
||||||
op0_high = new TruncInst(s_op0, NewIntType);
|
op0_high = new TruncInst(s_op0, NewIntType);
|
||||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), op0_high);
|
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);
|
bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op1);
|
||||||
op1_high = new TruncInst(s_op1, NewIntType);
|
op1_high = new TruncInst(s_op1, NewIntType);
|
||||||
bb->getInstList().insert(bb->getTerminator()->getIterator(), op1_high);
|
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 > < */
|
/* now we have to destinguish between == != and > < */
|
||||||
if (pred == CmpInst::ICMP_EQ || pred == CmpInst::ICMP_NE) {
|
if (pred == CmpInst::ICMP_EQ || pred == CmpInst::ICMP_NE) {
|
||||||
|
|
||||||
/* transformation for == and != icmps */
|
/* transformation for == and != icmps */
|
||||||
|
|
||||||
/* create a compare for the lower half of the original operands */
|
/* create a compare for the lower half of the original operands */
|
||||||
Instruction *op0_low, *op1_low, *icmp_low;
|
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);
|
op0_low = new TruncInst(op0, NewIntType);
|
||||||
cmp_low_bb->getInstList().push_back(op0_low);
|
cmp_low_bb->getInstList().push_back(op0_low);
|
||||||
@ -407,21 +446,30 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
|||||||
* the comparison */
|
* the comparison */
|
||||||
auto term = bb->getTerminator();
|
auto term = bb->getTerminator();
|
||||||
if (pred == CmpInst::ICMP_EQ) {
|
if (pred == CmpInst::ICMP_EQ) {
|
||||||
|
|
||||||
BranchInst::Create(cmp_low_bb, end_bb, icmp_high, bb);
|
BranchInst::Create(cmp_low_bb, end_bb, icmp_high, bb);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
/* CmpInst::ICMP_NE */
|
/* CmpInst::ICMP_NE */
|
||||||
BranchInst::Create(end_bb, cmp_low_bb, icmp_high, bb);
|
BranchInst::Create(end_bb, cmp_low_bb, icmp_high, bb);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
term->eraseFromParent();
|
term->eraseFromParent();
|
||||||
|
|
||||||
/* create the PHI and connect the edges accordingly */
|
/* create the PHI and connect the edges accordingly */
|
||||||
PHINode *PN = PHINode::Create(Int1Ty, 2, "");
|
PHINode *PN = PHINode::Create(Int1Ty, 2, "");
|
||||||
PN->addIncoming(icmp_low, cmp_low_bb);
|
PN->addIncoming(icmp_low, cmp_low_bb);
|
||||||
if (pred == CmpInst::ICMP_EQ) {
|
if (pred == CmpInst::ICMP_EQ) {
|
||||||
|
|
||||||
PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb);
|
PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
/* CmpInst::ICMP_NE */
|
/* CmpInst::ICMP_NE */
|
||||||
PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb);
|
PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* replace the old icmp with the new PHI */
|
/* replace the old icmp with the new PHI */
|
||||||
@ -429,6 +477,7 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
|||||||
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
/* CmpInst::ICMP_UGT and CmpInst::ICMP_ULT */
|
/* CmpInst::ICMP_UGT and CmpInst::ICMP_ULT */
|
||||||
/* transformations for < and > */
|
/* transformations for < and > */
|
||||||
|
|
||||||
@ -436,12 +485,20 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
|||||||
* if this is true we can go to the end if not we have to got to the
|
* 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 */
|
* bb which checks the lower half of the operands */
|
||||||
Instruction *icmp_inv_cmp, *op0_low, *op1_low, *icmp_low;
|
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) {
|
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 {
|
} 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);
|
inv_cmp_bb->getInstList().push_back(icmp_inv_cmp);
|
||||||
|
|
||||||
auto term = bb->getTerminator();
|
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);
|
BranchInst::Create(end_bb, inv_cmp_bb, icmp_high, bb);
|
||||||
|
|
||||||
/* create a bb which handles the cmp of the lower halves */
|
/* 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);
|
op0_low = new TruncInst(op0, NewIntType);
|
||||||
cmp_low_bb->getInstList().push_back(op0_low);
|
cmp_low_bb->getInstList().push_back(op0_low);
|
||||||
op1_low = new TruncInst(op1, NewIntType);
|
op1_low = new TruncInst(op1, NewIntType);
|
||||||
@ -468,20 +526,22 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
|
|||||||
|
|
||||||
BasicBlock::iterator ii(IcmpInst);
|
BasicBlock::iterator ii(IcmpInst);
|
||||||
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SplitComparesTransform::runOnModule(Module &M) {
|
bool SplitComparesTransform::runOnModule(Module &M) {
|
||||||
|
|
||||||
int bitw = 64;
|
int bitw = 64;
|
||||||
|
|
||||||
char* bitw_env = getenv("LAF_SPLIT_COMPARES_BITW");
|
char *bitw_env = getenv("LAF_SPLIT_COMPARES_BITW");
|
||||||
if (!bitw_env)
|
if (!bitw_env) bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW");
|
||||||
bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW");
|
if (bitw_env) { bitw = atoi(bitw_env); }
|
||||||
if (bitw_env) {
|
|
||||||
bitw = atoi(bitw_env);
|
|
||||||
}
|
|
||||||
|
|
||||||
simplifyCompares(M);
|
simplifyCompares(M);
|
||||||
|
|
||||||
@ -491,18 +551,17 @@ bool SplitComparesTransform::runOnModule(Module &M) {
|
|||||||
errs() << "Split-compare-pass by laf.intel@gmail.com\n";
|
errs() << "Split-compare-pass by laf.intel@gmail.com\n";
|
||||||
|
|
||||||
switch (bitw) {
|
switch (bitw) {
|
||||||
|
|
||||||
case 64:
|
case 64:
|
||||||
errs() << "Running split-compare-pass " << 64 << "\n";
|
errs() << "Running split-compare-pass " << 64 << "\n";
|
||||||
splitCompares(M, 64);
|
splitCompares(M, 64);
|
||||||
|
|
||||||
[[clang::fallthrough]];
|
[[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */
|
||||||
/* fallthrough */
|
|
||||||
case 32:
|
case 32:
|
||||||
errs() << "Running split-compare-pass " << 32 << "\n";
|
errs() << "Running split-compare-pass " << 32 << "\n";
|
||||||
splitCompares(M, 32);
|
splitCompares(M, 32);
|
||||||
|
|
||||||
[[clang::fallthrough]];
|
[[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */
|
||||||
/* fallthrough */
|
|
||||||
case 16:
|
case 16:
|
||||||
errs() << "Running split-compare-pass " << 16 << "\n";
|
errs() << "Running split-compare-pass " << 16 << "\n";
|
||||||
splitCompares(M, 16);
|
splitCompares(M, 16);
|
||||||
@ -512,15 +571,19 @@ bool SplitComparesTransform::runOnModule(Module &M) {
|
|||||||
errs() << "NOT Running split-compare-pass \n";
|
errs() << "NOT Running split-compare-pass \n";
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyModule(M);
|
verifyModule(M);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registerSplitComparesPass(const PassManagerBuilder &,
|
static void registerSplitComparesPass(const PassManagerBuilder &,
|
||||||
legacy::PassManagerBase &PM) {
|
legacy::PassManagerBase &PM) {
|
||||||
|
|
||||||
PM.add(new SplitComparesTransform());
|
PM.add(new SplitComparesTransform());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterStandardPasses RegisterSplitComparesPass(
|
static RegisterStandardPasses RegisterSplitComparesPass(
|
||||||
@ -528,3 +591,4 @@ static RegisterStandardPasses RegisterSplitComparesPass(
|
|||||||
|
|
||||||
static RegisterStandardPasses RegisterSplitComparesTransPass0(
|
static RegisterStandardPasses RegisterSplitComparesTransPass0(
|
||||||
PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass);
|
PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass);
|
||||||
|
|
||||||
|
@ -36,54 +36,65 @@ using namespace llvm;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class SplitSwitchesTransform : public ModulePass {
|
class SplitSwitchesTransform : public ModulePass {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static char ID;
|
static char ID;
|
||||||
SplitSwitchesTransform() : ModulePass(ID) {
|
SplitSwitchesTransform() : ModulePass(ID) {
|
||||||
}
|
|
||||||
|
|
||||||
bool runOnModule(Module &M) override;
|
}
|
||||||
|
|
||||||
|
bool runOnModule(Module &M) override;
|
||||||
|
|
||||||
#if LLVM_VERSION_MAJOR >= 4
|
#if LLVM_VERSION_MAJOR >= 4
|
||||||
StringRef getPassName() const override {
|
StringRef getPassName() const override {
|
||||||
|
|
||||||
#else
|
#else
|
||||||
const char * getPassName() const override {
|
const char *getPassName() const override {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
return "splits switch constructs";
|
return "splits switch constructs";
|
||||||
}
|
|
||||||
struct CaseExpr {
|
|
||||||
ConstantInt* Val;
|
|
||||||
BasicBlock* BB;
|
|
||||||
|
|
||||||
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;
|
char SplitSwitchesTransform::ID = 0;
|
||||||
|
|
||||||
|
|
||||||
/* switchConvert - Transform simple list of Cases into list of CaseRange's */
|
/* switchConvert - Transform simple list of Cases into list of CaseRange's */
|
||||||
BasicBlock* SplitSwitchesTransform::switchConvert(CaseVector Cases, std::vector<bool> bytesChecked,
|
BasicBlock *SplitSwitchesTransform::switchConvert(
|
||||||
BasicBlock* OrigBlock, BasicBlock* NewDefault,
|
CaseVector Cases, std::vector<bool> bytesChecked, BasicBlock *OrigBlock,
|
||||||
Value* Val, unsigned level) {
|
BasicBlock *NewDefault, Value *Val, unsigned level) {
|
||||||
|
|
||||||
unsigned ValTypeBitWidth = Cases[0].Val->getBitWidth();
|
unsigned ValTypeBitWidth = Cases[0].Val->getBitWidth();
|
||||||
IntegerType *ValType = IntegerType::get(OrigBlock->getContext(), ValTypeBitWidth);
|
IntegerType *ValType =
|
||||||
IntegerType *ByteType = IntegerType::get(OrigBlock->getContext(), 8);
|
IntegerType::get(OrigBlock->getContext(), ValTypeBitWidth);
|
||||||
unsigned BytesInValue = bytesChecked.size();
|
IntegerType * ByteType = IntegerType::get(OrigBlock->getContext(), 8);
|
||||||
|
unsigned BytesInValue = bytesChecked.size();
|
||||||
std::vector<uint8_t> setSizes;
|
std::vector<uint8_t> setSizes;
|
||||||
std::vector<std::set<uint8_t>> byteSets(BytesInValue, std::set<uint8_t>());
|
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
|
/* 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 */
|
* 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++) {
|
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);
|
byteSets[i].insert(byte);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* find the index of the first byte position that was not yet checked. then
|
/* find the index of the first byte position that was not yet checked. then
|
||||||
* save the number of possible values at that byte position */
|
* save the number of possible values at that byte position */
|
||||||
unsigned smallestIndex = 0;
|
unsigned smallestIndex = 0;
|
||||||
unsigned smallestSize = 257;
|
unsigned smallestSize = 257;
|
||||||
for(unsigned i = 0; i < byteSets.size(); i++) {
|
for (unsigned i = 0; i < byteSets.size(); i++) {
|
||||||
if (bytesChecked[i])
|
|
||||||
continue;
|
if (bytesChecked[i]) continue;
|
||||||
if (byteSets[i].size() < smallestSize) {
|
if (byteSets[i].size() < smallestSize) {
|
||||||
|
|
||||||
smallestIndex = i;
|
smallestIndex = i;
|
||||||
smallestSize = byteSets[i].size();
|
smallestSize = byteSets[i].size();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(bytesChecked[smallestIndex] == false);
|
assert(bytesChecked[smallestIndex] == false);
|
||||||
|
|
||||||
/* there are only smallestSize different bytes at index smallestIndex */
|
/* there are only smallestSize different bytes at index smallestIndex */
|
||||||
|
|
||||||
Instruction *Shift, *Trunc;
|
Instruction *Shift, *Trunc;
|
||||||
Function* F = OrigBlock->getParent();
|
Function * F = OrigBlock->getParent();
|
||||||
BasicBlock* NewNode = BasicBlock::Create(Val->getContext(), "NodeBlock", F);
|
BasicBlock * NewNode = BasicBlock::Create(Val->getContext(), "NodeBlock", F);
|
||||||
Shift = BinaryOperator::Create(Instruction::LShr, Val, ConstantInt::get(ValType, smallestIndex * 8));
|
Shift = BinaryOperator::Create(Instruction::LShr, Val,
|
||||||
|
ConstantInt::get(ValType, smallestIndex * 8));
|
||||||
NewNode->getInstList().push_back(Shift);
|
NewNode->getInstList().push_back(Shift);
|
||||||
|
|
||||||
if (ValTypeBitWidth > 8) {
|
if (ValTypeBitWidth > 8) {
|
||||||
|
|
||||||
Trunc = new TruncInst(Shift, ByteType);
|
Trunc = new TruncInst(Shift, ByteType);
|
||||||
NewNode->getInstList().push_back(Trunc);
|
NewNode->getInstList().push_back(Trunc);
|
||||||
}
|
|
||||||
else {
|
} else {
|
||||||
|
|
||||||
/* not necessary to trunc */
|
/* not necessary to trunc */
|
||||||
Trunc = Shift;
|
Trunc = Shift;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is a trivial case, we can directly check for the byte,
|
/* 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
|
* mark the byte as checked. if this was the last byte to check
|
||||||
* we can finally execute the block belonging to this case */
|
* we can finally execute the block belonging to this case */
|
||||||
|
|
||||||
|
|
||||||
if (smallestSize == 1) {
|
if (smallestSize == 1) {
|
||||||
|
|
||||||
uint8_t byte = *(byteSets[smallestIndex].begin());
|
uint8_t byte = *(byteSets[smallestIndex].begin());
|
||||||
|
|
||||||
/* insert instructions to check whether the value we are switching on is equal to byte */
|
/* insert instructions to check whether the value we are switching on is
|
||||||
ICmpInst* Comp = new ICmpInst(ICmpInst::ICMP_EQ, Trunc, ConstantInt::get(ByteType, byte), "byteMatch");
|
* equal to byte */
|
||||||
|
ICmpInst *Comp =
|
||||||
|
new ICmpInst(ICmpInst::ICMP_EQ, Trunc, ConstantInt::get(ByteType, byte),
|
||||||
|
"byteMatch");
|
||||||
NewNode->getInstList().push_back(Comp);
|
NewNode->getInstList().push_back(Comp);
|
||||||
|
|
||||||
bytesChecked[smallestIndex] = true;
|
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);
|
assert(Cases.size() == 1);
|
||||||
BranchInst::Create(Cases[0].BB, NewDefault, Comp, NewNode);
|
BranchInst::Create(Cases[0].BB, NewDefault, Comp, NewNode);
|
||||||
|
|
||||||
/* we have to update the phi nodes! */
|
/* we have to update the phi nodes! */
|
||||||
for (BasicBlock::iterator I = Cases[0].BB->begin(); I != Cases[0].BB->end(); ++I) {
|
for (BasicBlock::iterator I = Cases[0].BB->begin();
|
||||||
if (!isa<PHINode>(&*I)) {
|
I != Cases[0].BB->end(); ++I) {
|
||||||
continue;
|
|
||||||
}
|
if (!isa<PHINode>(&*I)) { continue; }
|
||||||
PHINode *PN = cast<PHINode>(I);
|
PHINode *PN = cast<PHINode>(I);
|
||||||
|
|
||||||
/* Only update the first occurrence. */
|
/* Only update the first occurrence. */
|
||||||
unsigned Idx = 0, E = PN->getNumIncomingValues();
|
unsigned Idx = 0, E = PN->getNumIncomingValues();
|
||||||
for (; Idx != E; ++Idx) {
|
for (; Idx != E; ++Idx) {
|
||||||
|
|
||||||
if (PN->getIncomingBlock(Idx) == OrigBlock) {
|
if (PN->getIncomingBlock(Idx) == OrigBlock) {
|
||||||
|
|
||||||
PN->setIncomingBlock(Idx, NewNode);
|
PN->setIncomingBlock(Idx, NewNode);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
} else {
|
||||||
BasicBlock* BB = switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, level + 1);
|
|
||||||
|
BasicBlock *BB = switchConvert(Cases, bytesChecked, OrigBlock, NewDefault,
|
||||||
|
Val, level + 1);
|
||||||
BranchInst::Create(BB, NewDefault, Comp, NewNode);
|
BranchInst::Create(BB, NewDefault, Comp, NewNode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* there is no byte which we can directly check on, split the tree */
|
/* there is no byte which we can directly check on, split the tree */
|
||||||
else {
|
else {
|
||||||
|
|
||||||
std::vector<uint8_t> byteVector;
|
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());
|
std::sort(byteVector.begin(), byteVector.end());
|
||||||
uint8_t pivot = byteVector[byteVector.size() / 2];
|
uint8_t pivot = byteVector[byteVector.size() / 2];
|
||||||
|
|
||||||
/* we already chose to divide the cases based on the value of byte at index smallestIndex
|
/* we already chose to divide the cases based on the value of byte at index
|
||||||
* the pivot value determines the threshold for the decicion; if a case value
|
* smallestIndex the pivot value determines the threshold for the decicion;
|
||||||
* is smaller at this byte index move it to the LHS vector, otherwise to the RHS vector */
|
* if a case value
|
||||||
|
* is smaller at this byte index move it to the LHS vector, otherwise to the
|
||||||
|
* RHS vector */
|
||||||
|
|
||||||
CaseVector LHSCases, RHSCases;
|
CaseVector LHSCases, RHSCases;
|
||||||
|
|
||||||
for (CaseExpr& Case: Cases) {
|
for (CaseExpr &Case : Cases) {
|
||||||
uint8_t byte = (Case.Val->getZExtValue() >> (smallestIndex*8)) & 0xFF;
|
|
||||||
|
uint8_t byte = (Case.Val->getZExtValue() >> (smallestIndex * 8)) & 0xFF;
|
||||||
|
|
||||||
if (byte < pivot) {
|
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 */
|
LHSCases.push_back(Case);
|
||||||
ICmpInst* Comp = new ICmpInst(ICmpInst::ICMP_ULT, Trunc, ConstantInt::get(ByteType, pivot), "byteMatch");
|
|
||||||
|
} 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);
|
NewNode->getInstList().push_back(Comp);
|
||||||
BranchInst::Create(LBB, RBB, Comp, NewNode);
|
BranchInst::Create(LBB, RBB, Comp, NewNode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewNode;
|
return NewNode;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
||||||
|
|
||||||
std::vector<SwitchInst*> switches;
|
std::vector<SwitchInst *> switches;
|
||||||
|
|
||||||
/* iterate over all functions, bbs and instruction and add
|
/* iterate over all functions, bbs and instruction and add
|
||||||
* all switches to switches vector for later processing */
|
* all switches to switches vector for later processing */
|
||||||
for (auto &F : M) {
|
for (auto &F : M) {
|
||||||
|
|
||||||
for (auto &BB : F) {
|
for (auto &BB : F) {
|
||||||
SwitchInst* switchInst = nullptr;
|
|
||||||
|
SwitchInst *switchInst = nullptr;
|
||||||
|
|
||||||
if ((switchInst = dyn_cast<SwitchInst>(BB.getTerminator()))) {
|
if ((switchInst = dyn_cast<SwitchInst>(BB.getTerminator()))) {
|
||||||
if (switchInst->getNumCases() < 1)
|
|
||||||
continue;
|
if (switchInst->getNumCases() < 1) continue;
|
||||||
switches.push_back(switchInst);
|
switches.push_back(switchInst);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!switches.size())
|
if (!switches.size()) return false;
|
||||||
return false;
|
errs() << "Rewriting " << switches.size() << " switch statements "
|
||||||
errs() << "Rewriting " << switches.size() << " switch statements " << "\n";
|
<< "\n";
|
||||||
|
|
||||||
for (auto &SI: switches) {
|
for (auto &SI : switches) {
|
||||||
|
|
||||||
BasicBlock *CurBlock = SI->getParent();
|
BasicBlock *CurBlock = SI->getParent();
|
||||||
BasicBlock *OrigBlock = CurBlock;
|
BasicBlock *OrigBlock = CurBlock;
|
||||||
Function *F = CurBlock->getParent();
|
Function * F = CurBlock->getParent();
|
||||||
/* this is the value we are switching on */
|
/* this is the value we are switching on */
|
||||||
Value *Val = SI->getCondition();
|
Value * Val = SI->getCondition();
|
||||||
BasicBlock* Default = SI->getDefaultDest();
|
BasicBlock *Default = SI->getDefaultDest();
|
||||||
unsigned bitw = Val->getType()->getIntegerBitWidth();
|
unsigned bitw = Val->getType()->getIntegerBitWidth();
|
||||||
|
|
||||||
errs() << "switch: " << SI->getNumCases() << " cases " << bitw << " bit\n";
|
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 (!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;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a new, empty default block so that the new hierarchy of
|
/* 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);
|
NewDefault->insertInto(F, Default);
|
||||||
BranchInst::Create(Default, NewDefault);
|
BranchInst::Create(Default, NewDefault);
|
||||||
|
|
||||||
|
|
||||||
/* Prepare cases vector. */
|
/* Prepare cases vector. */
|
||||||
CaseVector Cases;
|
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
|
#if LLVM_VERSION_MAJOR < 5
|
||||||
Cases.push_back(CaseExpr(i.getCaseValue(), i.getCaseSuccessor()));
|
Cases.push_back(CaseExpr(i.getCaseValue(), i.getCaseSuccessor()));
|
||||||
#else
|
#else
|
||||||
@ -269,8 +328,10 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
|||||||
#endif
|
#endif
|
||||||
/* bugfix thanks to pbst
|
/* bugfix thanks to pbst
|
||||||
* round up bytesChecked (in case getBitWidth() % 8 != 0) */
|
* round up bytesChecked (in case getBitWidth() % 8 != 0) */
|
||||||
std::vector<bool> bytesChecked((7 + Cases[0].Val->getBitWidth()) / 8, false);
|
std::vector<bool> bytesChecked((7 + Cases[0].Val->getBitWidth()) / 8,
|
||||||
BasicBlock* SwitchBlock = switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, 0);
|
false);
|
||||||
|
BasicBlock * SwitchBlock =
|
||||||
|
switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, 0);
|
||||||
|
|
||||||
/* Branch to our shiny new if-then stuff... */
|
/* Branch to our shiny new if-then stuff... */
|
||||||
BranchInst::Create(SwitchBlock, OrigBlock);
|
BranchInst::Create(SwitchBlock, OrigBlock);
|
||||||
@ -278,27 +339,32 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
|||||||
/* We are now done with the switch instruction, delete it. */
|
/* We are now done with the switch instruction, delete it. */
|
||||||
CurBlock->getInstList().erase(SI);
|
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! */
|
if (!isa<PHINode>(&*I)) { continue; }
|
||||||
for (BasicBlock::iterator I = Default->begin(); I != Default->end(); ++I) {
|
PHINode *PN = cast<PHINode>(I);
|
||||||
if (!isa<PHINode>(&*I)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
PHINode *PN = cast<PHINode>(I);
|
|
||||||
|
|
||||||
/* Only update the first occurrence. */
|
/* Only update the first occurrence. */
|
||||||
unsigned Idx = 0, E = PN->getNumIncomingValues();
|
unsigned Idx = 0, E = PN->getNumIncomingValues();
|
||||||
for (; Idx != E; ++Idx) {
|
for (; Idx != E; ++Idx) {
|
||||||
if (PN->getIncomingBlock(Idx) == OrigBlock) {
|
|
||||||
PN->setIncomingBlock(Idx, NewDefault);
|
if (PN->getIncomingBlock(Idx) == OrigBlock) {
|
||||||
break;
|
|
||||||
}
|
PN->setIncomingBlock(Idx, NewDefault);
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyModule(M);
|
||||||
|
return true;
|
||||||
|
|
||||||
verifyModule(M);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SplitSwitchesTransform::runOnModule(Module &M) {
|
bool SplitSwitchesTransform::runOnModule(Module &M) {
|
||||||
@ -309,10 +375,11 @@ bool SplitSwitchesTransform::runOnModule(Module &M) {
|
|||||||
verifyModule(M);
|
verifyModule(M);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registerSplitSwitchesTransPass(const PassManagerBuilder &,
|
static void registerSplitSwitchesTransPass(const PassManagerBuilder &,
|
||||||
legacy::PassManagerBase &PM) {
|
legacy::PassManagerBase &PM) {
|
||||||
|
|
||||||
auto p = new SplitSwitchesTransform();
|
auto p = new SplitSwitchesTransform();
|
||||||
PM.add(p);
|
PM.add(p);
|
||||||
@ -324,3 +391,4 @@ static RegisterStandardPasses RegisterSplitSwitchesTransPass(
|
|||||||
|
|
||||||
static RegisterStandardPasses RegisterSplitSwitchesTransPass0(
|
static RegisterStandardPasses RegisterSplitSwitchesTransPass0(
|
||||||
PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass);
|
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.)
|
(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
|
The code in this directory allows you to build a standalone feature that
|
||||||
leverages the QEMU "user emulation" mode and allows callers to obtain
|
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
|
The usual performance cost is 2-5x, which is considerably better than
|
||||||
seen so far in experiments with tools such as DynamoRIO and PIN.
|
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
|
The feature is implemented with a patch to QEMU 3.1.0. The simplest way
|
||||||
simplest way to build it is to run ./build_qemu_support.sh. The script will
|
to build it is to run ./build_qemu_support.sh. The script will download,
|
||||||
download, configure, and compile the QEMU binary for you.
|
configure, and compile the QEMU binary for you.
|
||||||
|
|
||||||
QEMU is a big project, so this will take a while, and you may have to
|
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
|
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
|
users, you need to build it before issuing 'make install' in the parent
|
||||||
directory.
|
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 feature is supported only on Linux. Supporting BSD may amount to porting
|
||||||
the changes made to linux-user/elfload.c and applying them to
|
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
|
Setting AFL_INST_LIBS=1 can be used to circumvent the .text detection logic
|
||||||
and instrument every basic block encountered.
|
and instrument every basic block encountered.
|
||||||
|
|
||||||
4) Benchmarking
|
## 5) Benchmarking
|
||||||
---------------
|
|
||||||
|
|
||||||
If you want to compare the performance of the QEMU instrumentation with that of
|
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
|
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
|
fairly meaningless if the optimization levels or instrumentation scopes don't
|
||||||
match.
|
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
|
If you need to fix up checksums or do other cleanup on mutated test cases, see
|
||||||
experimental/post_library/ for a viable solution.
|
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.
|
Beyond that, this is an early-stage mechanism, so fields reports are welcome.
|
||||||
You can send them to <afl-users@googlegroups.com>.
|
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
|
Statically rewriting binaries just once, instead of attempting to translate
|
||||||
them at run time, can be a faster alternative. That said, static rewriting is
|
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
|
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.
|
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
|
The best implementation is this one:
|
||||||
contributed by Aleksandar Nikolich:
|
|
||||||
|
|
||||||
https://github.com/vanhauser-thc/afl-dyninst
|
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
|
The issue however is Dyninst which is not rewriting the binaries so that
|
||||||
binaries. That said, if we can get it to be comparably reliable to QEMU, we may
|
they run stable. a lot of crashes happen, especially in C++ programs that
|
||||||
decide to switch to this mode, but I had no time to play with it yet.
|
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
|
# american fuzzy lop - QEMU build script
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
#
|
#
|
||||||
# Written by Andrew Griffiths <agriffiths@google.com> and
|
# Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||||
# Michal Zalewski <lcamtuf@google.com>
|
# 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 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");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -105,7 +112,8 @@ if [ "$CKSUM" = "$QEMU_SHA384" ]; then
|
|||||||
|
|
||||||
else
|
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
|
exit 1
|
||||||
|
|
||||||
fi
|
fi
|
||||||
@ -193,6 +201,8 @@ if [ "$ORIG_CPU_TARGET" = "" ]; then
|
|||||||
echo "[+] Instrumentation tests passed. "
|
echo "[+] Instrumentation tests passed. "
|
||||||
echo "[+] All set, you can now use the -Q mode in afl-fuzz!"
|
echo "[+] All set, you can now use the -Q mode in afl-fuzz!"
|
||||||
|
|
||||||
|
cd qemu_mode || exit 1
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
echo "[!] Note: can't test instrumentation when CPU_TARGET set."
|
echo "[!] Note: can't test instrumentation when CPU_TARGET set."
|
||||||
@ -200,4 +210,9 @@ else
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "[+] Building libcompcov ..."
|
||||||
|
make -C libcompcov
|
||||||
|
echo "[+] libcompcov ready"
|
||||||
|
echo "[+] All done for qemu_mode, enjoy!"
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -18,25 +18,25 @@ HELPER_PATH = $(PREFIX)/lib/afl
|
|||||||
|
|
||||||
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
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
|
CFLAGS += -Wall -Wno-unused-result -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
|
||||||
LDFLAGS += -ldl
|
LDFLAGS += -ldl
|
||||||
|
|
||||||
all: libcompcov.so compcovtest
|
all: libcompcov.so compcovtest
|
||||||
|
|
||||||
libcompcov.so: libcompcov.so.c ../../config.h
|
libcompcov.so: libcompcov.so.c ../../config.h
|
||||||
$(CC) $(CFLAGS) -shared -fPIC $< -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) -shared -fPIC $< -o ../../$@ $(LDFLAGS)
|
||||||
|
|
||||||
.NOTPARALLEL: clean
|
.NOTPARALLEL: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *.so *~ a.out core core.[1-9][0-9]*
|
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
|
compcovtest: compcovtest.cc
|
||||||
$(CXX) $< -o $@
|
$(CXX) $< -o $@
|
||||||
|
|
||||||
install: all
|
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)
|
install -m 644 README.compcov $${DESTDIR}$(HELPER_PATH)
|
||||||
|
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
================================================================
|
# strcmp() / memcmp() CompareCoverage library for afl++ QEMU
|
||||||
strcmp() / memcmp() CompareCoverage library for AFLplusplus-QEMU
|
|
||||||
================================================================
|
|
||||||
|
|
||||||
Written by Andrea Fioraldi <andreafioraldi@gmail.com>
|
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.
|
and related functions to log the CompareCoverage of these libcalls.
|
||||||
|
|
||||||
Use this with caution. While this can speedup a lot the bypass of hard
|
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.
|
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
|
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.
|
To use this library make sure to preload it with AFL_PRELOAD.
|
||||||
|
|
||||||
|
```
|
||||||
export AFL_PRELOAD=/path/to/libcompcov.so
|
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>
|
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
|
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
|
Linux specific. However this is not a strict dependency, other UNIX operating
|
@ -17,7 +17,8 @@
|
|||||||
// limitations under the License.
|
// 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 <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@ -25,39 +26,40 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
char buffer[44] = { /* zero padding */ };
|
|
||||||
|
char buffer[44] = {/* zero padding */};
|
||||||
fread(buffer, 1, sizeof(buffer) - 1, stdin);
|
fread(buffer, 1, sizeof(buffer) - 1, stdin);
|
||||||
|
|
||||||
if (memcmp(&buffer[0], "The quick brown fox ", 20) != 0 ||
|
if (memcmp(&buffer[0], "The quick brown fox ", 20) != 0 ||
|
||||||
strncmp(&buffer[20], "jumps over ", 11) != 0 ||
|
strncmp(&buffer[20], "jumps over ", 11) != 0 ||
|
||||||
strcmp(&buffer[31], "the lazy dog") != 0) {
|
strcmp(&buffer[31], "the lazy dog") != 0) {
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t x = 0;
|
uint64_t x = 0;
|
||||||
fread(&x, sizeof(x), 1, stdin);
|
fread(&x, sizeof(x), 1, stdin);
|
||||||
if (x != 0xCAFEBABECAFEBABE) {
|
if (x != 0xCAFEBABECAFEBABE) { return 2; }
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t y = 0;
|
uint32_t y = 0;
|
||||||
fread(&y, sizeof(y), 1, stdin);
|
fread(&y, sizeof(y), 1, stdin);
|
||||||
if (y != 0xDEADC0DE) {
|
if (y != 0xDEADC0DE) { return 3; }
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t z = 0;
|
uint16_t z = 0;
|
||||||
fread(&z, sizeof(z), 1, stdin);
|
fread(&z, sizeof(z), 1, stdin);
|
||||||
|
|
||||||
switch (z) {
|
switch (z) {
|
||||||
case 0xBEEF:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
case 0xBEEF: break;
|
||||||
return 4;
|
|
||||||
|
default: return 4;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Puzzle solved, congrats!\n");
|
printf("Puzzle solved, congrats!\n");
|
||||||
abort();
|
abort();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
Written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com>
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -27,23 +27,24 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/shm.h>
|
#include <sys/shm.h>
|
||||||
|
|
||||||
#include "../../types.h"
|
#include "types.h"
|
||||||
#include "../../config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "pmparser.h"
|
#include "pmparser.h"
|
||||||
|
|
||||||
#ifndef __linux__
|
#ifndef __linux__
|
||||||
# error "Sorry, this library is Linux-specific for now!"
|
#error "Sorry, this library is Linux-specific for now!"
|
||||||
#endif /* !__linux__ */
|
#endif /* !__linux__ */
|
||||||
|
|
||||||
/* Change this value to tune the compare coverage */
|
/* Change this value to tune the compare coverage */
|
||||||
|
|
||||||
#define MAX_CMP_LENGTH 32
|
#define MAX_CMP_LENGTH 32
|
||||||
|
|
||||||
static void *__compcov_code_start,
|
static void *__compcov_code_start, *__compcov_code_end;
|
||||||
*__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_strcmp)(const char*, const char*);
|
||||||
static int (*__libc_strncmp)(const char*, const char*, size_t);
|
static int (*__libc_strncmp)(const char*, const char*, size_t);
|
||||||
@ -53,13 +54,33 @@ static int (*__libc_memcmp)(const void*, const void*, size_t);
|
|||||||
|
|
||||||
static int debug_fd = -1;
|
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
|
// from https://github.com/googleprojectzero/CompareCoverage
|
||||||
|
|
||||||
size_t len = 0;
|
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;
|
return len;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Identify the binary boundaries in the memory mapping */
|
/* Identify the binary boundaries in the memory mapping */
|
||||||
@ -72,8 +93,15 @@ static void __compcov_load(void) {
|
|||||||
__libc_strncasecmp = dlsym(RTLD_NEXT, "strncasecmp");
|
__libc_strncasecmp = dlsym(RTLD_NEXT, "strncasecmp");
|
||||||
__libc_memcmp = dlsym(RTLD_NEXT, "memcmp");
|
__libc_memcmp = dlsym(RTLD_NEXT, "memcmp");
|
||||||
|
|
||||||
char *id_str = getenv(SHM_ENV_VAR);
|
if (getenv("AFL_QEMU_COMPCOV")) { __compcov_level = 1; }
|
||||||
int shm_id;
|
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) {
|
if (id_str) {
|
||||||
|
|
||||||
@ -81,9 +109,11 @@ static void __compcov_load(void) {
|
|||||||
__compcov_afl_map = shmat(shm_id, NULL, 0);
|
__compcov_afl_map = shmat(shm_id, NULL, 0);
|
||||||
|
|
||||||
if (__compcov_afl_map == (void*)-1) exit(1);
|
if (__compcov_afl_map == (void*)-1) exit(1);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
__compcov_afl_map = calloc(1, MAP_SIZE);
|
__compcov_afl_map = calloc(1, MAP_SIZE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getenv("AFL_INST_LIBS")) {
|
if (getenv("AFL_INST_LIBS")) {
|
||||||
@ -91,12 +121,13 @@ static void __compcov_load(void) {
|
|||||||
__compcov_code_start = (void*)0;
|
__compcov_code_start = (void*)0;
|
||||||
__compcov_code_end = (void*)-1;
|
__compcov_code_end = (void*)-1;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char* bin_name = getenv("AFL_COMPCOV_BINNAME");
|
char* bin_name = getenv("AFL_COMPCOV_BINNAME");
|
||||||
|
|
||||||
procmaps_iterator* maps = pmparser_parse(-1);
|
procmaps_iterator* maps = pmparser_parse(-1);
|
||||||
procmaps_struct* maps_tmp = NULL;
|
procmaps_struct* maps_tmp = NULL;
|
||||||
|
|
||||||
while ((maps_tmp = pmparser_next(maps)) != NULL) {
|
while ((maps_tmp = pmparser_next(maps)) != NULL) {
|
||||||
|
|
||||||
@ -104,32 +135,46 @@ static void __compcov_load(void) {
|
|||||||
if (!bin_name || strstr(maps_tmp->pathname, bin_name) != NULL) {
|
if (!bin_name || strstr(maps_tmp->pathname, bin_name) != NULL) {
|
||||||
|
|
||||||
if (maps_tmp->is_x) {
|
if (maps_tmp->is_x) {
|
||||||
if (!__compcov_code_start)
|
|
||||||
__compcov_code_start = maps_tmp->addr_start;
|
if (!__compcov_code_start) __compcov_code_start = maps_tmp->addr_start;
|
||||||
if (!__compcov_code_end)
|
if (!__compcov_code_end) __compcov_code_end = maps_tmp->addr_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);
|
pmparser_free(maps);
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static void __compcov_trace(u64 cur_loc, const u8* v0, const u8* v1, size_t n) {
|
static void __compcov_trace(u64 cur_loc, const u8* v0, const u8* v1, size_t n) {
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (debug_fd != 1) {
|
if (debug_fd != 1) {
|
||||||
|
|
||||||
char debugbuf[4096];
|
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));
|
write(debug_fd, debugbuf, strlen(debugbuf));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < n && v0[i] == v1[i]; ++i) {
|
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. */
|
/* 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) {
|
static u8 __compcov_is_in_bound(const void* ptr) {
|
||||||
|
|
||||||
return ptr >= __compcov_code_start && ptr < __compcov_code_end;
|
return ptr >= __compcov_code_start && ptr < __compcov_code_end;
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* Replacements for strcmp(), memcmp(), and so on. Note that these will be used
|
/* 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. */
|
only if the target is compiled with -fno-builtins and linked dynamically. */
|
||||||
@ -149,23 +194,27 @@ int strcmp(const char* str1, const char* str2) {
|
|||||||
|
|
||||||
void* retaddr = __builtin_return_address(0);
|
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))) {
|
||||||
|
|
||||||
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) {
|
if (n <= MAX_CMP_LENGTH) {
|
||||||
|
|
||||||
u64 cur_loc = (u64)retaddr;
|
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;
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
__compcov_trace(cur_loc, str1, str2, n);
|
__compcov_trace(cur_loc, str1, str2, n);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return __libc_strcmp(str1, str2);
|
return __libc_strcmp(str1, str2);
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#undef strncmp
|
#undef strncmp
|
||||||
|
|
||||||
@ -173,24 +222,28 @@ int strncmp(const char* str1, const char* str2, size_t len) {
|
|||||||
|
|
||||||
void* retaddr = __builtin_return_address(0);
|
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))) {
|
||||||
|
|
||||||
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
|
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH + 1);
|
||||||
n = MIN(n, len);
|
n = MIN(n, len);
|
||||||
|
|
||||||
if (n <= MAX_CMP_LENGTH) {
|
if (n <= MAX_CMP_LENGTH) {
|
||||||
|
|
||||||
u64 cur_loc = (u64)retaddr;
|
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;
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
__compcov_trace(cur_loc, str1, str2, n);
|
__compcov_trace(cur_loc, str1, str2, n);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return __libc_strncmp(str1, str2, len);
|
return __libc_strncmp(str1, str2, len);
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#undef strcasecmp
|
#undef strcasecmp
|
||||||
|
|
||||||
@ -198,24 +251,29 @@ int strcasecmp(const char* str1, const char* str2) {
|
|||||||
|
|
||||||
void* retaddr = __builtin_return_address(0);
|
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 */
|
/* 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) {
|
if (n <= MAX_CMP_LENGTH) {
|
||||||
|
|
||||||
u64 cur_loc = (u64)retaddr;
|
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;
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
__compcov_trace(cur_loc, str1, str2, n);
|
__compcov_trace(cur_loc, str1, str2, n);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return __libc_strcasecmp(str1, str2);
|
return __libc_strcasecmp(str1, str2);
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#undef strncasecmp
|
#undef strncasecmp
|
||||||
|
|
||||||
@ -223,25 +281,30 @@ int strncasecmp(const char* str1, const char* str2, size_t len) {
|
|||||||
|
|
||||||
void* retaddr = __builtin_return_address(0);
|
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 */
|
/* 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);
|
n = MIN(n, len);
|
||||||
|
|
||||||
if (n <= MAX_CMP_LENGTH) {
|
if (n <= MAX_CMP_LENGTH) {
|
||||||
|
|
||||||
u64 cur_loc = (u64)retaddr;
|
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;
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
__compcov_trace(cur_loc, str1, str2, n);
|
__compcov_trace(cur_loc, str1, str2, n);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return __libc_strncasecmp(str1, str2, len);
|
return __libc_strncasecmp(str1, str2, len);
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#undef memcmp
|
#undef memcmp
|
||||||
|
|
||||||
@ -249,21 +312,26 @@ int memcmp(const void* mem1, const void* mem2, size_t len) {
|
|||||||
|
|
||||||
void* retaddr = __builtin_return_address(0);
|
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;
|
size_t n = len;
|
||||||
|
|
||||||
if (n <= MAX_CMP_LENGTH) {
|
if (n <= MAX_CMP_LENGTH) {
|
||||||
|
|
||||||
u64 cur_loc = (u64)retaddr;
|
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;
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
__compcov_trace(cur_loc, mem1, mem2, n);
|
__compcov_trace(cur_loc, mem1, mem2, n);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return __libc_memcmp(mem1, mem2, len);
|
return __libc_memcmp(mem1, mem2, len);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init code to open init the library. */
|
/* 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) {
|
__attribute__((constructor)) void __compcov_init(void) {
|
||||||
|
|
||||||
if (getenv("AFL_QEMU_COMPCOV_DEBUG") != NULL)
|
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();
|
__compcov_load();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,43 +24,49 @@ implied warranty.
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
|
|
||||||
//maximum line length in a procmaps file
|
// maximum line length in a procmaps file
|
||||||
#define PROCMAPS_LINE_MAX_LENGTH (PATH_MAX + 100)
|
#define PROCMAPS_LINE_MAX_LENGTH (PATH_MAX + 100)
|
||||||
/**
|
/**
|
||||||
* procmaps_struct
|
* procmaps_struct
|
||||||
* @desc hold all the information about an area in the process's VM
|
* @desc hold all the information about an area in the process's VM
|
||||||
*/
|
*/
|
||||||
typedef struct procmaps_struct{
|
typedef struct procmaps_struct {
|
||||||
void* addr_start; //< start address of the area
|
|
||||||
void* addr_end; //< end address
|
|
||||||
unsigned long length; //< size of the range
|
|
||||||
|
|
||||||
char perm[5]; //< permissions rwxp
|
void* addr_start; //< start address of the area
|
||||||
short is_r; //< rewrote of perm with short flags
|
void* addr_end; //< end address
|
||||||
short is_w;
|
unsigned long length; //< size of the range
|
||||||
short is_x;
|
|
||||||
short is_p;
|
|
||||||
|
|
||||||
long offset; //< offset
|
char perm[5]; //< permissions rwxp
|
||||||
char dev[12]; //< dev major:minor
|
short is_r; //< rewrote of perm with short flags
|
||||||
int inode; //< inode of the file that backs the area
|
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_struct;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* procmaps_iterator
|
* procmaps_iterator
|
||||||
* @desc holds iterating information
|
* @desc holds iterating information
|
||||||
*/
|
*/
|
||||||
typedef struct procmaps_iterator{
|
typedef struct procmaps_iterator {
|
||||||
procmaps_struct* head;
|
|
||||||
procmaps_struct* current;
|
procmaps_struct* head;
|
||||||
|
procmaps_struct* current;
|
||||||
|
|
||||||
} procmaps_iterator;
|
} procmaps_iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pmparser_parse
|
* 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
|
* @return an iterator over all the nodes
|
||||||
*/
|
*/
|
||||||
procmaps_iterator* pmparser_parse(int pid);
|
procmaps_iterator* pmparser_parse(int pid);
|
||||||
@ -83,198 +89,238 @@ void pmparser_free(procmaps_iterator* p_procmaps_it);
|
|||||||
* _pmparser_split_line
|
* _pmparser_split_line
|
||||||
* @description internal usage
|
* @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
|
* pmparser_print
|
||||||
* @param map the head of the list
|
* @param map the head of the list
|
||||||
* @order the order of the area to print, -1 to print everything
|
* @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
|
* gobal variables
|
||||||
*/
|
*/
|
||||||
//procmaps_struct* g_last_head=NULL;
|
// procmaps_struct* g_last_head=NULL;
|
||||||
//procmaps_struct* g_current=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));
|
||||||
procmaps_iterator* maps_it = malloc(sizeof(procmaps_iterator));
|
char maps_path[500];
|
||||||
char maps_path[500];
|
if (pid >= 0) {
|
||||||
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');
|
|
||||||
|
|
||||||
//offset
|
sprintf(maps_path, "/proc/%d/maps", pid);
|
||||||
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
|
} else {
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
|
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){
|
fprintf(stderr, "pmparser : cannot open the memory maps, %s\n",
|
||||||
if(p_procmaps_it->current == NULL)
|
strerror(errno));
|
||||||
return 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;
|
|
||||||
|
|
||||||
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){
|
// offset
|
||||||
procmaps_struct* maps_list = p_procmaps_it->head;
|
sscanf(offset, "%lx", &tmp->offset);
|
||||||
if(maps_list==NULL) return ;
|
// device
|
||||||
procmaps_struct* act=maps_list;
|
strcpy(tmp->dev, dev);
|
||||||
procmaps_struct* nxt=act->next;
|
// inode
|
||||||
while(act!=NULL){
|
tmp->inode = atoi(inode);
|
||||||
free(act);
|
// pathname
|
||||||
act=nxt;
|
strcpy(tmp->pathname, pathname);
|
||||||
if(nxt!=NULL)
|
tmp->next = NULL;
|
||||||
nxt=nxt->next;
|
// 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(
|
if (p_procmaps_it->current == NULL) return NULL;
|
||||||
char*buf,char*addr1,char*addr2,
|
procmaps_struct* p_current = p_procmaps_it->current;
|
||||||
char*perm,char* offset,char* device,char*inode,
|
p_procmaps_it->current = p_procmaps_it->current->next;
|
||||||
char* pathname){
|
return p_current;
|
||||||
//
|
/*
|
||||||
int orig=0;
|
if(g_current==NULL){
|
||||||
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
|
g_current=g_last_head;
|
||||||
while(buf[i]=='\t' || buf[i]==' ')
|
|
||||||
i++;
|
}else
|
||||||
orig=i;
|
|
||||||
while(buf[i]!='\t' && buf[i]!=' '){
|
g_current=g_current->next;
|
||||||
perm[i-orig]=buf[i];
|
|
||||||
i++;
|
return g_current;
|
||||||
}
|
*/
|
||||||
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';
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
#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
|
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||||
Michal Zalewski <lcamtuf@google.com>
|
Michal Zalewski <lcamtuf@google.com>
|
||||||
|
|
||||||
Idea & design very much by Andrew Griffiths.
|
|
||||||
|
|
||||||
TCG instrumentation and block chaining support by Andrea Biondo
|
TCG instrumentation and block chaining support by Andrea Biondo
|
||||||
<andrea.biondo965@gmail.com>
|
<andrea.biondo965@gmail.com>
|
||||||
|
|
||||||
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
QEMU 3.1.0 port, TCG thread-safety, CompareCoverage and NeverZero
|
||||||
<andreafioraldi@gmail.com>
|
counters by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||||
|
|
||||||
Copyright 2015, 2016, 2017 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -43,11 +42,16 @@
|
|||||||
_start and does the usual forkserver stuff, not very different from
|
_start and does the usual forkserver stuff, not very different from
|
||||||
regular instrumentation injected via afl-as.h. */
|
regular instrumentation injected via afl-as.h. */
|
||||||
|
|
||||||
#define AFL_QEMU_CPU_SNIPPET2 do { \
|
#define AFL_QEMU_CPU_SNIPPET2 \
|
||||||
if(itb->pc == afl_entry_point) { \
|
do { \
|
||||||
afl_setup(); \
|
\
|
||||||
afl_forkserver(cpu); \
|
if (itb->pc == afl_entry_point) { \
|
||||||
} \
|
\
|
||||||
|
afl_setup(); \
|
||||||
|
afl_forkserver(cpu); \
|
||||||
|
\
|
||||||
|
} \
|
||||||
|
\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* We use one additional file descriptor to relay "needs translation"
|
/* We use one additional file descriptor to relay "needs translation"
|
||||||
@ -57,60 +61,71 @@
|
|||||||
|
|
||||||
/* This is equivalent to afl-as.h: */
|
/* This is equivalent to afl-as.h: */
|
||||||
|
|
||||||
static unsigned char dummy[MAP_SIZE]; /* costs MAP_SIZE but saves a few instructions */
|
static unsigned char
|
||||||
unsigned char *afl_area_ptr = dummy; /* Exported for afl_gen_trace */
|
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: */
|
/* Exported variables populated by the code patched into elfload.c: */
|
||||||
|
|
||||||
abi_ulong afl_entry_point, /* ELF entry point (_start) */
|
abi_ulong afl_entry_point, /* ELF entry point (_start) */
|
||||||
afl_start_code, /* .text start pointer */
|
afl_start_code, /* .text start pointer */
|
||||||
afl_end_code; /* .text end pointer */
|
afl_end_code; /* .text end pointer */
|
||||||
|
|
||||||
u8 afl_enable_compcov;
|
u8 afl_compcov_level;
|
||||||
|
|
||||||
/* Set in the child process in forkserver mode: */
|
/* Set in the child process in forkserver mode: */
|
||||||
|
|
||||||
static int forkserver_installed = 0;
|
static int forkserver_installed = 0;
|
||||||
static unsigned char afl_fork_child;
|
static unsigned char afl_fork_child;
|
||||||
unsigned int afl_forksrv_pid;
|
unsigned int afl_forksrv_pid;
|
||||||
|
|
||||||
/* Instrumentation ratio: */
|
/* 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. */
|
/* Function declarations. */
|
||||||
|
|
||||||
static void afl_setup(void);
|
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_wait_tsl(CPUState *, int);
|
||||||
static void afl_request_tsl(target_ulong, target_ulong, uint32_t, uint32_t, TranslationBlock*, int);
|
static void afl_request_tsl(target_ulong, target_ulong, uint32_t, uint32_t,
|
||||||
|
TranslationBlock *, int);
|
||||||
|
|
||||||
/* Data structures passed around by the translate handlers: */
|
/* Data structures passed around by the translate handlers: */
|
||||||
|
|
||||||
struct afl_tb {
|
struct afl_tb {
|
||||||
|
|
||||||
target_ulong pc;
|
target_ulong pc;
|
||||||
target_ulong cs_base;
|
target_ulong cs_base;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint32_t cf_mask;
|
uint32_t cf_mask;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct afl_tsl {
|
struct afl_tsl {
|
||||||
|
|
||||||
struct afl_tb tb;
|
struct afl_tb tb;
|
||||||
char is_chain;
|
char is_chain;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct afl_chain {
|
struct afl_chain {
|
||||||
|
|
||||||
struct afl_tb last_tb;
|
struct afl_tb last_tb;
|
||||||
uint32_t cf_mask;
|
uint32_t cf_mask;
|
||||||
int tb_exit;
|
int tb_exit;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Some forward decls: */
|
/* Some forward decls: */
|
||||||
|
|
||||||
TranslationBlock *tb_htable_lookup(CPUState*, target_ulong, target_ulong, uint32_t, uint32_t);
|
TranslationBlock *tb_htable_lookup(CPUState *, target_ulong, target_ulong,
|
||||||
static inline TranslationBlock *tb_find(CPUState*, TranslationBlock*, int, uint32_t);
|
uint32_t, uint32_t);
|
||||||
static inline void tb_add_jump(TranslationBlock *tb, int n, TranslationBlock *tb_next);
|
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 *
|
* ACTUAL IMPLEMENTATION *
|
||||||
@ -120,8 +135,7 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, TranslationBlock *tb
|
|||||||
|
|
||||||
static void afl_setup(void) {
|
static void afl_setup(void) {
|
||||||
|
|
||||||
char *id_str = getenv(SHM_ENV_VAR),
|
char *id_str = getenv(SHM_ENV_VAR), *inst_r = getenv("AFL_INST_RATIO");
|
||||||
*inst_r = getenv("AFL_INST_RATIO");
|
|
||||||
|
|
||||||
int shm_id;
|
int shm_id;
|
||||||
|
|
||||||
@ -143,7 +157,7 @@ static void afl_setup(void) {
|
|||||||
shm_id = atoi(id_str);
|
shm_id = atoi(id_str);
|
||||||
afl_area_ptr = shmat(shm_id, NULL, 0);
|
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
|
/* 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. */
|
so that the parent doesn't give up on us. */
|
||||||
@ -155,13 +169,16 @@ static void afl_setup(void) {
|
|||||||
if (getenv("AFL_INST_LIBS")) {
|
if (getenv("AFL_INST_LIBS")) {
|
||||||
|
|
||||||
afl_start_code = 0;
|
afl_start_code = 0;
|
||||||
afl_end_code = (abi_ulong)-1;
|
afl_end_code = (abi_ulong)-1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getenv("AFL_QEMU_COMPCOV")) {
|
/* 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"));
|
||||||
|
|
||||||
afl_enable_compcov = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm
|
/* 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. */
|
/* Fork server logic, invoked once we hit _start. */
|
||||||
|
|
||||||
static void afl_forkserver(CPUState *cpu) {
|
static void afl_forkserver(CPUState *cpu) {
|
||||||
|
|
||||||
static unsigned char tmp[4];
|
static unsigned char tmp[4];
|
||||||
|
|
||||||
if (forkserver_installed == 1)
|
if (forkserver_installed == 1) return;
|
||||||
return;
|
|
||||||
forkserver_installed = 1;
|
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
|
/* Tell the parent that we're alive. If the parent doesn't want
|
||||||
to talk, assume that we're not running in forkserver mode. */
|
to talk, assume that we're not running in forkserver mode. */
|
||||||
@ -196,7 +211,7 @@ static void afl_forkserver(CPUState *cpu) {
|
|||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
pid_t child_pid;
|
pid_t child_pid;
|
||||||
int status, t_fd[2];
|
int status, t_fd[2];
|
||||||
|
|
||||||
/* Whoops, parent dead? */
|
/* 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
|
/* 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
|
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
|
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. */
|
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,
|
static void afl_request_tsl(target_ulong pc, target_ulong cb, uint32_t flags,
|
||||||
TranslationBlock *last_tb, int tb_exit) {
|
uint32_t cf_mask, TranslationBlock *last_tb,
|
||||||
|
int tb_exit) {
|
||||||
|
|
||||||
struct afl_tsl t;
|
struct afl_tsl t;
|
||||||
struct afl_chain c;
|
struct afl_chain c;
|
||||||
|
|
||||||
if (!afl_fork_child) return;
|
if (!afl_fork_child) return;
|
||||||
|
|
||||||
t.tb.pc = pc;
|
t.tb.pc = pc;
|
||||||
t.tb.cs_base = cb;
|
t.tb.cs_base = cb;
|
||||||
t.tb.flags = flags;
|
t.tb.flags = flags;
|
||||||
t.tb.cf_mask = cf_mask;
|
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))
|
if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (t.is_chain) {
|
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.cs_base = last_tb->cs_base;
|
||||||
c.last_tb.flags = last_tb->flags;
|
c.last_tb.flags = last_tb->flags;
|
||||||
c.cf_mask = cf_mask;
|
c.cf_mask = cf_mask;
|
||||||
c.tb_exit = tb_exit;
|
c.tb_exit = tb_exit;
|
||||||
|
|
||||||
if (write(TSL_FD, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain))
|
if (write(TSL_FD, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Check if an address is valid in the current mapping */
|
/* Check if an address is valid in the current mapping */
|
||||||
|
|
||||||
static inline int is_valid_addr(target_ulong addr) {
|
static inline int is_valid_addr(target_ulong addr) {
|
||||||
|
|
||||||
int l, flags;
|
int l, flags;
|
||||||
target_ulong page;
|
target_ulong page;
|
||||||
void * p;
|
void * p;
|
||||||
|
|
||||||
page = addr & TARGET_PAGE_MASK;
|
page = addr & TARGET_PAGE_MASK;
|
||||||
l = (page + TARGET_PAGE_SIZE) - addr;
|
l = (page + TARGET_PAGE_SIZE) - addr;
|
||||||
|
|
||||||
flags = page_get_flags(page);
|
flags = page_get_flags(page);
|
||||||
if (!(flags & PAGE_VALID) || !(flags & PAGE_READ))
|
if (!(flags & PAGE_VALID) || !(flags & PAGE_READ)) return 0;
|
||||||
return 0;
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is the other side of the same channel. Since timeouts are handled by
|
/* 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) {
|
static void afl_wait_tsl(CPUState *cpu, int fd) {
|
||||||
|
|
||||||
struct afl_tsl t;
|
struct afl_tsl t;
|
||||||
struct afl_chain c;
|
struct afl_chain c;
|
||||||
TranslationBlock *tb, *last_tb;
|
TranslationBlock *tb, *last_tb;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
@ -312,12 +328,11 @@ static void afl_wait_tsl(CPUState *cpu, int fd) {
|
|||||||
|
|
||||||
/* Broken pipe means it's time to return to the fork server routine. */
|
/* 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))
|
if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) break;
|
||||||
break;
|
|
||||||
|
|
||||||
tb = tb_htable_lookup(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, t.tb.cf_mask);
|
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
|
/* The child may request to transate a block of memory that is not
|
||||||
mapped in the parent (e.g. jitted code or dlopened code).
|
mapped in the parent (e.g. jitted code or dlopened code).
|
||||||
@ -327,15 +342,19 @@ static void afl_wait_tsl(CPUState *cpu, int fd) {
|
|||||||
if (is_valid_addr(t.tb.pc)) {
|
if (is_valid_addr(t.tb.pc)) {
|
||||||
|
|
||||||
mmap_lock();
|
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();
|
mmap_unlock();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
invalid_pc = 1;
|
invalid_pc = 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t.is_chain) {
|
if (t.is_chain) {
|
||||||
|
|
||||||
if (read(fd, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain))
|
if (read(fd, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain))
|
||||||
break;
|
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,
|
last_tb = tb_htable_lookup(cpu, c.last_tb.pc, c.last_tb.cs_base,
|
||||||
c.last_tb.flags, c.cf_mask);
|
c.last_tb.flags, c.cf_mask);
|
||||||
if (last_tb) {
|
if (last_tb) { tb_add_jump(last_tb, c.tb_exit, 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);
|
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
|
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||||
Michal Zalewski <lcamtuf@google.com>
|
Michal Zalewski <lcamtuf@google.com>
|
||||||
|
|
||||||
Idea & design very much by Andrew Griffiths.
|
|
||||||
|
|
||||||
TCG instrumentation and block chaining support by Andrea Biondo
|
TCG instrumentation and block chaining support by Andrea Biondo
|
||||||
<andrea.biondo965@gmail.com>
|
<andrea.biondo965@gmail.com>
|
||||||
|
|
||||||
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
QEMU 3.1.0 port, TCG thread-safety, CompareCoverage and NeverZero
|
||||||
<andreafioraldi@gmail.com>
|
counters by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||||
|
|
||||||
Copyright 2015, 2016, 2017 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -32,15 +31,15 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../../config.h"
|
#include "afl-qemu-common.h"
|
||||||
#include "tcg.h"
|
#include "tcg.h"
|
||||||
#include "tcg-op.h"
|
#include "tcg-op.h"
|
||||||
|
|
||||||
/* Declared in afl-qemu-cpu-inl.h */
|
/* Declared in afl-qemu-cpu-inl.h */
|
||||||
extern unsigned char *afl_area_ptr;
|
extern unsigned char *afl_area_ptr;
|
||||||
extern unsigned int afl_inst_rms;
|
extern unsigned int afl_inst_rms;
|
||||||
extern abi_ulong afl_start_code, afl_end_code;
|
extern abi_ulong afl_start_code, afl_end_code;
|
||||||
extern u8 afl_enable_compcov;
|
extern u8 afl_compcov_level;
|
||||||
|
|
||||||
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc,
|
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc,
|
||||||
TCGv_i64 arg1, TCGv_i64 arg2);
|
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,
|
static void afl_compcov_log_16(target_ulong cur_loc, target_ulong arg1,
|
||||||
target_ulong arg2) {
|
target_ulong arg2) {
|
||||||
|
|
||||||
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
if ((arg1 & 0xff) == (arg2 & 0xff)) { INC_AFL_AREA(cur_loc); }
|
||||||
afl_area_ptr[cur_loc]++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void afl_compcov_log_32(target_ulong cur_loc, target_ulong arg1,
|
static void afl_compcov_log_32(target_ulong cur_loc, target_ulong arg1,
|
||||||
target_ulong arg2) {
|
target_ulong arg2) {
|
||||||
|
|
||||||
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
||||||
afl_area_ptr[cur_loc]++;
|
|
||||||
|
INC_AFL_AREA(cur_loc);
|
||||||
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
|
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
|
||||||
afl_area_ptr[cur_loc +1]++;
|
|
||||||
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
|
INC_AFL_AREA(cur_loc + 1);
|
||||||
afl_area_ptr[cur_loc +2]++;
|
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) { INC_AFL_AREA(cur_loc + 2); }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void afl_compcov_log_64(target_ulong cur_loc, target_ulong arg1,
|
static void afl_compcov_log_64(target_ulong cur_loc, target_ulong arg1,
|
||||||
target_ulong arg2) {
|
target_ulong arg2) {
|
||||||
|
|
||||||
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
||||||
afl_area_ptr[cur_loc]++;
|
|
||||||
|
INC_AFL_AREA(cur_loc);
|
||||||
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
|
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
|
||||||
afl_area_ptr[cur_loc +1]++;
|
|
||||||
|
INC_AFL_AREA(cur_loc + 1);
|
||||||
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
|
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
|
||||||
afl_area_ptr[cur_loc +2]++;
|
|
||||||
|
INC_AFL_AREA(cur_loc + 2);
|
||||||
if ((arg1 & 0xffffffff) == (arg2 & 0xffffffff)) {
|
if ((arg1 & 0xffffffff) == (arg2 & 0xffffffff)) {
|
||||||
afl_area_ptr[cur_loc +3]++;
|
|
||||||
|
INC_AFL_AREA(cur_loc + 3);
|
||||||
if ((arg1 & 0xffffffffff) == (arg2 & 0xffffffffff)) {
|
if ((arg1 & 0xffffffffff) == (arg2 & 0xffffffffff)) {
|
||||||
afl_area_ptr[cur_loc +4]++;
|
|
||||||
|
INC_AFL_AREA(cur_loc + 4);
|
||||||
if ((arg1 & 0xffffffffffff) == (arg2 & 0xffffffffffff)) {
|
if ((arg1 & 0xffffffffffff) == (arg2 & 0xffffffffffff)) {
|
||||||
afl_area_ptr[cur_loc +5]++;
|
|
||||||
|
INC_AFL_AREA(cur_loc + 5);
|
||||||
if ((arg1 & 0xffffffffffffff) == (arg2 & 0xffffffffffffff)) {
|
if ((arg1 & 0xffffffffffffff) == (arg2 & 0xffffffffffffff)) {
|
||||||
afl_area_ptr[cur_loc +6]++;
|
|
||||||
|
INC_AFL_AREA(cur_loc + 6);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void afl_gen_compcov(target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2,
|
static void afl_gen_compcov(target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2,
|
||||||
TCGMemOp ot) {
|
TCGMemOp ot, int is_imm) {
|
||||||
|
|
||||||
void *func;
|
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;
|
return;
|
||||||
|
|
||||||
|
if (!is_imm && afl_compcov_level < 2) return;
|
||||||
|
|
||||||
switch (ot) {
|
switch (ot) {
|
||||||
case MO_64:
|
|
||||||
func = &afl_compcov_log_64;
|
case MO_64: func = &afl_compcov_log_64; break;
|
||||||
break;
|
case MO_32: func = &afl_compcov_log_32; break;
|
||||||
case MO_32:
|
case MO_16: func = &afl_compcov_log_16; break;
|
||||||
func = &afl_compcov_log_32;
|
default: return;
|
||||||
break;
|
|
||||||
case MO_16:
|
|
||||||
func = &afl_compcov_log_16;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||||
cur_loc &= MAP_SIZE - 1;
|
cur_loc &= MAP_SIZE - 7;
|
||||||
|
|
||||||
if (cur_loc >= afl_inst_rms) return;
|
if (cur_loc >= afl_inst_rms) return;
|
||||||
|
|
||||||
tcg_gen_afl_compcov_log_call(func, cur_loc, arg1, arg2);
|
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
|
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||||
Michal Zalewski <lcamtuf@google.com>
|
Michal Zalewski <lcamtuf@google.com>
|
||||||
|
|
||||||
Idea & design very much by Andrew Griffiths.
|
|
||||||
|
|
||||||
TCG instrumentation and block chaining support by Andrea Biondo
|
TCG instrumentation and block chaining support by Andrea Biondo
|
||||||
<andrea.biondo965@gmail.com>
|
<andrea.biondo965@gmail.com>
|
||||||
|
|
||||||
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
QEMU 3.1.0 port, TCG thread-safety, CompareCoverage and NeverZero
|
||||||
<andreafioraldi@gmail.com>
|
counters by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||||
|
|
||||||
Copyright 2015, 2016, 2017 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -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
|
/* 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. Maybe it would be better to do the alignment
|
||||||
and endian swap in tcg_reg_alloc_call(). */
|
and endian swap in tcg_reg_alloc_call(). */
|
||||||
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
|
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc) {
|
||||||
{
|
|
||||||
int real_args, pi;
|
|
||||||
unsigned sizemask, flags;
|
|
||||||
TCGOp *op;
|
|
||||||
|
|
||||||
TCGTemp *arg = tcgv_i64_temp( tcg_const_tl(cur_loc) );
|
int real_args, pi;
|
||||||
|
unsigned sizemask, flags;
|
||||||
|
TCGOp * op;
|
||||||
|
|
||||||
flags = 0;
|
TCGTemp *arg = tcgv_i64_temp(tcg_const_tl(cur_loc));
|
||||||
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1);
|
|
||||||
|
|
||||||
#if defined(__sparc__) && !defined(__arch64__) \
|
flags = 0;
|
||||||
&& !defined(CONFIG_TCG_INTERPRETER)
|
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1);
|
||||||
/* 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];
|
|
||||||
|
|
||||||
retl = NULL;
|
#if defined(__sparc__) && !defined(__arch64__) && \
|
||||||
reth = NULL;
|
!defined(CONFIG_TCG_INTERPRETER)
|
||||||
if (sizemask != 0) {
|
/* We have 64-bit values in one register, but need to pass as two
|
||||||
real_args = 0;
|
separate parameters. Split them. */
|
||||||
int is_64bit = sizemask & (1 << 2);
|
int orig_sizemask = sizemask;
|
||||||
if (is_64bit) {
|
TCGv_i64 retl, reth;
|
||||||
TCGv_i64 orig = temp_tcgv_i64(arg);
|
TCGTemp *split_args[MAX_OPC_PARAM];
|
||||||
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);
|
retl = NULL;
|
||||||
|
reth = NULL;
|
||||||
pi = 0;
|
if (sizemask != 0) {
|
||||||
|
|
||||||
TCGOP_CALLO(op) = 0;
|
|
||||||
|
|
||||||
real_args = 0;
|
real_args = 0;
|
||||||
int is_64bit = sizemask & (1 << 2);
|
int is_64bit = sizemask & (1 << 2);
|
||||||
if (TCG_TARGET_REG_BITS < 64 && is_64bit) {
|
if (is_64bit) {
|
||||||
#ifdef TCG_TARGET_CALL_ALIGN_ARGS
|
|
||||||
/* some targets want aligned 64 bit args */
|
TCGv_i64 orig = temp_tcgv_i64(arg);
|
||||||
if (real_args & 1) {
|
TCGv_i32 h = tcg_temp_new_i32();
|
||||||
op->args[pi++] = TCG_CALL_DUMMY_ARG;
|
TCGv_i32 l = tcg_temp_new_i32();
|
||||||
real_args++;
|
tcg_gen_extr_i64_i32(l, h, orig);
|
||||||
}
|
split_args[real_args++] = tcgv_i32_temp(h);
|
||||||
#endif
|
split_args[real_args++] = tcgv_i32_temp(l);
|
||||||
/* If stack grows up, then we will be placing successive
|
|
||||||
arguments at lower addresses, which means we need to
|
} else {
|
||||||
reverse the order compared to how we would normally
|
|
||||||
treat either big or little-endian. For those arguments
|
split_args[real_args++] = arg;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
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++;
|
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. */
|
if (orig_sizemask & 1) {
|
||||||
tcg_debug_assert(TCGOP_CALLI(op) == real_args);
|
|
||||||
tcg_debug_assert(pi <= ARRAY_SIZE(op->args));
|
/* 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
|
#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) {
|
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;
|
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||||
TCGTemp *args[3] = { tcgv_i64_temp( tcg_const_tl(cur_loc) ),
|
|
||||||
tcgv_i64_temp(arg1),
|
|
||||||
tcgv_i64_temp(arg2) };
|
|
||||||
|
|
||||||
flags = 0;
|
op = tcg_emit_op(INDEX_op_call);
|
||||||
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1) |
|
|
||||||
dh_sizemask(i64, 2) | dh_sizemask(i64, 3);
|
|
||||||
|
|
||||||
#if defined(__sparc__) && !defined(__arch64__) \
|
pi = 0;
|
||||||
&& !defined(CONFIG_TCG_INTERPRETER)
|
nb_rets = 0;
|
||||||
/* We have 64-bit values in one register, but need to pass as two
|
TCGOP_CALLO(op) = nb_rets;
|
||||||
separate parameters. Split them. */
|
|
||||||
int orig_sizemask = sizemask;
|
|
||||||
int orig_nargs = nargs;
|
|
||||||
TCGv_i64 retl, reth;
|
|
||||||
TCGTemp *split_args[MAX_OPC_PARAM];
|
|
||||||
|
|
||||||
retl = NULL;
|
real_args = 0;
|
||||||
reth = NULL;
|
for (i = 0; i < nargs; i++) {
|
||||||
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 */
|
|
||||||
|
|
||||||
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
|
#ifdef TCG_TARGET_CALL_ALIGN_ARGS
|
||||||
/* some targets want aligned 64 bit args */
|
/* some targets want aligned 64 bit args */
|
||||||
if (real_args & 1) {
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
op->args[pi++] = temp_arg(args[i]);
|
op->args[pi++] = TCG_CALL_DUMMY_ARG;
|
||||||
real_args++;
|
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.
|
op->args[pi++] = temp_arg(args[i]);
|
||||||
Note that describing these as TCGv_i64 eliminates an unnecessary
|
real_args++;
|
||||||
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++] = (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
|
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
|
||||||
for (i = 0; i < nargs; ++i) {
|
for (i = 0; i < nargs; ++i) {
|
||||||
int is_64bit = sizemask & (1 << (i+1)*2);
|
|
||||||
if (!is_64bit) {
|
int is_64bit = sizemask & (1 << (i + 1) * 2);
|
||||||
tcg_temp_free_internal(args[i]);
|
if (!is_64bit) { tcg_temp_free_internal(args[i]); }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif /* TCG_TARGET_EXTEND_ARGS */
|
|
||||||
|
#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
|
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||||
Michal Zalewski <lcamtuf@google.com>
|
Michal Zalewski <lcamtuf@google.com>
|
||||||
|
|
||||||
Idea & design very much by Andrew Griffiths.
|
|
||||||
|
|
||||||
TCG instrumentation and block chaining support by Andrea Biondo
|
TCG instrumentation and block chaining support by Andrea Biondo
|
||||||
<andrea.biondo965@gmail.com>
|
<andrea.biondo965@gmail.com>
|
||||||
|
|
||||||
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
QEMU 3.1.0 port, TCG thread-safety, CompareCoverage and NeverZero
|
||||||
<andreafioraldi@gmail.com>
|
counters by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||||
|
|
||||||
Copyright 2015, 2016, 2017 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -32,13 +31,13 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../../config.h"
|
#include "afl-qemu-common.h"
|
||||||
#include "tcg-op.h"
|
#include "tcg-op.h"
|
||||||
|
|
||||||
/* Declared in afl-qemu-cpu-inl.h */
|
/* Declared in afl-qemu-cpu-inl.h */
|
||||||
extern unsigned char *afl_area_ptr;
|
extern unsigned char *afl_area_ptr;
|
||||||
extern unsigned int afl_inst_rms;
|
extern unsigned int afl_inst_rms;
|
||||||
extern abi_ulong afl_start_code, afl_end_code;
|
extern abi_ulong afl_start_code, afl_end_code;
|
||||||
|
|
||||||
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc);
|
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc);
|
||||||
|
|
||||||
@ -46,7 +45,10 @@ void afl_maybe_log(target_ulong cur_loc) {
|
|||||||
|
|
||||||
static __thread abi_ulong prev_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;
|
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
|
/* Optimize for cur_loc > afl_end_code, which is the most likely case on
|
||||||
Linux systems. */
|
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;
|
return;
|
||||||
|
|
||||||
/* Looks like QEMU always maps to fixed locations, so ASLR is not a
|
/* Looks like QEMU always maps to fixed locations, so ASLR is not a
|
||||||
concern. Phew. But instruction addresses may be aligned. Let's mangle
|
concern. Phew. But instruction addresses may be aligned. Let's mangle
|
||||||
the value to get something quasi-uniform. */
|
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;
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
/* Implement probabilistic instrumentation by looking at scrambled block
|
/* Implement probabilistic instrumentation by looking at scrambled block
|
||||||
@ -75,3 +79,4 @@ static void afl_gen_trace(target_ulong cur_loc) {
|
|||||||
tcg_gen_afl_maybe_log_call(cur_loc);
|
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,
|
tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0,
|
||||||
s1->mem_index, ot | MO_LE);
|
s1->mem_index, ot | MO_LE);
|
||||||
tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1);
|
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 {
|
} else {
|
||||||
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
|
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
|
||||||
tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1);
|
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_st_rm_T0_A0(s1, ot, d);
|
||||||
}
|
}
|
||||||
gen_op_update2_cc(s1);
|
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(cpu_cc_src, s1->T1);
|
||||||
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
|
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
|
||||||
tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1);
|
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);
|
set_cc_op(s1, CC_OP_SUBB + ot);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,10 @@ diff --git a/linux-user/syscall.c b/linux-user/syscall.c
|
|||||||
index 280137da..8c0e749f 100644
|
index 280137da..8c0e749f 100644
|
||||||
--- a/linux-user/syscall.c
|
--- a/linux-user/syscall.c
|
||||||
+++ b/linux-user/syscall.c
|
+++ b/linux-user/syscall.c
|
||||||
@@ -112,6 +112,8 @@
|
@@ -112,6 +112,9 @@
|
||||||
#include "qemu.h"
|
#include "qemu.h"
|
||||||
#include "fd-trans.h"
|
#include "fd-trans.h"
|
||||||
|
+#include <linux/sockios.h>
|
||||||
|
|
||||||
+extern unsigned int afl_forksrv_pid;
|
+extern unsigned int afl_forksrv_pid;
|
||||||
+
|
+
|
||||||
|
@ -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 2016, 2017 Google Inc. All rights reserved.
|
||||||
|
Copyright 2019 AFLplusplus Project. All rights reserved.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -21,13 +26,16 @@
|
|||||||
|
|
||||||
#define AFL_MAIN
|
#define AFL_MAIN
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include "android-ashmem.h"
|
||||||
|
#endif
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "alloc-inl.h"
|
#include "alloc-inl.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "sharedmem.h"
|
#include "sharedmem.h"
|
||||||
#include "afl-common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -47,61 +55,59 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/resource.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 */
|
static u8 *in_file, /* Analyzer input test case */
|
||||||
*prog_in, /* Targeted program input file */
|
*prog_in, /* Targeted program input file */
|
||||||
*target_path, /* Path to target binary */
|
*target_path, /* Path to target binary */
|
||||||
*doc_path; /* Path to docs */
|
*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 */
|
static u32 in_len, /* Input data length */
|
||||||
orig_cksum, /* Original checksum */
|
orig_cksum, /* Original checksum */
|
||||||
total_execs, /* Total number of execs */
|
total_execs, /* Total number of execs */
|
||||||
exec_hangs, /* Total number of hangs */
|
exec_hangs, /* Total number of hangs */
|
||||||
exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */
|
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? */
|
static u8 edges_only, /* Ignore hit counts? */
|
||||||
use_hex_offsets, /* Show hex offsets? */
|
use_hex_offsets, /* Show hex offsets? */
|
||||||
use_stdin = 1; /* Use stdin for program input? */
|
use_stdin = 1; /* Use stdin for program input? */
|
||||||
|
|
||||||
static volatile u8
|
|
||||||
stop_soon, /* Ctrl-C pressed? */
|
|
||||||
child_timed_out; /* Child timed out? */
|
|
||||||
|
|
||||||
|
static volatile u8 stop_soon, /* Ctrl-C pressed? */
|
||||||
|
child_timed_out; /* Child timed out? */
|
||||||
|
|
||||||
/* Constants used for describing byte behavior. */
|
/* Constants used for describing byte behavior. */
|
||||||
|
|
||||||
#define RESP_NONE 0x00 /* Changing byte is a no-op. */
|
#define RESP_NONE 0x00 /* Changing byte is a no-op. */
|
||||||
#define RESP_MINOR 0x01 /* Some changes have no effect. */
|
#define RESP_MINOR 0x01 /* Some changes have no effect. */
|
||||||
#define RESP_VARIABLE 0x02 /* Changes produce variable paths. */
|
#define RESP_VARIABLE 0x02 /* Changes produce variable paths. */
|
||||||
#define RESP_FIXED 0x03 /* Changes produce fixed patterns. */
|
#define RESP_FIXED 0x03 /* Changes produce fixed patterns. */
|
||||||
|
|
||||||
#define RESP_LEN 0x04 /* Potential length field */
|
#define RESP_LEN 0x04 /* Potential length field */
|
||||||
#define RESP_CKSUM 0x05 /* Potential checksum */
|
#define RESP_CKSUM 0x05 /* Potential checksum */
|
||||||
#define RESP_SUSPECT 0x06 /* Potential "suspect" blob */
|
#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] = {
|
static u8 count_class_lookup[256] = {
|
||||||
|
|
||||||
[0] = 0,
|
[0] = 0,
|
||||||
[1] = 1,
|
[1] = 1,
|
||||||
[2] = 2,
|
[2] = 2,
|
||||||
[3] = 4,
|
[3] = 4,
|
||||||
[4 ... 7] = 8,
|
[4 ... 7] = 8,
|
||||||
[8 ... 15] = 16,
|
[8 ... 15] = 16,
|
||||||
[16 ... 31] = 32,
|
[16 ... 31] = 32,
|
||||||
[32 ... 127] = 64,
|
[32 ... 127] = 64,
|
||||||
[128 ... 255] = 128
|
[128 ... 255] = 128
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -112,61 +118,62 @@ static void classify_counts(u8* mem) {
|
|||||||
if (edges_only) {
|
if (edges_only) {
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
|
|
||||||
if (*mem) *mem = 1;
|
if (*mem) *mem = 1;
|
||||||
mem++;
|
mem++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
|
|
||||||
*mem = count_class_lookup[*mem];
|
*mem = count_class_lookup[*mem];
|
||||||
mem++;
|
mem++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* See if any bytes are set in the bitmap. */
|
/* See if any bytes are set in the bitmap. */
|
||||||
|
|
||||||
static inline u8 anything_set(void) {
|
static inline u8 anything_set(void) {
|
||||||
|
|
||||||
u32* ptr = (u32*)trace_bits;
|
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;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get rid of temp files (atexit handler). */
|
/* Get rid of temp files (atexit handler). */
|
||||||
|
|
||||||
static void at_exit_handler(void) {
|
static void at_exit_handler(void) {
|
||||||
|
|
||||||
unlink(prog_in); /* Ignore errors */
|
unlink(prog_in); /* Ignore errors */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Read initial file. */
|
/* Read initial file. */
|
||||||
|
|
||||||
static void read_initial_file(void) {
|
static void read_initial_file(void) {
|
||||||
|
|
||||||
struct stat st;
|
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 (fd < 0) PFATAL("Unable to open '%s'", in_file);
|
||||||
|
|
||||||
if (fstat(fd, &st) || !st.st_size)
|
if (fstat(fd, &st) || !st.st_size) FATAL("Zero-sized input file.");
|
||||||
FATAL("Zero-sized input file.");
|
|
||||||
|
|
||||||
if (st.st_size >= TMIN_MAX_FILE)
|
if (st.st_size >= TMIN_MAX_FILE)
|
||||||
FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024);
|
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);
|
in_data = ck_alloc_nozero(in_len);
|
||||||
|
|
||||||
ck_read(fd, in_data, in_len, in_file);
|
ck_read(fd, in_data, in_len, in_file);
|
||||||
@ -177,14 +184,13 @@ static void read_initial_file(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Write output file. */
|
/* Write output file. */
|
||||||
|
|
||||||
static s32 write_to_file(u8* path, u8* mem, u32 len) {
|
static s32 write_to_file(u8* path, u8* mem, u32 len) {
|
||||||
|
|
||||||
s32 ret;
|
s32 ret;
|
||||||
|
|
||||||
unlink(path); /* Ignore errors */
|
unlink(path); /* Ignore errors */
|
||||||
|
|
||||||
ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
|
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. */
|
/* Handle timeout signal. */
|
||||||
|
|
||||||
static void handle_timeout(int sig) {
|
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
|
/* Execute target application. Returns exec checksum, or 0 if program
|
||||||
times out. */
|
times out. */
|
||||||
|
|
||||||
static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
||||||
|
|
||||||
static struct itimerval it;
|
static struct itimerval it;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
s32 prog_in_fd;
|
s32 prog_in_fd;
|
||||||
u32 cksum;
|
u32 cksum;
|
||||||
@ -234,8 +238,7 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
|||||||
struct rlimit r;
|
struct rlimit r;
|
||||||
|
|
||||||
if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 ||
|
if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 ||
|
||||||
dup2(dev_null_fd, 1) < 0 ||
|
dup2(dev_null_fd, 1) < 0 || dup2(dev_null_fd, 2) < 0) {
|
||||||
dup2(dev_null_fd, 2) < 0) {
|
|
||||||
|
|
||||||
*(u32*)trace_bits = EXEC_FAIL_SIG;
|
*(u32*)trace_bits = EXEC_FAIL_SIG;
|
||||||
PFATAL("dup2() failed");
|
PFATAL("dup2() failed");
|
||||||
@ -251,18 +254,18 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
|||||||
|
|
||||||
#ifdef RLIMIT_AS
|
#ifdef RLIMIT_AS
|
||||||
|
|
||||||
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
||||||
|
|
||||||
#else
|
#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;
|
r.rlim_max = r.rlim_cur = 0;
|
||||||
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
|
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
|
||||||
|
|
||||||
execv(target_path, argv);
|
execv(target_path, argv);
|
||||||
|
|
||||||
@ -300,8 +303,10 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
|||||||
total_execs++;
|
total_execs++;
|
||||||
|
|
||||||
if (stop_soon) {
|
if (stop_soon) {
|
||||||
|
|
||||||
SAYF(cRST cLRD "\n+++ Analysis aborted by user +++\n" cRST);
|
SAYF(cRST cLRD "\n+++ Analysis aborted by user +++\n" cRST);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Always discard inputs that time out. */
|
/* 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
|
#ifdef USE_COLOR
|
||||||
|
|
||||||
/* Helper function to display a human-readable character. */
|
/* Helper function to display a human-readable character. */
|
||||||
@ -350,23 +354,24 @@ static void show_char(u8 val) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Show the legend */
|
/* Show the legend */
|
||||||
|
|
||||||
static void show_legend(void) {
|
static void show_legend(void) {
|
||||||
|
|
||||||
SAYF(" " cLGR bgGRA " 01 " cRST " - no-op block "
|
SAYF(" " cLGR bgGRA " 01 " cRST " - no-op block " cBLK bgLGN
|
||||||
cBLK bgLGN " 01 " cRST " - suspected length field\n"
|
" 01 " cRST
|
||||||
" " cBRI bgGRA " 01 " cRST " - superficial content "
|
" - suspected length field\n"
|
||||||
cBLK bgYEL " 01 " cRST " - suspected cksum or magic int\n"
|
" " cBRI bgGRA " 01 " cRST " - superficial content " cBLK bgYEL
|
||||||
" " cBLK bgCYA " 01 " cRST " - critical stream "
|
" 01 " cRST
|
||||||
cBLK bgLRD " 01 " cRST " - suspected checksummed block\n"
|
" - 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");
|
" " cBLK bgMGN " 01 " cRST " - \"magic value\" section\n\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* USE_COLOR */
|
#endif /* USE_COLOR */
|
||||||
|
|
||||||
|
|
||||||
/* Interpret and report a pattern in the input file. */
|
/* 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;
|
u32 rlen = 1, off;
|
||||||
#else
|
#else
|
||||||
u32 rlen = 1;
|
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. */
|
/* 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: {
|
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)) {
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
rtype = RESP_LEN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Uniform integers may be checksums. */
|
||||||
|
|
||||||
|
if (val && abs(in_data[i] - in_data[i + 1]) > 32) {
|
||||||
|
|
||||||
|
rtype = RESP_CKSUM;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
case 4: {
|
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)) {
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
rtype = RESP_LEN;
|
||||||
break;
|
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;
|
default: rtype = RESP_SUSPECT;
|
||||||
|
|
||||||
@ -474,19 +489,22 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) {
|
|||||||
|
|
||||||
switch (rtype) {
|
switch (rtype) {
|
||||||
|
|
||||||
case RESP_NONE: SAYF(cLGR bgGRA); break;
|
case RESP_NONE: SAYF(cLGR bgGRA); break;
|
||||||
case RESP_MINOR: SAYF(cBRI bgGRA); break;
|
case RESP_MINOR: SAYF(cBRI bgGRA); break;
|
||||||
case RESP_VARIABLE: SAYF(cBLK bgCYA); break;
|
case RESP_VARIABLE: SAYF(cBLK bgCYA); break;
|
||||||
case RESP_FIXED: SAYF(cBLK bgMGN); break;
|
case RESP_FIXED: SAYF(cBLK bgMGN); break;
|
||||||
case RESP_LEN: SAYF(cBLK bgLGN); break;
|
case RESP_LEN: SAYF(cBLK bgLGN); break;
|
||||||
case RESP_CKSUM: SAYF(cBLK bgYEL); break;
|
case RESP_CKSUM: SAYF(cBLK bgYEL); break;
|
||||||
case RESP_SUSPECT: SAYF(cBLK bgLRD); break;
|
case RESP_SUSPECT: SAYF(cBLK bgLRD); break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
show_char(in_data[i + off]);
|
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) {
|
switch (rtype) {
|
||||||
|
|
||||||
case RESP_NONE: SAYF("no-op block\n"); break;
|
case RESP_NONE: SAYF("no-op block\n"); break;
|
||||||
case RESP_MINOR: SAYF("superficial content\n"); break;
|
case RESP_MINOR: SAYF("superficial content\n"); break;
|
||||||
case RESP_VARIABLE: SAYF("critical stream\n"); break;
|
case RESP_VARIABLE: SAYF("critical stream\n"); break;
|
||||||
case RESP_FIXED: SAYF("\"magic value\" section\n"); break;
|
case RESP_FIXED: SAYF("\"magic value\" section\n"); break;
|
||||||
case RESP_LEN: SAYF("suspected length field\n"); break;
|
case RESP_LEN: SAYF("suspected length field\n"); break;
|
||||||
case RESP_CKSUM: SAYF("suspected cksum or magic int\n"); break;
|
case RESP_CKSUM: SAYF("suspected cksum or magic int\n"); break;
|
||||||
case RESP_SUSPECT: SAYF("suspected checksummed block\n"); break;
|
case RESP_SUSPECT: SAYF("suspected checksummed block\n"); break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* ^USE_COLOR */
|
#endif /* ^USE_COLOR */
|
||||||
|
|
||||||
i += rlen - 1;
|
i += rlen - 1;
|
||||||
|
|
||||||
@ -517,12 +535,10 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) {
|
|||||||
|
|
||||||
#ifdef USE_COLOR
|
#ifdef USE_COLOR
|
||||||
SAYF(cRST "\n");
|
SAYF(cRST "\n");
|
||||||
#endif /* USE_COLOR */
|
#endif /* USE_COLOR */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Actually analyze! */
|
/* Actually analyze! */
|
||||||
|
|
||||||
static void analyze(char** argv) {
|
static void analyze(char** argv) {
|
||||||
@ -533,13 +549,13 @@ static void analyze(char** argv) {
|
|||||||
u8* b_data = ck_alloc(in_len + 1);
|
u8* b_data = ck_alloc(in_len + 1);
|
||||||
u8 seq_byte = 0;
|
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");
|
ACTF("Analyzing input file (this may take a while)...\n");
|
||||||
|
|
||||||
#ifdef USE_COLOR
|
#ifdef USE_COLOR
|
||||||
show_legend();
|
show_legend();
|
||||||
#endif /* USE_COLOR */
|
#endif /* USE_COLOR */
|
||||||
|
|
||||||
for (i = 0; i < in_len; i++) {
|
for (i = 0; i < in_len; i++) {
|
||||||
|
|
||||||
@ -584,12 +600,15 @@ static void analyze(char** argv) {
|
|||||||
|
|
||||||
b_data[i] = RESP_FIXED;
|
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. */
|
/* When all checksums change, flip most significant bit of b_data. */
|
||||||
|
|
||||||
if (prev_xff != xor_ff && prev_x01 != xor_01 &&
|
if (prev_xff != xor_ff && prev_x01 != xor_01 && prev_s10 != sub_10 &&
|
||||||
prev_s10 != sub_10 && prev_a10 != add_10) seq_byte ^= 0x80;
|
prev_a10 != add_10)
|
||||||
|
seq_byte ^= 0x80;
|
||||||
|
|
||||||
b_data[i] |= seq_byte;
|
b_data[i] |= seq_byte;
|
||||||
|
|
||||||
@ -615,8 +634,6 @@ static void analyze(char** argv) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Handle Ctrl-C and the like. */
|
/* Handle Ctrl-C and the like. */
|
||||||
|
|
||||||
static void handle_stop_sig(int sig) {
|
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. */
|
/* Do basic preparations - persistent fds, filenames, etc. */
|
||||||
|
|
||||||
static void set_up_environment(void) {
|
static void set_up_environment(void) {
|
||||||
@ -671,18 +687,20 @@ static void set_up_environment(void) {
|
|||||||
if (x) {
|
if (x) {
|
||||||
|
|
||||||
if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR)))
|
if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR)))
|
||||||
FATAL("Custom MSAN_OPTIONS set without exit_code="
|
FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(
|
||||||
STRINGIFY(MSAN_ERROR) " - please fix!");
|
MSAN_ERROR) " - please fix!");
|
||||||
|
|
||||||
if (!strstr(x, "symbolize=0"))
|
if (!strstr(x, "symbolize=0"))
|
||||||
FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");
|
FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setenv("ASAN_OPTIONS", "abort_on_error=1:"
|
setenv("ASAN_OPTIONS",
|
||||||
"detect_leaks=0:"
|
"abort_on_error=1:"
|
||||||
"symbolize=0:"
|
"detect_leaks=0:"
|
||||||
"allocator_may_return_null=1", 0);
|
"symbolize=0:"
|
||||||
|
"allocator_may_return_null=1",
|
||||||
|
0);
|
||||||
|
|
||||||
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
||||||
"symbolize=0:"
|
"symbolize=0:"
|
||||||
@ -691,21 +709,22 @@ static void set_up_environment(void) {
|
|||||||
"msan_track_origins=0", 0);
|
"msan_track_origins=0", 0);
|
||||||
|
|
||||||
if (getenv("AFL_PRELOAD")) {
|
if (getenv("AFL_PRELOAD")) {
|
||||||
|
|
||||||
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
||||||
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Setup signal handlers, duh. */
|
/* Setup signal handlers, duh. */
|
||||||
|
|
||||||
static void setup_signal_handlers(void) {
|
static void setup_signal_handlers(void) {
|
||||||
|
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
sa.sa_handler = NULL;
|
sa.sa_handler = NULL;
|
||||||
sa.sa_flags = SA_RESTART;
|
sa.sa_flags = SA_RESTART;
|
||||||
sa.sa_sigaction = NULL;
|
sa.sa_sigaction = NULL;
|
||||||
|
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
@ -724,43 +743,42 @@ static void setup_signal_handlers(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Display usage hints. */
|
/* Display usage hints. */
|
||||||
|
|
||||||
static void usage(u8* argv0) {
|
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"
|
" -f file - input file read by the tested program (stdin)\n"
|
||||||
" -t msec - timeout for each run (%u ms)\n"
|
" -t msec - timeout for each run (%d ms)\n"
|
||||||
" -m megs - memory limit for child process (%u MB)\n"
|
" -m megs - memory limit for child process (%d MB)\n"
|
||||||
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||||
" -U - use unicorn-based instrumentation (Unicorn mode)\n\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);
|
exit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Find binary. */
|
/* Find binary. */
|
||||||
|
|
||||||
static void find_binary(u8* fname) {
|
static void find_binary(u8* fname) {
|
||||||
|
|
||||||
u8* env_path = 0;
|
u8* env_path = 0;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
|
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);
|
memcpy(cur_elem, env_path, delim - env_path);
|
||||||
delim++;
|
delim++;
|
||||||
|
|
||||||
} else cur_elem = ck_strdup(env_path);
|
} else
|
||||||
|
|
||||||
|
cur_elem = ck_strdup(env_path);
|
||||||
|
|
||||||
env_path = delim;
|
env_path = delim;
|
||||||
|
|
||||||
@ -795,7 +815,8 @@ static void find_binary(u8* fname) {
|
|||||||
ck_free(cur_elem);
|
ck_free(cur_elem);
|
||||||
|
|
||||||
if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
|
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);
|
ck_free(target_path);
|
||||||
target_path = 0;
|
target_path = 0;
|
||||||
@ -808,13 +829,12 @@ static void find_binary(u8* fname) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Fix up argv for QEMU. */
|
/* Fix up argv for QEMU. */
|
||||||
|
|
||||||
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
||||||
|
|
||||||
char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
|
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);
|
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);
|
cp = alloc_printf("%s/afl-qemu-trace", tmp);
|
||||||
|
|
||||||
if (access(cp, X_OK))
|
if (access(cp, X_OK)) FATAL("Unable to find '%s'", tmp);
|
||||||
FATAL("Unable to find '%s'", tmp);
|
|
||||||
|
|
||||||
target_path = new_argv[0] = cp;
|
target_path = new_argv[0] = cp;
|
||||||
return new_argv;
|
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)) {
|
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");
|
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) {
|
switch (opt) {
|
||||||
|
|
||||||
@ -893,7 +914,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
if (prog_in) FATAL("Multiple -f options not supported");
|
if (prog_in) FATAL("Multiple -f options not supported");
|
||||||
use_stdin = 0;
|
use_stdin = 0;
|
||||||
prog_in = optarg;
|
prog_in = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'e':
|
case 'e':
|
||||||
@ -904,40 +925,41 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
case 'm': {
|
case 'm': {
|
||||||
|
|
||||||
u8 suffix = 'M';
|
u8 suffix = 'M';
|
||||||
|
|
||||||
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
||||||
mem_limit_given = 1;
|
mem_limit_given = 1;
|
||||||
|
|
||||||
if (!strcmp(optarg, "none")) {
|
if (!strcmp(optarg, "none")) {
|
||||||
|
|
||||||
mem_limit = 0;
|
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;
|
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':
|
case 't':
|
||||||
|
|
||||||
@ -967,9 +989,12 @@ int main(int argc, char** argv) {
|
|||||||
unicorn_mode = 1;
|
unicorn_mode = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case 'h':
|
||||||
|
|
||||||
usage(argv[0]);
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -48,19 +53,19 @@
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/time.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* input_file; /* Originally specified input file */
|
||||||
static u8* modified_file; /* Instrumented file for the real 'as' */
|
static u8* modified_file; /* Instrumented file for the real 'as' */
|
||||||
|
|
||||||
static u8 be_quiet, /* Quiet mode (no stderr output) */
|
static u8 be_quiet, /* Quiet mode (no stderr output) */
|
||||||
clang_mode, /* Running in clang mode? */
|
clang_mode, /* Running in clang mode? */
|
||||||
pass_thru, /* Just pass data through? */
|
pass_thru, /* Just pass data through? */
|
||||||
just_version, /* Just show version? */
|
just_version, /* Just show version? */
|
||||||
sanitizer; /* Using ASAN / MSAN */
|
sanitizer; /* Using ASAN / MSAN */
|
||||||
|
|
||||||
static u32 inst_ratio = 100, /* Instrumentation probability (%) */
|
static u32 inst_ratio = 100, /* Instrumentation probability (%) */
|
||||||
as_par_cnt = 1; /* Number of params to 'as' */
|
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
|
instrumentation for whichever mode we were compiled with. This is not
|
||||||
@ -68,18 +73,17 @@ static u32 inst_ratio = 100, /* Instrumentation probability (%) */
|
|||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
static u8 use_64bit = 1;
|
static u8 use_64bit = 1;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static u8 use_64bit = 0;
|
static u8 use_64bit = 0;
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
# error "Sorry, 32-bit Apple platforms are not supported."
|
#error "Sorry, 32-bit Apple platforms are not supported."
|
||||||
#endif /* __APPLE__ */
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
#endif /* ^__x86_64__ */
|
|
||||||
|
|
||||||
|
#endif /* ^__x86_64__ */
|
||||||
|
|
||||||
/* Examine and modify parameters to pass to 'as'. Note that the file name
|
/* 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
|
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
|
/* 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
|
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++) {
|
for (i = 1; i < argc - 1; i++) {
|
||||||
|
|
||||||
if (!strcmp(argv[i], "--64")) use_64bit = 1;
|
if (!strcmp(argv[i], "--64"))
|
||||||
else if (!strcmp(argv[i], "--32")) use_64bit = 0;
|
use_64bit = 1;
|
||||||
|
else if (!strcmp(argv[i], "--32"))
|
||||||
|
use_64bit = 0;
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#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], "-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"))
|
else if (!strcmp(argv[i + 1], "i386"))
|
||||||
FATAL("Sorry, 32-bit Apple platforms are not supported.");
|
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")))
|
if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q")))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
#endif /* __APPLE__ */
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
as_params[as_par_cnt++] = argv[i];
|
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];
|
input_file = argv[argc - 1];
|
||||||
|
|
||||||
if (input_file[0] == '-') {
|
if (input_file[0] == '-') {
|
||||||
|
|
||||||
if (!strcmp(input_file + 1, "-version")) {
|
if (!strcmp(input_file + 1, "-version")) {
|
||||||
|
|
||||||
just_version = 1;
|
just_version = 1;
|
||||||
modified_file = input_file;
|
modified_file = input_file;
|
||||||
goto wrap_things_up;
|
goto wrap_things_up;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_file[1]) FATAL("Incorrect use (not called through afl-gcc?)");
|
if (input_file[1])
|
||||||
else input_file = NULL;
|
FATAL("Incorrect use (not called through afl-gcc?)");
|
||||||
|
else
|
||||||
|
input_file = NULL;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -197,22 +208,21 @@ static void edit_params(int argc, char** argv) {
|
|||||||
NSS. */
|
NSS. */
|
||||||
|
|
||||||
if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) &&
|
if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) &&
|
||||||
strncmp(input_file, "/var/tmp/", 9) &&
|
strncmp(input_file, "/var/tmp/", 9) && strncmp(input_file, "/tmp/", 5))
|
||||||
strncmp(input_file, "/tmp/", 5)) pass_thru = 1;
|
pass_thru = 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(),
|
modified_file =
|
||||||
(u32)time(NULL));
|
alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(), (u32)time(NULL));
|
||||||
|
|
||||||
wrap_things_up:
|
wrap_things_up:
|
||||||
|
|
||||||
as_params[as_par_cnt++] = modified_file;
|
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
|
/* Process input file, generate modified_file. Insert instrumentation in all
|
||||||
the appropriate places. */
|
the appropriate places. */
|
||||||
|
|
||||||
@ -222,24 +232,26 @@ static void add_instrumentation(void) {
|
|||||||
|
|
||||||
FILE* inf;
|
FILE* inf;
|
||||||
FILE* outf;
|
FILE* outf;
|
||||||
s32 outfd;
|
s32 outfd;
|
||||||
u32 ins_lines = 0;
|
u32 ins_lines = 0;
|
||||||
|
|
||||||
u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0,
|
u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0, skip_intel = 0,
|
||||||
skip_intel = 0, skip_app = 0, instrument_next = 0;
|
skip_app = 0, instrument_next = 0;
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
||||||
u8* colon_pos;
|
u8* colon_pos;
|
||||||
|
|
||||||
#endif /* __APPLE__ */
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
if (input_file) {
|
if (input_file) {
|
||||||
|
|
||||||
inf = fopen(input_file, "r");
|
inf = fopen(input_file, "r");
|
||||||
if (!inf) PFATAL("Unable to read '%s'", input_file);
|
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);
|
outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600);
|
||||||
|
|
||||||
@ -284,22 +296,26 @@ static void add_instrumentation(void) {
|
|||||||
around them, so we use that as a signal. */
|
around them, so we use that as a signal. */
|
||||||
|
|
||||||
if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) &&
|
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) ||
|
if (!strncmp(line + 2, "text\n", 5) ||
|
||||||
!strncmp(line + 2, "section\t.text", 13) ||
|
!strncmp(line + 2, "section\t.text", 13) ||
|
||||||
!strncmp(line + 2, "section\t__TEXT,__text", 21) ||
|
!strncmp(line + 2, "section\t__TEXT,__text", 21) ||
|
||||||
!strncmp(line + 2, "section __TEXT,__text", 21)) {
|
!strncmp(line + 2, "section __TEXT,__text", 21)) {
|
||||||
|
|
||||||
instr_ok = 1;
|
instr_ok = 1;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strncmp(line + 2, "section\t", 8) ||
|
if (!strncmp(line + 2, "section\t", 8) ||
|
||||||
!strncmp(line + 2, "section ", 8) ||
|
!strncmp(line + 2, "section ", 8) || !strncmp(line + 2, "bss\n", 4) ||
|
||||||
!strncmp(line + 2, "bss\n", 4) ||
|
|
||||||
!strncmp(line + 2, "data\n", 5)) {
|
!strncmp(line + 2, "data\n", 5)) {
|
||||||
|
|
||||||
instr_ok = 0;
|
instr_ok = 0;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -354,8 +370,9 @@ static void add_instrumentation(void) {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (skip_intel || skip_app || skip_csect || !instr_ok ||
|
if (skip_intel || skip_app || skip_csect || !instr_ok || line[0] == '#' ||
|
||||||
line[0] == '#' || line[0] == ' ') continue;
|
line[0] == ' ')
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Conditional branch instruction (jnz, etc). We append the instrumentation
|
/* Conditional branch instruction (jnz, etc). We append the instrumentation
|
||||||
right after the branch (to instrument the not-taken path) and at the
|
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] == '.') {
|
if (line[0] == '.') {
|
||||||
|
|
||||||
#endif /* __APPLE__ */
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
/* .L0: or LBB0_0: style jump destination */
|
/* .L0: or LBB0_0: style jump destination */
|
||||||
|
|
||||||
@ -404,17 +421,18 @@ static void add_instrumentation(void) {
|
|||||||
|
|
||||||
/* Apple: L<num> / LBB<num> */
|
/* Apple: L<num> / LBB<num> */
|
||||||
|
|
||||||
if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3)))
|
if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3))) &&
|
||||||
&& R(100) < inst_ratio) {
|
R(100) < inst_ratio) {
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
/* Apple: .L<num> / .LBB<num> */
|
/* Apple: .L<num> / .LBB<num> */
|
||||||
|
|
||||||
if ((isdigit(line[2]) || (clang_mode && !strncmp(line + 1, "LBB", 3)))
|
if ((isdigit(line[2]) ||
|
||||||
&& R(100) < inst_ratio) {
|
(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
|
/* An optimization is possible here by adding the code only if the
|
||||||
label is mentioned in the code in contexts other than call / jmp.
|
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
|
.Lfunc_begin0-style exception handling calculations (a problem on
|
||||||
MacOS X). */
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,27 +464,27 @@ static void add_instrumentation(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ins_lines)
|
if (ins_lines) fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
|
||||||
fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
|
|
||||||
|
|
||||||
if (input_file) fclose(inf);
|
if (input_file) fclose(inf);
|
||||||
fclose(outf);
|
fclose(outf);
|
||||||
|
|
||||||
if (!be_quiet) {
|
if (!be_quiet) {
|
||||||
|
|
||||||
if (!ins_lines) WARNF("No instrumentation targets found%s.",
|
if (!ins_lines)
|
||||||
pass_thru ? " (pass-thru mode)" : "");
|
WARNF("No instrumentation targets found%s.",
|
||||||
else OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).",
|
pass_thru ? " (pass-thru mode)" : "");
|
||||||
ins_lines, use_64bit ? "64" : "32",
|
else
|
||||||
getenv("AFL_HARDEN") ? "hardened" :
|
OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).", ins_lines,
|
||||||
(sanitizer ? "ASAN/MSAN" : "non-hardened"),
|
use_64bit ? "64" : "32",
|
||||||
inst_ratio);
|
getenv("AFL_HARDEN") ? "hardened"
|
||||||
|
: (sanitizer ? "ASAN/MSAN" : "non-hardened"),
|
||||||
|
inst_ratio);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Main entry point */
|
/* Main entry point */
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
@ -473,7 +494,7 @@ int main(int argc, char** argv) {
|
|||||||
int status;
|
int status;
|
||||||
u8* inst_ratio_str = getenv("AFL_INST_RATIO");
|
u8* inst_ratio_str = getenv("AFL_INST_RATIO");
|
||||||
|
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
struct timezone tz;
|
struct timezone tz;
|
||||||
|
|
||||||
clang_mode = !!getenv(CLANG_ENV_VAR);
|
clang_mode = !!getenv(CLANG_ENV_VAR);
|
||||||
@ -482,18 +503,25 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
SAYF(cCYA "afl-as" VERSION cRST " by <lcamtuf@google.com>\n");
|
SAYF(cCYA "afl-as" VERSION cRST " by <lcamtuf@google.com>\n");
|
||||||
|
|
||||||
} else be_quiet = 1;
|
} else
|
||||||
|
|
||||||
|
be_quiet = 1;
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
|
|
||||||
SAYF("\n"
|
SAYF(
|
||||||
"This is a helper application for afl-fuzz. It is a wrapper around GNU 'as',\n"
|
"\n"
|
||||||
"executed by the toolchain whenever using afl-gcc or afl-clang. You probably\n"
|
"This is a helper application for afl-fuzz. It is a wrapper around GNU "
|
||||||
"don't want to run this program directly.\n\n"
|
"'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"
|
"Rarely, when dealing with extremely complex projects, it may be "
|
||||||
"set AFL_INST_RATIO to a value less than 100 in order to reduce the odds of\n"
|
"advisable to\n"
|
||||||
"instrumenting every discovered branch.\n\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);
|
exit(1);
|
||||||
|
|
||||||
@ -524,9 +552,10 @@ int main(int argc, char** argv) {
|
|||||||
that... */
|
that... */
|
||||||
|
|
||||||
if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) {
|
if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) {
|
||||||
|
|
||||||
sanitizer = 1;
|
sanitizer = 1;
|
||||||
if (!getenv("AFL_INST_RATIO"))
|
if (!getenv("AFL_INST_RATIO")) inst_ratio /= 3;
|
||||||
inst_ratio /= 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!just_version) add_instrumentation();
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -43,19 +48,18 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static u8* as_path; /* Path to the AFL 'as' wrapper */
|
static u8* as_path; /* Path to the AFL 'as' wrapper */
|
||||||
static u8** cc_params; /* Parameters passed to the real CC */
|
static u8** cc_params; /* Parameters passed to the real CC */
|
||||||
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
|
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
|
||||||
static u8 be_quiet, /* Quiet mode */
|
static u8 be_quiet, /* Quiet mode */
|
||||||
clang_mode; /* Invoked as afl-clang*? */
|
clang_mode; /* Invoked as afl-clang*? */
|
||||||
|
|
||||||
|
|
||||||
/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
|
/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
|
||||||
from argv[0]. If that fails, abort. */
|
from argv[0]. If that fails, abort. */
|
||||||
|
|
||||||
static void find_as(u8* argv0) {
|
static void find_as(u8* argv0) {
|
||||||
|
|
||||||
u8 *afl_path = getenv("AFL_PATH");
|
u8* afl_path = getenv("AFL_PATH");
|
||||||
u8 *slash, *tmp;
|
u8 *slash, *tmp;
|
||||||
|
|
||||||
if (afl_path) {
|
if (afl_path) {
|
||||||
@ -63,9 +67,11 @@ static void find_as(u8* argv0) {
|
|||||||
tmp = alloc_printf("%s/as", afl_path);
|
tmp = alloc_printf("%s/as", afl_path);
|
||||||
|
|
||||||
if (!access(tmp, X_OK)) {
|
if (!access(tmp, X_OK)) {
|
||||||
|
|
||||||
as_path = afl_path;
|
as_path = afl_path;
|
||||||
ck_free(tmp);
|
ck_free(tmp);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ck_free(tmp);
|
ck_free(tmp);
|
||||||
@ -76,7 +82,7 @@ static void find_as(u8* argv0) {
|
|||||||
|
|
||||||
if (slash) {
|
if (slash) {
|
||||||
|
|
||||||
u8 *dir;
|
u8* dir;
|
||||||
|
|
||||||
*slash = 0;
|
*slash = 0;
|
||||||
dir = ck_strdup(argv0);
|
dir = ck_strdup(argv0);
|
||||||
@ -85,9 +91,11 @@ static void find_as(u8* argv0) {
|
|||||||
tmp = alloc_printf("%s/afl-as", dir);
|
tmp = alloc_printf("%s/afl-as", dir);
|
||||||
|
|
||||||
if (!access(tmp, X_OK)) {
|
if (!access(tmp, X_OK)) {
|
||||||
|
|
||||||
as_path = dir;
|
as_path = dir;
|
||||||
ck_free(tmp);
|
ck_free(tmp);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ck_free(tmp);
|
ck_free(tmp);
|
||||||
@ -96,21 +104,22 @@ static void find_as(u8* argv0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!access(AFL_PATH "/as", X_OK)) {
|
if (!access(AFL_PATH "/as", X_OK)) {
|
||||||
|
|
||||||
as_path = AFL_PATH;
|
as_path = AFL_PATH;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
|
FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Copy argv to cc_params, making the necessary edits. */
|
/* Copy argv to cc_params, making the necessary edits. */
|
||||||
|
|
||||||
static void edit_params(u32 argc, char** argv) {
|
static void edit_params(u32 argc, char** argv) {
|
||||||
|
|
||||||
u8 fortify_set = 0, asan_set = 0;
|
u8 fortify_set = 0, asan_set = 0;
|
||||||
u8 *name;
|
u8* name;
|
||||||
|
|
||||||
#if defined(__FreeBSD__) && defined(__x86_64__)
|
#if defined(__FreeBSD__) && defined(__x86_64__)
|
||||||
u8 m32_set = 0;
|
u8 m32_set = 0;
|
||||||
@ -119,7 +128,10 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
cc_params = ck_alloc((argc + 128) * sizeof(u8*));
|
cc_params = ck_alloc((argc + 128) * sizeof(u8*));
|
||||||
|
|
||||||
name = strrchr(argv[0], '/');
|
name = strrchr(argv[0], '/');
|
||||||
if (!name) name = argv[0]; else name++;
|
if (!name)
|
||||||
|
name = argv[0];
|
||||||
|
else
|
||||||
|
name++;
|
||||||
|
|
||||||
if (!strncmp(name, "afl-clang", 9)) {
|
if (!strncmp(name, "afl-clang", 9)) {
|
||||||
|
|
||||||
@ -128,11 +140,15 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
setenv(CLANG_ENV_VAR, "1", 1);
|
setenv(CLANG_ENV_VAR, "1", 1);
|
||||||
|
|
||||||
if (!strcmp(name, "afl-clang++")) {
|
if (!strcmp(name, "afl-clang++")) {
|
||||||
|
|
||||||
u8* alt_cxx = getenv("AFL_CXX");
|
u8* alt_cxx = getenv("AFL_CXX");
|
||||||
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
|
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"clang++";
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
u8* alt_cc = getenv("AFL_CC");
|
u8* alt_cc = getenv("AFL_CC");
|
||||||
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
|
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -145,16 +161,22 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
||||||
if (!strcmp(name, "afl-g++")) cc_params[0] = getenv("AFL_CXX");
|
if (!strcmp(name, "afl-g++"))
|
||||||
else if (!strcmp(name, "afl-gcj")) cc_params[0] = getenv("AFL_GCJ");
|
cc_params[0] = getenv("AFL_CXX");
|
||||||
else cc_params[0] = getenv("AFL_CC");
|
else if (!strcmp(name, "afl-gcj"))
|
||||||
|
cc_params[0] = getenv("AFL_GCJ");
|
||||||
|
else
|
||||||
|
cc_params[0] = getenv("AFL_CC");
|
||||||
|
|
||||||
if (!cc_params[0]) {
|
if (!cc_params[0]) {
|
||||||
|
|
||||||
SAYF("\n" cLRD "[-] " cRST
|
SAYF("\n" cLRD "[-] " cRST
|
||||||
"On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n"
|
"On Apple systems, 'gcc' is usually just a wrapper for clang. "
|
||||||
" 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n"
|
"Please use the\n"
|
||||||
" set AFL_CC or AFL_CXX to specify the correct path to that compiler.\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");
|
FATAL("AFL_CC or AFL_CXX required on MacOS X");
|
||||||
|
|
||||||
@ -163,28 +185,41 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
if (!strcmp(name, "afl-g++")) {
|
if (!strcmp(name, "afl-g++")) {
|
||||||
|
|
||||||
u8* alt_cxx = getenv("AFL_CXX");
|
u8* alt_cxx = getenv("AFL_CXX");
|
||||||
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
|
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
|
||||||
|
|
||||||
} else if (!strcmp(name, "afl-gcj")) {
|
} else if (!strcmp(name, "afl-gcj")) {
|
||||||
|
|
||||||
u8* alt_cc = getenv("AFL_GCJ");
|
u8* alt_cc = getenv("AFL_GCJ");
|
||||||
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
|
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcj";
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
u8* alt_cc = getenv("AFL_CC");
|
u8* alt_cc = getenv("AFL_CC");
|
||||||
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
|
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __APPLE__ */
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (--argc) {
|
while (--argc) {
|
||||||
|
|
||||||
u8* cur = *(++argv);
|
u8* cur = *(++argv);
|
||||||
|
|
||||||
if (!strncmp(cur, "-B", 2)) {
|
if (!strncmp(cur, "-B", 2)) {
|
||||||
|
|
||||||
if (!be_quiet) WARNF("-B is already set, overriding");
|
if (!be_quiet) WARNF("-B is already set, overriding");
|
||||||
|
|
||||||
if (!cur[2] && argc > 1) { argc--; argv++; }
|
if (!cur[2] && argc > 1) {
|
||||||
|
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -197,8 +232,8 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
if (!strcmp(cur, "-m32")) m32_set = 1;
|
if (!strcmp(cur, "-m32")) m32_set = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!strcmp(cur, "-fsanitize=address") ||
|
if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory"))
|
||||||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;
|
asan_set = 1;
|
||||||
|
|
||||||
if (strstr(cur, "FORTIFY_SOURCE")) fortify_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++] = "-B";
|
||||||
cc_params[cc_par_cnt++] = as_path;
|
cc_params[cc_par_cnt++] = as_path;
|
||||||
|
|
||||||
if (clang_mode)
|
if (clang_mode) cc_params[cc_par_cnt++] = "-no-integrated-as";
|
||||||
cc_params[cc_par_cnt++] = "-no-integrated-as";
|
|
||||||
|
|
||||||
if (getenv("AFL_HARDEN")) {
|
if (getenv("AFL_HARDEN")) {
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-fstack-protector-all";
|
cc_params[cc_par_cnt++] = "-fstack-protector-all";
|
||||||
|
|
||||||
if (!fortify_set)
|
if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
|
||||||
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")) {
|
} else if (getenv("AFL_USE_ASAN")) {
|
||||||
|
|
||||||
if (getenv("AFL_USE_MSAN"))
|
if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive");
|
||||||
FATAL("ASAN and MSAN are mutually exclusive");
|
|
||||||
|
|
||||||
if (getenv("AFL_HARDEN"))
|
if (getenv("AFL_HARDEN"))
|
||||||
FATAL("ASAN and AFL_HARDEN are mutually exclusive");
|
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")) {
|
} else if (getenv("AFL_USE_MSAN")) {
|
||||||
|
|
||||||
if (getenv("AFL_USE_ASAN"))
|
if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive");
|
||||||
FATAL("ASAN and MSAN are mutually exclusive");
|
|
||||||
|
|
||||||
if (getenv("AFL_HARDEN"))
|
if (getenv("AFL_HARDEN"))
|
||||||
FATAL("MSAN and AFL_HARDEN are mutually exclusive");
|
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++] = "-U_FORTIFY_SOURCE";
|
||||||
cc_params[cc_par_cnt++] = "-fsanitize=memory";
|
cc_params[cc_par_cnt++] = "-fsanitize=memory";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USEMMAP
|
#ifdef USEMMAP
|
||||||
cc_params[cc_par_cnt++] = "-lrt";
|
cc_params[cc_par_cnt++] = "-lrt";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!getenv("AFL_DONT_OPTIMIZE")) {
|
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
|
works OK. This has nothing to do with us, but let's avoid triggering
|
||||||
that bug. */
|
that bug. */
|
||||||
|
|
||||||
if (!clang_mode || !m32_set)
|
if (!clang_mode || !m32_set) cc_params[cc_par_cnt++] = "-g";
|
||||||
cc_params[cc_par_cnt++] = "-g";
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-g";
|
cc_params[cc_par_cnt++] = "-g";
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -300,31 +329,48 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Main entry point */
|
/* Main entry point */
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
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")) {
|
if (isatty(2) && !getenv("AFL_QUIET")) {
|
||||||
|
|
||||||
SAYF(cCYA "afl-cc" VERSION cRST " by <lcamtuf@google.com>\n");
|
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) {
|
if (argc < 2) {
|
||||||
|
|
||||||
SAYF("\n"
|
SAYF(
|
||||||
"This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
|
"\n"
|
||||||
"for gcc or clang, letting you recompile third-party code with the required\n"
|
"This is a helper application for afl-fuzz. It serves as a drop-in "
|
||||||
"runtime instrumentation. A common use pattern would be one of the following:\n\n"
|
"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"
|
" CC=%s/afl-gcc ./configure\n"
|
||||||
" CXX=%s/afl-g++ ./configure\n\n"
|
" CXX=%s/afl-g++ ./configure\n\n"
|
||||||
|
|
||||||
"You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n"
|
"You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and "
|
||||||
"Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
|
"AFL_AS.\n"
|
||||||
BIN_PATH, BIN_PATH);
|
"Setting AFL_HARDEN enables hardening optimizations in the compiled "
|
||||||
|
"code.\n\n",
|
||||||
|
BIN_PATH, BIN_PATH);
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
@ -341,3 +387,4 @@ int main(int argc, char** argv) {
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,9 +2,14 @@
|
|||||||
american fuzzy lop - free CPU gizmo
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -26,8 +31,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define AFL_MAIN
|
#define AFL_MAIN
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include "android-ashmem.h"
|
||||||
|
#endif
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -43,15 +53,14 @@
|
|||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
# define HAVE_AFFINITY 1
|
#define HAVE_AFFINITY 1
|
||||||
#endif /* __linux__ */
|
#endif /* __linux__ */
|
||||||
|
|
||||||
|
|
||||||
/* Get unix time in microseconds. */
|
/* Get unix time in microseconds. */
|
||||||
|
|
||||||
static u64 get_cur_time_us(void) {
|
static u64 get_cur_time_us(void) {
|
||||||
|
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
struct timezone tz;
|
struct timezone tz;
|
||||||
|
|
||||||
gettimeofday(&tv, &tz);
|
gettimeofday(&tv, &tz);
|
||||||
@ -60,7 +69,6 @@ static u64 get_cur_time_us(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get CPU usage in microseconds. */
|
/* Get CPU usage in microseconds. */
|
||||||
|
|
||||||
static u64 get_cpu_usage_us(void) {
|
static u64 get_cpu_usage_us(void) {
|
||||||
@ -74,7 +82,6 @@ static u64 get_cpu_usage_us(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Measure preemption rate. */
|
/* Measure preemption rate. */
|
||||||
|
|
||||||
static u32 measure_preemption(u32 target_ms) {
|
static u32 measure_preemption(u32 target_ms) {
|
||||||
@ -91,14 +98,17 @@ repeat_loop:
|
|||||||
|
|
||||||
v1 = CTEST_BUSY_CYCLES;
|
v1 = CTEST_BUSY_CYCLES;
|
||||||
|
|
||||||
while (v1--) v2++;
|
while (v1--)
|
||||||
|
v2++;
|
||||||
sched_yield();
|
sched_yield();
|
||||||
|
|
||||||
en_t = get_cur_time_us();
|
en_t = get_cur_time_us();
|
||||||
|
|
||||||
if (en_t - st_t < target_ms * 1000) {
|
if (en_t - st_t < target_ms * 1000) {
|
||||||
|
|
||||||
loop_repeats++;
|
loop_repeats++;
|
||||||
goto repeat_loop;
|
goto repeat_loop;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Let's see what percentage of this time we actually had a chance to
|
/* 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();
|
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;
|
slice_delta = (en_c - st_c) / 1000;
|
||||||
|
|
||||||
return real_delta * 100 / slice_delta;
|
return real_delta * 100 / slice_delta;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Do the benchmark thing. */
|
/* Do the benchmark thing. */
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
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
|
#ifdef HAVE_AFFINITY
|
||||||
|
|
||||||
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN),
|
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN), idle_cpus = 0, maybe_cpus = 0, i;
|
||||||
idle_cpus = 0, maybe_cpus = 0, i;
|
|
||||||
|
|
||||||
SAYF(cCYA "afl-gotcpu" VERSION cRST " by <lcamtuf@google.com>\n");
|
SAYF(cCYA "afl-gotcpu" VERSION cRST " by <lcamtuf@google.com>\n");
|
||||||
|
|
||||||
@ -137,7 +153,7 @@ int main(int argc, char** argv) {
|
|||||||
if (!fr) {
|
if (!fr) {
|
||||||
|
|
||||||
cpu_set_t c;
|
cpu_set_t c;
|
||||||
u32 util_perc;
|
u32 util_perc;
|
||||||
|
|
||||||
CPU_ZERO(&c);
|
CPU_ZERO(&c);
|
||||||
CPU_SET(i, &c);
|
CPU_SET(i, &c);
|
||||||
@ -247,6 +263,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
return (util_perc > 105) + (util_perc > 130);
|
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
|
#define AFL_MAIN
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include "android-ashmem.h"
|
||||||
|
#endif
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
@ -29,68 +54,79 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#ifndef USEMMAP
|
#ifndef USEMMAP
|
||||||
#include <sys/ipc.h>
|
#include <sys/ipc.h>
|
||||||
#include <sys/shm.h>
|
#include <sys/shm.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern unsigned char*trace_bits;
|
extern unsigned char *trace_bits;
|
||||||
|
|
||||||
#ifdef USEMMAP
|
#ifdef USEMMAP
|
||||||
/* ================ Proteas ================ */
|
/* ================ Proteas ================ */
|
||||||
int g_shm_fd = -1;
|
int g_shm_fd = -1;
|
||||||
unsigned char *g_shm_base = NULL;
|
unsigned char *g_shm_base = NULL;
|
||||||
char g_shm_file_path[L_tmpnam];
|
char g_shm_file_path[L_tmpnam];
|
||||||
/* ========================================= */
|
/* ========================================= */
|
||||||
#else
|
#else
|
||||||
static s32 shm_id; /* ID of the SHM region */
|
static s32 shm_id; /* ID of the SHM region */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Get rid of shared memory (atexit handler). */
|
/* Get rid of shared memory (atexit handler). */
|
||||||
|
|
||||||
void remove_shm(void) {
|
void remove_shm(void) {
|
||||||
|
|
||||||
#ifdef USEMMAP
|
#ifdef USEMMAP
|
||||||
if (g_shm_base != NULL) {
|
if (g_shm_base != NULL) {
|
||||||
|
|
||||||
munmap(g_shm_base, MAP_SIZE);
|
munmap(g_shm_base, MAP_SIZE);
|
||||||
g_shm_base = NULL;
|
g_shm_base = NULL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_shm_fd != -1) {
|
if (g_shm_fd != -1) {
|
||||||
|
|
||||||
close(g_shm_fd);
|
close(g_shm_fd);
|
||||||
g_shm_fd = -1;
|
g_shm_fd = -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
shmctl(shm_id, IPC_RMID, NULL);
|
shmctl(shm_id, IPC_RMID, NULL);
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* Configure shared memory. */
|
/* Configure shared memory. */
|
||||||
|
|
||||||
void setup_shm(unsigned char dumb_mode) {
|
void setup_shm(unsigned char dumb_mode) {
|
||||||
|
|
||||||
#ifdef USEMMAP
|
#ifdef USEMMAP
|
||||||
/* generate random file name for multi instance */
|
/* 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 */
|
/* so we do this worse workaround */
|
||||||
snprintf(g_shm_file_path, L_tmpnam, "/afl_%d_%ld", getpid(), random());
|
snprintf(g_shm_file_path, L_tmpnam, "/afl_%d_%ld", getpid(), random());
|
||||||
|
|
||||||
/* create the shared memory segment as if it was a file */
|
/* 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);
|
g_shm_fd = shm_open(g_shm_file_path, O_CREAT | O_RDWR | O_EXCL, 0600);
|
||||||
if (g_shm_fd == -1) {
|
if (g_shm_fd == -1) { PFATAL("shm_open() failed"); }
|
||||||
PFATAL("shm_open() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* configure the size of the shared memory segment */
|
/* configure the size of the shared memory segment */
|
||||||
if (ftruncate(g_shm_fd, MAP_SIZE)) {
|
if (ftruncate(g_shm_fd, MAP_SIZE)) {
|
||||||
|
|
||||||
PFATAL("setup_shm(): ftruncate() failed");
|
PFATAL("setup_shm(): ftruncate() failed");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* map the shared memory segment to the address space of the process */
|
/* 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) {
|
if (g_shm_base == MAP_FAILED) {
|
||||||
|
|
||||||
close(g_shm_fd);
|
close(g_shm_fd);
|
||||||
g_shm_fd = -1;
|
g_shm_fd = -1;
|
||||||
PFATAL("mmap() failed");
|
PFATAL("mmap() failed");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
atexit(remove_shm);
|
atexit(remove_shm);
|
||||||
@ -133,5 +169,6 @@ void setup_shm(unsigned char dumb_mode) {
|
|||||||
if (!trace_bits) PFATAL("shmat() failed");
|
if (!trace_bits) PFATAL("shmat() failed");
|
||||||
|
|
||||||
#endif
|
#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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -23,13 +30,16 @@
|
|||||||
|
|
||||||
#define AFL_MAIN
|
#define AFL_MAIN
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include "android-ashmem.h"
|
||||||
|
#endif
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "alloc-inl.h"
|
#include "alloc-inl.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "sharedmem.h"
|
#include "sharedmem.h"
|
||||||
#include "afl-common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -48,61 +58,54 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/resource.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 */
|
static u8 *out_file, /* Trace output file */
|
||||||
*doc_path, /* Path to docs */
|
*doc_path, /* Path to docs */
|
||||||
*target_path, /* Path to target binary */
|
*target_path, /* Path to target binary */
|
||||||
*at_file; /* Substitution string for @@ */
|
*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? */
|
static u8 quiet_mode, /* Hide non-essential messages? */
|
||||||
edges_only, /* Ignore hit counts? */
|
edges_only, /* Ignore hit counts? */
|
||||||
raw_instr_output, /* Do not apply AFL filters */
|
raw_instr_output, /* Do not apply AFL filters */
|
||||||
cmin_mode, /* Generate output in afl-cmin mode? */
|
cmin_mode, /* Generate output in afl-cmin mode? */
|
||||||
binary_mode, /* Write output as a binary map */
|
binary_mode, /* Write output as a binary map */
|
||||||
keep_cores; /* Allow coredumps? */
|
keep_cores; /* Allow coredumps? */
|
||||||
|
|
||||||
static volatile u8
|
static volatile u8 stop_soon, /* Ctrl-C pressed? */
|
||||||
stop_soon, /* Ctrl-C pressed? */
|
child_timed_out, /* Child timed out? */
|
||||||
child_timed_out, /* Child timed out? */
|
child_crashed; /* Child crashed? */
|
||||||
child_crashed; /* Child crashed? */
|
|
||||||
|
|
||||||
/* Classify tuple counts. Instead of mapping to individual bits, as in
|
/* 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. */
|
afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */
|
||||||
|
|
||||||
static const u8 count_class_human[256] = {
|
static const u8 count_class_human[256] = {
|
||||||
|
|
||||||
[0] = 0,
|
[0] = 0, [1] = 1, [2] = 2, [3] = 3,
|
||||||
[1] = 1,
|
[4 ... 7] = 4, [8 ... 15] = 5, [16 ... 31] = 6, [32 ... 127] = 7,
|
||||||
[2] = 2,
|
[128 ... 255] = 8
|
||||||
[3] = 3,
|
|
||||||
[4 ... 7] = 4,
|
|
||||||
[8 ... 15] = 5,
|
|
||||||
[16 ... 31] = 6,
|
|
||||||
[32 ... 127] = 7,
|
|
||||||
[128 ... 255] = 8
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u8 count_class_binary[256] = {
|
static const u8 count_class_binary[256] = {
|
||||||
|
|
||||||
[0] = 0,
|
[0] = 0,
|
||||||
[1] = 1,
|
[1] = 1,
|
||||||
[2] = 2,
|
[2] = 2,
|
||||||
[3] = 4,
|
[3] = 4,
|
||||||
[4 ... 7] = 8,
|
[4 ... 7] = 8,
|
||||||
[8 ... 15] = 16,
|
[8 ... 15] = 16,
|
||||||
[16 ... 31] = 32,
|
[16 ... 31] = 32,
|
||||||
[32 ... 127] = 64,
|
[32 ... 127] = 64,
|
||||||
[128 ... 255] = 128
|
[128 ... 255] = 128
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -113,22 +116,25 @@ static void classify_counts(u8* mem, const u8* map) {
|
|||||||
if (edges_only) {
|
if (edges_only) {
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
|
|
||||||
if (*mem) *mem = 1;
|
if (*mem) *mem = 1;
|
||||||
mem++;
|
mem++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (!raw_instr_output) {
|
} else if (!raw_instr_output) {
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
|
|
||||||
*mem = map[*mem];
|
*mem = map[*mem];
|
||||||
mem++;
|
mem++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Write results. */
|
/* Write results. */
|
||||||
|
|
||||||
static u32 write_results(void) {
|
static u32 write_results(void) {
|
||||||
@ -136,8 +142,8 @@ static u32 write_results(void) {
|
|||||||
s32 fd;
|
s32 fd;
|
||||||
u32 i, ret = 0;
|
u32 i, ret = 0;
|
||||||
|
|
||||||
u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"),
|
u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"),
|
||||||
caa = !!getenv("AFL_CMIN_ALLOW_ANY");
|
caa = !!getenv("AFL_CMIN_ALLOW_ANY");
|
||||||
|
|
||||||
if (!strncmp(out_file, "/dev/", 5)) {
|
if (!strncmp(out_file, "/dev/", 5)) {
|
||||||
|
|
||||||
@ -151,7 +157,7 @@ static u32 write_results(void) {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
unlink(out_file); /* Ignore errors */
|
unlink(out_file); /* Ignore errors */
|
||||||
fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||||
if (fd < 0) PFATAL("Unable to create '%s'", out_file);
|
if (fd < 0) PFATAL("Unable to create '%s'", out_file);
|
||||||
|
|
||||||
@ -177,8 +183,7 @@ static u32 write_results(void) {
|
|||||||
ret++;
|
ret++;
|
||||||
|
|
||||||
total += trace_bits[i];
|
total += trace_bits[i];
|
||||||
if (highest < trace_bits[i])
|
if (highest < trace_bits[i]) highest = trace_bits[i];
|
||||||
highest = trace_bits[i];
|
|
||||||
|
|
||||||
if (cmin_mode) {
|
if (cmin_mode) {
|
||||||
|
|
||||||
@ -187,7 +192,9 @@ static u32 write_results(void) {
|
|||||||
|
|
||||||
fprintf(f, "%u%u\n", trace_bits[i], i);
|
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]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +206,6 @@ static u32 write_results(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Handle timeout signal. */
|
/* Handle timeout signal. */
|
||||||
|
|
||||||
static void handle_timeout(int sig) {
|
static void handle_timeout(int sig) {
|
||||||
@ -209,16 +215,14 @@ static void handle_timeout(int sig) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Execute target application. */
|
/* Execute target application. */
|
||||||
|
|
||||||
static void run_target(char** argv) {
|
static void run_target(char** argv) {
|
||||||
|
|
||||||
static struct itimerval it;
|
static struct itimerval it;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
if (!quiet_mode)
|
if (!quiet_mode) SAYF("-- Program output begins --\n" cRST);
|
||||||
SAYF("-- Program output begins --\n" cRST);
|
|
||||||
|
|
||||||
MEM_BARRIER();
|
MEM_BARRIER();
|
||||||
|
|
||||||
@ -235,8 +239,10 @@ static void run_target(char** argv) {
|
|||||||
s32 fd = open("/dev/null", O_RDWR);
|
s32 fd = open("/dev/null", O_RDWR);
|
||||||
|
|
||||||
if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) {
|
if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) {
|
||||||
|
|
||||||
*(u32*)trace_bits = EXEC_FAIL_SIG;
|
*(u32*)trace_bits = EXEC_FAIL_SIG;
|
||||||
PFATAL("Descriptor initialization failed");
|
PFATAL("Descriptor initialization failed");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
@ -249,20 +255,22 @@ static void run_target(char** argv) {
|
|||||||
|
|
||||||
#ifdef RLIMIT_AS
|
#ifdef RLIMIT_AS
|
||||||
|
|
||||||
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
||||||
|
|
||||||
#else
|
#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;
|
if (!keep_cores)
|
||||||
else r.rlim_max = r.rlim_cur = RLIM_INFINITY;
|
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);
|
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)
|
if (*(u32*)trace_bits == EXEC_FAIL_SIG)
|
||||||
FATAL("Unable to execute '%s'", argv[0]);
|
FATAL("Unable to execute '%s'", argv[0]);
|
||||||
|
|
||||||
classify_counts(trace_bits, binary_mode ?
|
classify_counts(trace_bits,
|
||||||
count_class_binary : count_class_human);
|
binary_mode ? count_class_binary : count_class_human);
|
||||||
|
|
||||||
if (!quiet_mode)
|
if (!quiet_mode) SAYF(cRST "-- Program output ends --\n");
|
||||||
SAYF(cRST "-- Program output ends --\n");
|
|
||||||
|
|
||||||
if (!child_timed_out && !stop_soon && WIFSIGNALED(status))
|
if (!child_timed_out && !stop_soon && WIFSIGNALED(status)) child_crashed = 1;
|
||||||
child_crashed = 1;
|
|
||||||
|
|
||||||
if (!quiet_mode) {
|
if (!quiet_mode) {
|
||||||
|
|
||||||
@ -317,14 +323,13 @@ static void run_target(char** argv) {
|
|||||||
else if (stop_soon)
|
else if (stop_soon)
|
||||||
SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST);
|
SAYF(cLRD "\n+++ Program aborted by user +++\n" cRST);
|
||||||
else if (child_crashed)
|
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. */
|
/* Handle Ctrl-C and the like. */
|
||||||
|
|
||||||
static void handle_stop_sig(int sig) {
|
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. */
|
/* Do basic preparations - persistent fds, filenames, etc. */
|
||||||
|
|
||||||
static void set_up_environment(void) {
|
static void set_up_environment(void) {
|
||||||
|
|
||||||
setenv("ASAN_OPTIONS", "abort_on_error=1:"
|
setenv("ASAN_OPTIONS",
|
||||||
"detect_leaks=0:"
|
"abort_on_error=1:"
|
||||||
"symbolize=0:"
|
"detect_leaks=0:"
|
||||||
"allocator_may_return_null=1", 0);
|
"symbolize=0:"
|
||||||
|
"allocator_may_return_null=1",
|
||||||
|
0);
|
||||||
|
|
||||||
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
||||||
"symbolize=0:"
|
"symbolize=0:"
|
||||||
@ -352,21 +358,22 @@ static void set_up_environment(void) {
|
|||||||
"msan_track_origins=0", 0);
|
"msan_track_origins=0", 0);
|
||||||
|
|
||||||
if (getenv("AFL_PRELOAD")) {
|
if (getenv("AFL_PRELOAD")) {
|
||||||
|
|
||||||
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
||||||
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Setup signal handlers, duh. */
|
/* Setup signal handlers, duh. */
|
||||||
|
|
||||||
static void setup_signal_handlers(void) {
|
static void setup_signal_handlers(void) {
|
||||||
|
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
sa.sa_handler = NULL;
|
sa.sa_handler = NULL;
|
||||||
sa.sa_flags = SA_RESTART;
|
sa.sa_flags = SA_RESTART;
|
||||||
sa.sa_sigaction = NULL;
|
sa.sa_sigaction = NULL;
|
||||||
|
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
@ -385,7 +392,6 @@ static void setup_signal_handlers(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Show banner. */
|
/* Show banner. */
|
||||||
|
|
||||||
static void show_banner(void) {
|
static void show_banner(void) {
|
||||||
@ -400,42 +406,43 @@ static void usage(u8* argv0) {
|
|||||||
|
|
||||||
show_banner();
|
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"
|
" -t msec - timeout for each run (none)\n"
|
||||||
" -m megs - memory limit for child process (%u MB)\n"
|
" -m megs - memory limit for child process (%d MB)\n"
|
||||||
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||||
" -U - use Unicorn-based instrumentation (Unicorn mode)\n"
|
" -U - use Unicorn-based instrumentation (Unicorn mode)\n"
|
||||||
" (Not necessary, here for consistency with other afl-* tools)\n\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"
|
" -q - sink program's output and don't show messages\n"
|
||||||
" -e - show edge coverage only, ignore hit counts\n"
|
" -e - show edge coverage only, ignore hit counts\n"
|
||||||
" -r - show real tuple values instead of AFL filter values\n"
|
" -r - show real tuple values instead of AFL filter values\n"
|
||||||
" -c - allow core dumps\n\n"
|
" -c - allow core dumps\n\n"
|
||||||
|
|
||||||
"This tool displays raw tuple data captured by AFL instrumentation.\n"
|
"This tool displays raw tuple data captured by AFL instrumentation.\n"
|
||||||
"For additional help, consult %s/README.\n\n" cRST,
|
"For additional help, consult %s/README.\n\n" cRST,
|
||||||
|
|
||||||
argv0, MEM_LIMIT, doc_path);
|
argv0, MEM_LIMIT, doc_path);
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Find binary. */
|
/* Find binary. */
|
||||||
|
|
||||||
static void find_binary(u8* fname) {
|
static void find_binary(u8* fname) {
|
||||||
|
|
||||||
u8* env_path = 0;
|
u8* env_path = 0;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
|
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);
|
memcpy(cur_elem, env_path, delim - env_path);
|
||||||
delim++;
|
delim++;
|
||||||
|
|
||||||
} else cur_elem = ck_strdup(env_path);
|
} else
|
||||||
|
|
||||||
|
cur_elem = ck_strdup(env_path);
|
||||||
|
|
||||||
env_path = delim;
|
env_path = delim;
|
||||||
|
|
||||||
@ -470,7 +479,8 @@ static void find_binary(u8* fname) {
|
|||||||
ck_free(cur_elem);
|
ck_free(cur_elem);
|
||||||
|
|
||||||
if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
|
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);
|
ck_free(target_path);
|
||||||
target_path = 0;
|
target_path = 0;
|
||||||
@ -483,13 +493,12 @@ static void find_binary(u8* fname) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Fix up argv for QEMU. */
|
/* Fix up argv for QEMU. */
|
||||||
|
|
||||||
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
||||||
|
|
||||||
char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
|
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);
|
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);
|
cp = alloc_printf("%s/afl-qemu-trace", tmp);
|
||||||
|
|
||||||
if (access(cp, X_OK))
|
if (access(cp, X_OK)) FATAL("Unable to find '%s'", tmp);
|
||||||
FATAL("Unable to find '%s'", tmp);
|
|
||||||
|
|
||||||
target_path = new_argv[0] = cp;
|
target_path = new_argv[0] = cp;
|
||||||
return new_argv;
|
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)) {
|
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;
|
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) {
|
switch (opt) {
|
||||||
|
|
||||||
@ -565,40 +575,41 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
case 'm': {
|
case 'm': {
|
||||||
|
|
||||||
u8 suffix = 'M';
|
u8 suffix = 'M';
|
||||||
|
|
||||||
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
||||||
mem_limit_given = 1;
|
mem_limit_given = 1;
|
||||||
|
|
||||||
if (!strcmp(optarg, "none")) {
|
if (!strcmp(optarg, "none")) {
|
||||||
|
|
||||||
mem_limit = 0;
|
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;
|
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':
|
case 't':
|
||||||
|
|
||||||
@ -606,6 +617,7 @@ int main(int argc, char** argv) {
|
|||||||
timeout_given = 1;
|
timeout_given = 1;
|
||||||
|
|
||||||
if (strcmp(optarg, "none")) {
|
if (strcmp(optarg, "none")) {
|
||||||
|
|
||||||
exec_tmout = atoi(optarg);
|
exec_tmout = atoi(optarg);
|
||||||
|
|
||||||
if (exec_tmout < 20 || optarg[0] == '-')
|
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
|
/* This is an undocumented option to write data in the syntax expected
|
||||||
by afl-cmin. Nobody else should have any use for this. */
|
by afl-cmin. Nobody else should have any use for this. */
|
||||||
|
|
||||||
cmin_mode = 1;
|
cmin_mode = 1;
|
||||||
quiet_mode = 1;
|
quiet_mode = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -680,9 +692,12 @@ int main(int argc, char** argv) {
|
|||||||
raw_instr_output = 1;
|
raw_instr_output = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case 'h':
|
||||||
|
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: usage(argv[0]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,8 +711,10 @@ int main(int argc, char** argv) {
|
|||||||
find_binary(argv[optind]);
|
find_binary(argv[optind]);
|
||||||
|
|
||||||
if (!quiet_mode) {
|
if (!quiet_mode) {
|
||||||
|
|
||||||
show_banner();
|
show_banner();
|
||||||
ACTF("Executing '%s'...\n", target_path);
|
ACTF("Executing '%s'...\n", target_path);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
detect_file_args(argv + optind, at_file);
|
detect_file_args(argv + optind, at_file);
|
||||||
@ -714,7 +731,8 @@ int main(int argc, char** argv) {
|
|||||||
if (!quiet_mode) {
|
if (!quiet_mode) {
|
||||||
|
|
||||||
if (!tcnt) FATAL("No instrumentation detected" cRST);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
american fuzzy lop - test case minimizer
|
american fuzzy lop++ - test case minimizer
|
||||||
----------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
Originally written by Michal Zalewski <lcamtuf@google.com>
|
||||||
|
|
||||||
Copyright 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -21,13 +28,18 @@
|
|||||||
|
|
||||||
#define AFL_MAIN
|
#define AFL_MAIN
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include "android-ashmem.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "alloc-inl.h"
|
#include "alloc-inl.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
|
#include "forkserver.h"
|
||||||
#include "sharedmem.h"
|
#include "sharedmem.h"
|
||||||
#include "afl-common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -46,61 +58,71 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
|
||||||
static s32 forksrv_pid, /* PID of the fork server */
|
s32 forksrv_pid, /* PID of the fork server */
|
||||||
child_pid; /* PID of the tested program */
|
child_pid; /* PID of the tested program */
|
||||||
|
|
||||||
static s32 fsrv_ctl_fd, /* Fork server control pipe (write) */
|
s32 fsrv_ctl_fd, /* Fork server control pipe (write) */
|
||||||
fsrv_st_fd; /* Fork server status pipe (read) */
|
fsrv_st_fd; /* Fork server status pipe (read) */
|
||||||
|
|
||||||
u8 *trace_bits; /* SHM with instrumentation bitmap */
|
u8* trace_bits; /* SHM with instrumentation bitmap */
|
||||||
static u8 *mask_bitmap; /* Mask for trace bits (-B) */
|
static u8* mask_bitmap; /* Mask for trace bits (-B) */
|
||||||
|
|
||||||
static u8 *in_file, /* Minimizer input test case */
|
u8 *in_file, /* Minimizer input test case */
|
||||||
*out_file, /* Minimizer output file */
|
*output_file, /* Minimizer output file */
|
||||||
*prog_in, /* Targeted program input file */
|
*out_file, /* Targeted program input file */
|
||||||
*target_path, /* Path to target binary */
|
*target_path, /* Path to target binary */
|
||||||
*doc_path; /* Path to docs */
|
*doc_path; /* Path to docs */
|
||||||
|
|
||||||
static s32 prog_in_fd; /* Persistent fd for prog_in */
|
s32 out_fd; /* Persistent fd for out_file */
|
||||||
|
|
||||||
static u8* in_data; /* Input data for trimming */
|
static u8* in_data; /* Input data for trimming */
|
||||||
|
|
||||||
static u32 in_len, /* Input data length */
|
static u32 in_len, /* Input data length */
|
||||||
orig_cksum, /* Original checksum */
|
orig_cksum, /* Original checksum */
|
||||||
total_execs, /* Total number of execs */
|
total_execs, /* Total number of execs */
|
||||||
missed_hangs, /* Misses due to hangs */
|
missed_hangs, /* Misses due to hangs */
|
||||||
missed_crashes, /* Misses due to crashes */
|
missed_crashes, /* Misses due to crashes */
|
||||||
missed_paths, /* Misses due to exec path diffs */
|
missed_paths; /* Misses due to exec path diffs */
|
||||||
exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */
|
u32 exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */
|
||||||
|
|
||||||
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
||||||
|
|
||||||
static s32 dev_null_fd = -1; /* FD to /dev/null */
|
s32 dev_null_fd = -1; /* FD to /dev/null */
|
||||||
|
|
||||||
static u8 crash_mode, /* Crash-centric mode? */
|
static u8 crash_mode, /* Crash-centric mode? */
|
||||||
exit_crash, /* Treat non-zero exit as crash? */
|
exit_crash, /* Treat non-zero exit as crash? */
|
||||||
edges_only, /* Ignore hit counts? */
|
edges_only, /* Ignore hit counts? */
|
||||||
exact_mode, /* Require path match for crashes? */
|
exact_mode, /* Require path match for crashes? */
|
||||||
use_stdin = 1; /* Use stdin for program input? */
|
use_stdin = 1; /* Use stdin for program input? */
|
||||||
|
|
||||||
static volatile u8
|
static volatile u8 stop_soon; /* Ctrl-C pressed? */
|
||||||
stop_soon, /* Ctrl-C pressed? */
|
|
||||||
child_timed_out; /* Child timed out? */
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* forkserver section
|
||||||
|
*/
|
||||||
|
|
||||||
/* Classify tuple counts. This is a slow & naive version, but good enough here. */
|
/* we only need this to use afl-forkserver */
|
||||||
|
FILE* plot_file;
|
||||||
|
u8 uses_asan;
|
||||||
|
s32 out_fd = -1, out_dir_fd = -1, dev_urandom_fd = -1;
|
||||||
|
|
||||||
|
/* we import this as we need this information */
|
||||||
|
extern u8 child_timed_out;
|
||||||
|
|
||||||
|
/* Classify tuple counts. This is a slow & naive version, but good enough here.
|
||||||
|
*/
|
||||||
|
|
||||||
static const u8 count_class_lookup[256] = {
|
static const u8 count_class_lookup[256] = {
|
||||||
|
|
||||||
[0] = 0,
|
[0] = 0,
|
||||||
[1] = 1,
|
[1] = 1,
|
||||||
[2] = 2,
|
[2] = 2,
|
||||||
[3] = 4,
|
[3] = 4,
|
||||||
[4 ... 7] = 8,
|
[4 ... 7] = 8,
|
||||||
[8 ... 15] = 16,
|
[8 ... 15] = 16,
|
||||||
[16 ... 31] = 32,
|
[16 ... 31] = 32,
|
||||||
[32 ... 127] = 64,
|
[32 ... 127] = 64,
|
||||||
[128 ... 255] = 128
|
[128 ... 255] = 128
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -111,22 +133,25 @@ static void classify_counts(u8* mem) {
|
|||||||
if (edges_only) {
|
if (edges_only) {
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
|
|
||||||
if (*mem) *mem = 1;
|
if (*mem) *mem = 1;
|
||||||
mem++;
|
mem++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
|
|
||||||
*mem = count_class_lookup[*mem];
|
*mem = count_class_lookup[*mem];
|
||||||
mem++;
|
mem++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Apply mask to classified bitmap (if set). */
|
/* Apply mask to classified bitmap (if set). */
|
||||||
|
|
||||||
static void apply_mask(u32* mem, u32* mask) {
|
static void apply_mask(u32* mem, u32* mask) {
|
||||||
@ -145,25 +170,26 @@ static void apply_mask(u32* mem, u32* mask) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* See if any bytes are set in the bitmap. */
|
/* See if any bytes are set in the bitmap. */
|
||||||
|
|
||||||
static inline u8 anything_set(void) {
|
static inline u8 anything_set(void) {
|
||||||
|
|
||||||
u32* ptr = (u32*)trace_bits;
|
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;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get rid of temp files (atexit handler). */
|
/* Get rid of temp files (atexit handler). */
|
||||||
|
|
||||||
static void at_exit_handler(void) {
|
static void at_exit_handler(void) {
|
||||||
if (prog_in) unlink(prog_in); /* Ignore errors */
|
|
||||||
|
if (out_file) unlink(out_file); /* Ignore errors */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read initial file. */
|
/* Read initial file. */
|
||||||
@ -171,17 +197,16 @@ static void at_exit_handler(void) {
|
|||||||
static void read_initial_file(void) {
|
static void read_initial_file(void) {
|
||||||
|
|
||||||
struct stat st;
|
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 (fd < 0) PFATAL("Unable to open '%s'", in_file);
|
||||||
|
|
||||||
if (fstat(fd, &st) || !st.st_size)
|
if (fstat(fd, &st) || !st.st_size) FATAL("Zero-sized input file.");
|
||||||
FATAL("Zero-sized input file.");
|
|
||||||
|
|
||||||
if (st.st_size >= TMIN_MAX_FILE)
|
if (st.st_size >= TMIN_MAX_FILE)
|
||||||
FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024);
|
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);
|
in_data = ck_alloc_nozero(in_len);
|
||||||
|
|
||||||
ck_read(fd, in_data, in_len, in_file);
|
ck_read(fd, in_data, in_len, in_file);
|
||||||
@ -192,14 +217,13 @@ static void read_initial_file(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Write output file. */
|
/* Write output file. */
|
||||||
|
|
||||||
static s32 write_to_file(u8* path, u8* mem, u32 len) {
|
static s32 write_to_file(u8* path, u8* mem, u32 len) {
|
||||||
|
|
||||||
s32 ret;
|
s32 ret;
|
||||||
|
|
||||||
unlink(path); /* Ignore errors */
|
unlink(path); /* Ignore errors */
|
||||||
|
|
||||||
ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
|
ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||||
|
|
||||||
@ -214,38 +238,40 @@ static s32 write_to_file(u8* path, u8* mem, u32 len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Write modified data to file for testing. If use_stdin is clear, the old file
|
/* Write modified data to file for testing. If use_stdin is clear, the old file
|
||||||
is unlinked and a new one is created. Otherwise, prog_in_fd is rewound and
|
is unlinked and a new one is created. Otherwise, out_fd is rewound and
|
||||||
truncated. */
|
truncated. */
|
||||||
|
|
||||||
static void write_to_testcase(void* mem, u32 len) {
|
static void write_to_testcase(void* mem, u32 len) {
|
||||||
|
|
||||||
s32 fd = prog_in_fd;
|
s32 fd = out_fd;
|
||||||
|
|
||||||
if (!use_stdin) {
|
if (!use_stdin) {
|
||||||
|
|
||||||
unlink(prog_in); /* Ignore errors. */
|
unlink(out_file); /* Ignore errors. */
|
||||||
|
|
||||||
fd = open(prog_in, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||||
|
|
||||||
if (fd < 0) PFATAL("Unable to create '%s'", prog_in);
|
if (fd < 0) PFATAL("Unable to create '%s'", out_file);
|
||||||
|
|
||||||
} else lseek(fd, 0, SEEK_SET);
|
} else
|
||||||
|
|
||||||
ck_write(fd, mem, len, prog_in);
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
|
ck_write(fd, mem, len, out_file);
|
||||||
|
|
||||||
if (use_stdin) {
|
if (use_stdin) {
|
||||||
|
|
||||||
if (ftruncate(fd, len)) PFATAL("ftruncate() failed");
|
if (ftruncate(fd, len)) PFATAL("ftruncate() failed");
|
||||||
lseek(fd, 0, SEEK_SET);
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
} else close(fd);
|
} else
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Handle timeout signal. */
|
/* Handle timeout signal. */
|
||||||
|
/*
|
||||||
static void handle_timeout(int sig) {
|
static void handle_timeout(int sig) {
|
||||||
|
|
||||||
if (child_pid > 0) {
|
if (child_pid > 0) {
|
||||||
@ -262,8 +288,12 @@ static void handle_timeout(int sig) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
/* start the app and it's forkserver */
|
/* start the app and it's forkserver */
|
||||||
|
/*
|
||||||
static void init_forkserver(char **argv) {
|
static void init_forkserver(char **argv) {
|
||||||
|
|
||||||
static struct itimerval it;
|
static struct itimerval it;
|
||||||
int st_pipe[2], ctl_pipe[2];
|
int st_pipe[2], ctl_pipe[2];
|
||||||
int status = 0;
|
int status = 0;
|
||||||
@ -280,7 +310,7 @@ static void init_forkserver(char **argv) {
|
|||||||
|
|
||||||
struct rlimit r;
|
struct rlimit r;
|
||||||
|
|
||||||
if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 ||
|
if (dup2(use_stdin ? out_fd : dev_null_fd, 0) < 0 ||
|
||||||
dup2(dev_null_fd, 1) < 0 ||
|
dup2(dev_null_fd, 1) < 0 ||
|
||||||
dup2(dev_null_fd, 2) < 0) {
|
dup2(dev_null_fd, 2) < 0) {
|
||||||
|
|
||||||
@ -290,7 +320,7 @@ static void init_forkserver(char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
close(dev_null_fd);
|
close(dev_null_fd);
|
||||||
close(prog_in_fd);
|
close(out_fd);
|
||||||
|
|
||||||
setsid();
|
setsid();
|
||||||
|
|
||||||
@ -300,20 +330,20 @@ static void init_forkserver(char **argv) {
|
|||||||
|
|
||||||
#ifdef RLIMIT_AS
|
#ifdef RLIMIT_AS
|
||||||
|
|
||||||
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
|
setrlimit(RLIMIT_AS, &r); // Ignore errors
|
||||||
|
|
||||||
#else
|
#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;
|
r.rlim_max = r.rlim_cur = 0;
|
||||||
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
|
setrlimit(RLIMIT_CORE, &r); // Ignore errors
|
||||||
|
|
||||||
/* Set up control and status pipes, close the unneeded original fds. */
|
// Set up control and status pipes, close the unneeded original fds.
|
||||||
|
|
||||||
if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed");
|
if (dup2(ctl_pipe[0], FORKSRV_FD) < 0) PFATAL("dup2() failed");
|
||||||
if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed");
|
if (dup2(st_pipe[1], FORKSRV_FD + 1) < 0) PFATAL("dup2() failed");
|
||||||
@ -330,7 +360,7 @@ static void init_forkserver(char **argv) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close the unneeded endpoints. */
|
// Close the unneeded endpoints.
|
||||||
|
|
||||||
close(ctl_pipe[0]);
|
close(ctl_pipe[0]);
|
||||||
close(st_pipe[1]);
|
close(st_pipe[1]);
|
||||||
@ -338,7 +368,7 @@ static void init_forkserver(char **argv) {
|
|||||||
fsrv_ctl_fd = ctl_pipe[1];
|
fsrv_ctl_fd = ctl_pipe[1];
|
||||||
fsrv_st_fd = st_pipe[0];
|
fsrv_st_fd = st_pipe[0];
|
||||||
|
|
||||||
/* Configure timeout, wait for child, cancel timeout. */
|
// Configure timeout, wait for child, cancel timeout.
|
||||||
|
|
||||||
if (exec_tmout) {
|
if (exec_tmout) {
|
||||||
|
|
||||||
@ -356,12 +386,14 @@ static void init_forkserver(char **argv) {
|
|||||||
it.it_value.tv_usec = 0;
|
it.it_value.tv_usec = 0;
|
||||||
setitimer(ITIMER_REAL, &it, NULL);
|
setitimer(ITIMER_REAL, &it, NULL);
|
||||||
|
|
||||||
/* If we have a four-byte "hello" message from the server, we're all set.
|
// If we have a four-byte "hello" message from the server, we're all set.
|
||||||
Otherwise, try to figure out what went wrong. */
|
// Otherwise, try to figure out what went wrong.
|
||||||
|
|
||||||
if (rlen == 4) {
|
if (rlen == 4) {
|
||||||
|
|
||||||
ACTF("All right - fork server is up.");
|
ACTF("All right - fork server is up.");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitpid(forksrv_pid, &status, 0) <= 0)
|
if (waitpid(forksrv_pid, &status, 0) <= 0)
|
||||||
@ -381,6 +413,7 @@ static void init_forkserver(char **argv) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
/* Execute target application. Returns 0 if the changes are a dud, or
|
/* Execute target application. Returns 0 if the changes are a dud, or
|
||||||
1 if they should be kept. */
|
1 if they should be kept. */
|
||||||
@ -388,8 +421,8 @@ static void init_forkserver(char **argv) {
|
|||||||
static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
||||||
|
|
||||||
static struct itimerval it;
|
static struct itimerval it;
|
||||||
static u32 prev_timed_out = 0;
|
static u32 prev_timed_out = 0;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
u32 cksum;
|
u32 cksum;
|
||||||
|
|
||||||
@ -423,9 +456,8 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
|||||||
|
|
||||||
if (exec_tmout) {
|
if (exec_tmout) {
|
||||||
|
|
||||||
child_timed_out = 0;
|
it.it_value.tv_sec = (exec_tmout / 1000);
|
||||||
it.it_value.tv_sec = (exec_tmout / 1000);
|
it.it_value.tv_usec = (exec_tmout % 1000) * 1000;
|
||||||
it.it_value.tv_usec = (exec_tmout % 1000) * 1000;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,7 +490,7 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
|||||||
if (stop_soon) {
|
if (stop_soon) {
|
||||||
|
|
||||||
SAYF(cRST cLRD "\n+++ Minimization aborted by user +++\n" cRST);
|
SAYF(cRST cLRD "\n+++ Minimization aborted by user +++\n" cRST);
|
||||||
close(write_to_file(out_file, in_data, in_len));
|
close(write_to_file(output_file, in_data, in_len));
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -493,9 +525,9 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
|||||||
|
|
||||||
} else
|
} else
|
||||||
|
|
||||||
/* Handle non-crashing inputs appropriately. */
|
/* Handle non-crashing inputs appropriately. */
|
||||||
|
|
||||||
if (crash_mode) {
|
if (crash_mode) {
|
||||||
|
|
||||||
missed_paths++;
|
missed_paths++;
|
||||||
return 0;
|
return 0;
|
||||||
@ -513,18 +545,17 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Find first power of two greater or equal to val. */
|
/* Find first power of two greater or equal to val. */
|
||||||
|
|
||||||
static u32 next_p2(u32 val) {
|
static u32 next_p2(u32 val) {
|
||||||
|
|
||||||
u32 ret = 1;
|
u32 ret = 1;
|
||||||
while (val > ret) ret <<= 1;
|
while (val > ret)
|
||||||
|
ret <<= 1;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Actually minimize! */
|
/* Actually minimize! */
|
||||||
|
|
||||||
static void minimize(char** argv) {
|
static void minimize(char** argv) {
|
||||||
@ -542,8 +573,8 @@ static void minimize(char** argv) {
|
|||||||
* BLOCK NORMALIZATION *
|
* BLOCK NORMALIZATION *
|
||||||
***********************/
|
***********************/
|
||||||
|
|
||||||
set_len = next_p2(in_len / TMIN_SET_STEPS);
|
set_len = next_p2(in_len / TMIN_SET_STEPS);
|
||||||
set_pos = 0;
|
set_pos = 0;
|
||||||
|
|
||||||
if (set_len < TMIN_SET_MIN_SIZE) set_len = TMIN_SET_MIN_SIZE;
|
if (set_len < TMIN_SET_MIN_SIZE) set_len = TMIN_SET_MIN_SIZE;
|
||||||
|
|
||||||
@ -551,7 +582,6 @@ static void minimize(char** argv) {
|
|||||||
|
|
||||||
while (set_pos < in_len) {
|
while (set_pos < in_len) {
|
||||||
|
|
||||||
u8 res;
|
|
||||||
u32 use_len = MIN(set_len, in_len - set_pos);
|
u32 use_len = MIN(set_len, in_len - set_pos);
|
||||||
|
|
||||||
for (i = 0; i < use_len; i++)
|
for (i = 0; i < use_len; i++)
|
||||||
@ -562,12 +592,13 @@ static void minimize(char** argv) {
|
|||||||
memcpy(tmp_buf, in_data, in_len);
|
memcpy(tmp_buf, in_data, in_len);
|
||||||
memset(tmp_buf + set_pos, '0', use_len);
|
memset(tmp_buf + set_pos, '0', use_len);
|
||||||
|
|
||||||
|
u8 res;
|
||||||
res = run_target(argv, tmp_buf, in_len, 0);
|
res = run_target(argv, tmp_buf, in_len, 0);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
||||||
memset(in_data + set_pos, '0', use_len);
|
memset(in_data + set_pos, '0', use_len);
|
||||||
changed_any = 1;
|
/* changed_any = 1; value is not used */
|
||||||
alpha_del0 += use_len;
|
alpha_del0 += use_len;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -600,11 +631,11 @@ next_pass:
|
|||||||
next_del_blksize:
|
next_del_blksize:
|
||||||
|
|
||||||
if (!del_len) del_len = 1;
|
if (!del_len) del_len = 1;
|
||||||
del_pos = 0;
|
del_pos = 0;
|
||||||
prev_del = 1;
|
prev_del = 1;
|
||||||
|
|
||||||
SAYF(cGRA " Block length = %u, remaining size = %u\n" cRST,
|
SAYF(cGRA " Block length = %u, remaining size = %u\n" cRST, del_len,
|
||||||
del_len, in_len);
|
in_len);
|
||||||
|
|
||||||
while (del_pos < in_len) {
|
while (del_pos < in_len) {
|
||||||
|
|
||||||
@ -619,8 +650,8 @@ next_del_blksize:
|
|||||||
very end of the buffer (tail_len > 0), and the current block is the same
|
very end of the buffer (tail_len > 0), and the current block is the same
|
||||||
as the previous one... skip this step as a no-op. */
|
as the previous one... skip this step as a no-op. */
|
||||||
|
|
||||||
if (!prev_del && tail_len && !memcmp(in_data + del_pos - del_len,
|
if (!prev_del && tail_len &&
|
||||||
in_data + del_pos, del_len)) {
|
!memcmp(in_data + del_pos - del_len, in_data + del_pos, del_len)) {
|
||||||
|
|
||||||
del_pos += del_len;
|
del_pos += del_len;
|
||||||
continue;
|
continue;
|
||||||
@ -641,11 +672,13 @@ next_del_blksize:
|
|||||||
|
|
||||||
memcpy(in_data, tmp_buf, del_pos + tail_len);
|
memcpy(in_data, tmp_buf, del_pos + tail_len);
|
||||||
prev_del = 1;
|
prev_del = 1;
|
||||||
in_len = del_pos + tail_len;
|
in_len = del_pos + tail_len;
|
||||||
|
|
||||||
changed_any = 1;
|
changed_any = 1;
|
||||||
|
|
||||||
} else del_pos += del_len;
|
} else
|
||||||
|
|
||||||
|
del_pos += del_len;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -659,7 +692,8 @@ next_del_blksize:
|
|||||||
OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len);
|
OKF("Block removal complete, %u bytes deleted.", stage_o_len - in_len);
|
||||||
|
|
||||||
if (!in_len && changed_any)
|
if (!in_len && changed_any)
|
||||||
WARNF(cLRD "Down to zero bytes - check the command line and mem limit!" cRST);
|
WARNF(cLRD
|
||||||
|
"Down to zero bytes - check the command line and mem limit!" cRST);
|
||||||
|
|
||||||
if (cur_pass > 1 && !changed_any) goto finalize_all;
|
if (cur_pass > 1 && !changed_any) goto finalize_all;
|
||||||
|
|
||||||
@ -667,15 +701,17 @@ next_del_blksize:
|
|||||||
* ALPHABET MINIMIZATION *
|
* ALPHABET MINIMIZATION *
|
||||||
*************************/
|
*************************/
|
||||||
|
|
||||||
alpha_size = 0;
|
alpha_size = 0;
|
||||||
alpha_del1 = 0;
|
alpha_del1 = 0;
|
||||||
syms_removed = 0;
|
syms_removed = 0;
|
||||||
|
|
||||||
memset(alpha_map, 0, sizeof(alpha_map));
|
memset(alpha_map, 0, sizeof(alpha_map));
|
||||||
|
|
||||||
for (i = 0; i < in_len; i++) {
|
for (i = 0; i < in_len; i++) {
|
||||||
|
|
||||||
if (!alpha_map[in_data[i]]) alpha_size++;
|
if (!alpha_map[in_data[i]]) alpha_size++;
|
||||||
alpha_map[in_data[i]]++;
|
alpha_map[in_data[i]]++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...",
|
ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...",
|
||||||
@ -684,7 +720,7 @@ next_del_blksize:
|
|||||||
for (i = 0; i < 256; i++) {
|
for (i = 0; i < 256; i++) {
|
||||||
|
|
||||||
u32 r;
|
u32 r;
|
||||||
u8 res;
|
u8 res;
|
||||||
|
|
||||||
if (i == '0' || !alpha_map[i]) continue;
|
if (i == '0' || !alpha_map[i]) continue;
|
||||||
|
|
||||||
@ -709,8 +745,8 @@ next_del_blksize:
|
|||||||
alpha_d_total += alpha_del1;
|
alpha_d_total += alpha_del1;
|
||||||
|
|
||||||
OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.",
|
OKF("Symbol minimization finished, %u symbol%s (%u byte%s) replaced.",
|
||||||
syms_removed, syms_removed == 1 ? "" : "s",
|
syms_removed, syms_removed == 1 ? "" : "s", alpha_del1,
|
||||||
alpha_del1, alpha_del1 == 1 ? "" : "s");
|
alpha_del1 == 1 ? "" : "s");
|
||||||
|
|
||||||
/**************************
|
/**************************
|
||||||
* CHARACTER MINIMIZATION *
|
* CHARACTER MINIMIZATION *
|
||||||
@ -737,36 +773,34 @@ next_del_blksize:
|
|||||||
alpha_del2++;
|
alpha_del2++;
|
||||||
changed_any = 1;
|
changed_any = 1;
|
||||||
|
|
||||||
} else tmp_buf[i] = orig;
|
} else
|
||||||
|
|
||||||
|
tmp_buf[i] = orig;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
alpha_d_total += alpha_del2;
|
alpha_d_total += alpha_del2;
|
||||||
|
|
||||||
OKF("Character minimization done, %u byte%s replaced.",
|
OKF("Character minimization done, %u byte%s replaced.", alpha_del2,
|
||||||
alpha_del2, alpha_del2 == 1 ? "" : "s");
|
alpha_del2 == 1 ? "" : "s");
|
||||||
|
|
||||||
if (changed_any) goto next_pass;
|
if (changed_any) goto next_pass;
|
||||||
|
|
||||||
finalize_all:
|
finalize_all:
|
||||||
|
|
||||||
SAYF("\n"
|
SAYF("\n" cGRA " File size reduced by : " cRST
|
||||||
cGRA " File size reduced by : " cRST "%0.02f%% (to %u byte%s)\n"
|
"%0.02f%% (to %u byte%s)\n" cGRA " Characters simplified : " cRST
|
||||||
cGRA " Characters simplified : " cRST "%0.02f%%\n"
|
"%0.02f%%\n" cGRA " Number of execs done : " cRST "%u\n" cGRA
|
||||||
cGRA " Number of execs done : " cRST "%u\n"
|
" Fruitless execs : " cRST "path=%u crash=%u hang=%s%u\n\n",
|
||||||
cGRA " Fruitless execs : " cRST "path=%u crash=%u hang=%s%u\n\n",
|
|
||||||
100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s",
|
100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s",
|
||||||
((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1),
|
((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), total_execs,
|
||||||
total_execs, missed_paths, missed_crashes, missed_hangs ? cLRD : "",
|
missed_paths, missed_crashes, missed_hangs ? cLRD : "", missed_hangs);
|
||||||
missed_hangs);
|
|
||||||
|
|
||||||
if (total_execs > 50 && missed_hangs * 10 > total_execs)
|
if (total_execs > 50 && missed_hangs * 10 > total_execs)
|
||||||
WARNF(cLRD "Frequent timeouts - results may be skewed." cRST);
|
WARNF(cLRD "Frequent timeouts - results may be skewed." cRST);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Handle Ctrl-C and the like. */
|
/* Handle Ctrl-C and the like. */
|
||||||
|
|
||||||
static void handle_stop_sig(int sig) {
|
static void handle_stop_sig(int sig) {
|
||||||
@ -777,7 +811,6 @@ static void handle_stop_sig(int sig) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Do basic preparations - persistent fds, filenames, etc. */
|
/* Do basic preparations - persistent fds, filenames, etc. */
|
||||||
|
|
||||||
static void set_up_environment(void) {
|
static void set_up_environment(void) {
|
||||||
@ -787,7 +820,7 @@ static void set_up_environment(void) {
|
|||||||
dev_null_fd = open("/dev/null", O_RDWR);
|
dev_null_fd = open("/dev/null", O_RDWR);
|
||||||
if (dev_null_fd < 0) PFATAL("Unable to open /dev/null");
|
if (dev_null_fd < 0) PFATAL("Unable to open /dev/null");
|
||||||
|
|
||||||
if (!prog_in) {
|
if (!out_file) {
|
||||||
|
|
||||||
u8* use_dir = ".";
|
u8* use_dir = ".";
|
||||||
|
|
||||||
@ -798,16 +831,15 @@ static void set_up_environment(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prog_in = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid());
|
out_file = alloc_printf("%s/.afl-tmin-temp-%u", use_dir, getpid());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unlink(prog_in);
|
unlink(out_file);
|
||||||
|
|
||||||
prog_in_fd = open(prog_in, O_RDWR | O_CREAT | O_EXCL, 0600);
|
out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||||
|
|
||||||
if (prog_in_fd < 0) PFATAL("Unable to create '%s'", prog_in);
|
|
||||||
|
|
||||||
|
if (out_fd < 0) PFATAL("Unable to create '%s'", out_file);
|
||||||
|
|
||||||
/* Set sane defaults... */
|
/* Set sane defaults... */
|
||||||
|
|
||||||
@ -828,18 +860,20 @@ static void set_up_environment(void) {
|
|||||||
if (x) {
|
if (x) {
|
||||||
|
|
||||||
if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR)))
|
if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR)))
|
||||||
FATAL("Custom MSAN_OPTIONS set without exit_code="
|
FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(
|
||||||
STRINGIFY(MSAN_ERROR) " - please fix!");
|
MSAN_ERROR) " - please fix!");
|
||||||
|
|
||||||
if (!strstr(x, "symbolize=0"))
|
if (!strstr(x, "symbolize=0"))
|
||||||
FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");
|
FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setenv("ASAN_OPTIONS", "abort_on_error=1:"
|
setenv("ASAN_OPTIONS",
|
||||||
"detect_leaks=0:"
|
"abort_on_error=1:"
|
||||||
"symbolize=0:"
|
"detect_leaks=0:"
|
||||||
"allocator_may_return_null=1", 0);
|
"symbolize=0:"
|
||||||
|
"allocator_may_return_null=1",
|
||||||
|
0);
|
||||||
|
|
||||||
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
||||||
"symbolize=0:"
|
"symbolize=0:"
|
||||||
@ -848,21 +882,22 @@ static void set_up_environment(void) {
|
|||||||
"msan_track_origins=0", 0);
|
"msan_track_origins=0", 0);
|
||||||
|
|
||||||
if (getenv("AFL_PRELOAD")) {
|
if (getenv("AFL_PRELOAD")) {
|
||||||
|
|
||||||
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
|
||||||
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Setup signal handlers, duh. */
|
/* Setup signal handlers, duh. */
|
||||||
|
|
||||||
static void setup_signal_handlers(void) {
|
static void setup_signal_handlers(void) {
|
||||||
|
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
sa.sa_handler = NULL;
|
sa.sa_handler = NULL;
|
||||||
sa.sa_flags = SA_RESTART;
|
sa.sa_flags = SA_RESTART;
|
||||||
sa.sa_sigaction = NULL;
|
sa.sa_sigaction = NULL;
|
||||||
|
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
@ -881,46 +916,46 @@ static void setup_signal_handlers(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Display usage hints. */
|
/* Display usage hints. */
|
||||||
|
|
||||||
static void usage(u8* argv0) {
|
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 shrunk by the tool\n"
|
" -i file - input test case to be shrunk by the tool\n"
|
||||||
" -o file - final output location for the minimized data\n\n"
|
" -o file - final output location for the minimized data\n\n"
|
||||||
|
|
||||||
"Execution control settings:\n\n"
|
"Execution control settings:\n\n"
|
||||||
|
|
||||||
" -f file - input file read by the tested program (stdin)\n"
|
" -f file - input file read by the tested program (stdin)\n"
|
||||||
" -t msec - timeout for each run (%u ms)\n"
|
" -t msec - timeout for each run (%d ms)\n"
|
||||||
" -m megs - memory limit for child process (%u MB)\n"
|
" -m megs - memory limit for child process (%d MB)\n"
|
||||||
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||||
" -U - use Unicorn-based instrumentation (Unicorn mode)\n\n"
|
" -U - use Unicorn-based instrumentation (Unicorn mode)\n\n"
|
||||||
" (Not necessary, here for consistency with other afl-* tools)\n\n"
|
" (Not necessary, here for consistency with other afl-* "
|
||||||
|
"tools)\n\n"
|
||||||
|
|
||||||
"Minimization settings:\n\n"
|
"Minimization settings:\n\n"
|
||||||
|
|
||||||
" -e - solve for edge coverage only, ignore hit counts\n"
|
" -e - solve for edge coverage only, ignore hit counts\n"
|
||||||
" -x - treat non-zero exit codes as crashes\n\n"
|
" -x - treat non-zero exit codes as crashes\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);
|
exit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Find binary. */
|
/* Find binary. */
|
||||||
|
|
||||||
static void find_binary(u8* fname) {
|
static void find_binary(u8* fname) {
|
||||||
|
|
||||||
u8* env_path = 0;
|
u8* env_path = 0;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
|
if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
|
||||||
@ -943,7 +978,9 @@ static void find_binary(u8* fname) {
|
|||||||
memcpy(cur_elem, env_path, delim - env_path);
|
memcpy(cur_elem, env_path, delim - env_path);
|
||||||
delim++;
|
delim++;
|
||||||
|
|
||||||
} else cur_elem = ck_strdup(env_path);
|
} else
|
||||||
|
|
||||||
|
cur_elem = ck_strdup(env_path);
|
||||||
|
|
||||||
env_path = delim;
|
env_path = delim;
|
||||||
|
|
||||||
@ -955,7 +992,8 @@ static void find_binary(u8* fname) {
|
|||||||
ck_free(cur_elem);
|
ck_free(cur_elem);
|
||||||
|
|
||||||
if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
|
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);
|
ck_free(target_path);
|
||||||
target_path = 0;
|
target_path = 0;
|
||||||
@ -968,13 +1006,12 @@ static void find_binary(u8* fname) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Fix up argv for QEMU. */
|
/* Fix up argv for QEMU. */
|
||||||
|
|
||||||
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
||||||
|
|
||||||
char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
|
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);
|
memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc);
|
||||||
|
|
||||||
@ -989,8 +1026,7 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
|||||||
|
|
||||||
cp = alloc_printf("%s/afl-qemu-trace", tmp);
|
cp = alloc_printf("%s/afl-qemu-trace", tmp);
|
||||||
|
|
||||||
if (access(cp, X_OK))
|
if (access(cp, X_OK)) FATAL("Unable to find '%s'", tmp);
|
||||||
FATAL("Unable to find '%s'", tmp);
|
|
||||||
|
|
||||||
target_path = new_argv[0] = cp;
|
target_path = new_argv[0] = cp;
|
||||||
return new_argv;
|
return new_argv;
|
||||||
@ -1014,7 +1050,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)) {
|
if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {
|
||||||
|
|
||||||
@ -1041,8 +1079,6 @@ static void read_bitmap(u8* fname) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Main entry point */
|
/* Main entry point */
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
@ -1055,7 +1091,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
SAYF(cCYA "afl-tmin" VERSION cRST " by <lcamtuf@google.com>\n");
|
SAYF(cCYA "afl-tmin" VERSION cRST " by <lcamtuf@google.com>\n");
|
||||||
|
|
||||||
while ((opt = getopt(argc,argv,"+i:o:f:m:t:B:xeQU")) > 0)
|
while ((opt = getopt(argc, argv, "+i:o:f:m:t:B:xeQUh")) > 0)
|
||||||
|
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
|
|
||||||
@ -1067,15 +1103,15 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
case 'o':
|
case 'o':
|
||||||
|
|
||||||
if (out_file) FATAL("Multiple -o options not supported");
|
if (output_file) FATAL("Multiple -o options not supported");
|
||||||
out_file = optarg;
|
output_file = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'f':
|
case 'f':
|
||||||
|
|
||||||
if (prog_in) FATAL("Multiple -f options not supported");
|
if (out_file) FATAL("Multiple -f options not supported");
|
||||||
use_stdin = 0;
|
use_stdin = 0;
|
||||||
prog_in = optarg;
|
out_file = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'e':
|
case 'e':
|
||||||
@ -1092,40 +1128,41 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
case 'm': {
|
case 'm': {
|
||||||
|
|
||||||
u8 suffix = 'M';
|
u8 suffix = 'M';
|
||||||
|
|
||||||
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
if (mem_limit_given) FATAL("Multiple -m options not supported");
|
||||||
mem_limit_given = 1;
|
mem_limit_given = 1;
|
||||||
|
|
||||||
if (!strcmp(optarg, "none")) {
|
if (!strcmp(optarg, "none")) {
|
||||||
|
|
||||||
mem_limit = 0;
|
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;
|
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':
|
case 't':
|
||||||
|
|
||||||
@ -1155,7 +1192,7 @@ int main(int argc, char** argv) {
|
|||||||
unicorn_mode = 1;
|
unicorn_mode = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'B': /* load bitmap */
|
case 'B': /* load bitmap */
|
||||||
|
|
||||||
/* This is a secret undocumented option! It is speculated to be useful
|
/* This is a secret undocumented option! It is speculated to be useful
|
||||||
if you have a baseline "boring" input file and another "interesting"
|
if you have a baseline "boring" input file and another "interesting"
|
||||||
@ -1175,13 +1212,16 @@ int main(int argc, char** argv) {
|
|||||||
read_bitmap(optarg);
|
read_bitmap(optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case 'h':
|
||||||
|
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: usage(argv[0]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind == argc || !in_file || !out_file) usage(argv[0]);
|
if (optind == argc || !in_file || !output_file) usage(argv[0]);
|
||||||
|
|
||||||
setup_shm(0);
|
setup_shm(0);
|
||||||
atexit(at_exit_handler);
|
atexit(at_exit_handler);
|
||||||
@ -1190,7 +1230,7 @@ int main(int argc, char** argv) {
|
|||||||
set_up_environment();
|
set_up_environment();
|
||||||
|
|
||||||
find_binary(argv[optind]);
|
find_binary(argv[optind]);
|
||||||
detect_file_args(argv + optind, prog_in);
|
detect_file_args(argv + optind, out_file);
|
||||||
|
|
||||||
if (qemu_mode)
|
if (qemu_mode)
|
||||||
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
||||||
@ -1215,26 +1255,27 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
if (!crash_mode) {
|
if (!crash_mode) {
|
||||||
|
|
||||||
OKF("Program terminates normally, minimizing in "
|
OKF("Program terminates normally, minimizing in " cCYA "instrumented" cRST
|
||||||
cCYA "instrumented" cRST " mode.");
|
" mode.");
|
||||||
|
|
||||||
if (!anything_set()) FATAL("No instrumentation detected.");
|
if (!anything_set()) FATAL("No instrumentation detected.");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
OKF("Program exits with a signal, minimizing in " cMGN "%scrash" cRST
|
OKF("Program exits with a signal, minimizing in " cMGN "%scrash" cRST
|
||||||
" mode.", exact_mode ? "EXACT " : "");
|
" mode.",
|
||||||
|
exact_mode ? "EXACT " : "");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
minimize(use_argv);
|
minimize(use_argv);
|
||||||
|
|
||||||
ACTF("Writing output to '%s'...", out_file);
|
ACTF("Writing output to '%s'...", output_file);
|
||||||
|
|
||||||
unlink(prog_in);
|
unlink(out_file);
|
||||||
prog_in = NULL;
|
out_file = NULL;
|
||||||
|
|
||||||
close(write_to_file(out_file, in_data, in_len));
|
close(write_to_file(output_file, in_data, in_len));
|
||||||
|
|
||||||
OKF("We're done here. Have a nice day!\n");
|
OKF("We're done here. Have a nice day!\n");
|
||||||
|
|
10
test-instr.c
10
test-instr.c
@ -20,11 +20,16 @@
|
|||||||
|
|
||||||
int main(int argc, char** argv) {
|
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");
|
printf("Hum?\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf[0] == '0')
|
if (buf[0] == '0')
|
||||||
@ -37,3 +42,4 @@ int main(int argc, char** argv) {
|
|||||||
exit(0);
|
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
|
||||||
__ _ _
|
|
||||||
__ _ / _| | _ _ _ __ (_) ___ ___ _ __ _ __
|
|
||||||
/ _` | |_| |___| | | | '_ \| |/ __/ _ \| '__| '_ \
|
|
||||||
| (_| | _| |___| |_| | | | | | (_| (_) | | | | | |
|
|
||||||
\__,_|_| |_| \__,_|_| |_|_|\___\___/|_| |_| |_|
|
|
||||||
|
|
||||||
```
|
The idea and much of the original implementation comes from Nathan Voss <njvoss299@gmail.com>.
|
||||||
|
|
||||||
afl-unicorn lets you fuzz any piece of binary that can be emulated by
|
The port to afl++ if by Dominik Maier <mail@dmnk.co>.
|
||||||
[Unicorn Engine](http://www.unicorn-engine.org/).
|
|
||||||
|
|
||||||
Requirements: Python2
|
The CompareCoverage and NeverZero counters features by Andrea Fioraldi <andreafioraldi@gmail.com>.
|
||||||
|
|
||||||
For the full readme please see docs/unicorn_mode.txt
|
## 1) Introduction
|
||||||
|
|
||||||
For an in-depth description of what this is, how to install it, and how to use
|
The code in ./unicorn_mode allows you to build a standalone feature that
|
||||||
it check out this [blog post](https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf).
|
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.
|
||||||
|
|
||||||
For general help with AFL, please refer to the documents in the ./docs/ directory.
|
There is a significant performance penalty compared to native AFL,
|
||||||
|
but at least we're able to use AFL on these binaries, right?
|
||||||
|
|
||||||
Created by Nathan Voss, originally funded by
|
## 2) How to use
|
||||||
[Battelle](https://www.battelle.org/cyber).
|
|
||||||
|
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
|
#!/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
|
# Adapted from code by Andrew Griffiths <agriffiths@google.com> and
|
||||||
# Michal Zalewski <lcamtuf@google.com>
|
# 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 2017 Battelle Memorial Institute. All rights reserved.
|
||||||
|
# Copyright 2019 AFLplusplus Project. All rights reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -127,12 +131,13 @@ tar xzf "$ARCHIVE" -C ./unicorn --strip-components=1 || exit 1
|
|||||||
|
|
||||||
echo "[+] Unpacking successful."
|
echo "[+] Unpacking successful."
|
||||||
|
|
||||||
rm -rf "$ARCHIVE" || exit 1
|
#rm -rf "$ARCHIVE" || exit 1
|
||||||
|
|
||||||
echo "[*] Applying patches..."
|
echo "[*] Applying patches..."
|
||||||
|
|
||||||
cp patches/afl-unicorn-cpu-inl.h unicorn || exit 1
|
cp patches/*.h unicorn || exit 1
|
||||||
patch -p1 --directory unicorn <patches/patches.diff || exit 1
|
patch -p1 --directory unicorn < patches/patches.diff || exit 1
|
||||||
|
patch -p1 --directory unicorn < patches/compcov.diff || exit 1
|
||||||
|
|
||||||
echo "[+] Patching done."
|
echo "[+] Patching done."
|
||||||
|
|
||||||
@ -144,7 +149,7 @@ echo "[+] Configuration complete."
|
|||||||
|
|
||||||
echo "[*] Attempting to build Unicorn (fingers crossed!)..."
|
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!"
|
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
|
Originally written by Andrew Griffiths <agriffiths@google.com> and
|
||||||
Michal Zalewski <lcamtuf@google.com>
|
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>
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -24,7 +24,7 @@
|
|||||||
to implement AFL-style instrumentation and to take care of the remaining
|
to implement AFL-style instrumentation and to take care of the remaining
|
||||||
parts of the AFL fork server logic.
|
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
|
tool; for an example of how to leverage it for other purposes, you can
|
||||||
have a look at afl-showmap.c.
|
have a look at afl-showmap.c.
|
||||||
|
|
||||||
@ -33,7 +33,7 @@
|
|||||||
#include <sys/shm.h>
|
#include <sys/shm.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include "../../config.h"
|
#include "afl-unicorn-common.h"
|
||||||
|
|
||||||
/***************************
|
/***************************
|
||||||
* VARIOUS AUXILIARY STUFF *
|
* VARIOUS AUXILIARY STUFF *
|
||||||
@ -44,21 +44,29 @@
|
|||||||
it to translate within its own context, too (this avoids translation
|
it to translate within its own context, too (this avoids translation
|
||||||
overhead in the next forked-off copy). */
|
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); \
|
afl_request_tsl(pc, cs_base, flags); \
|
||||||
|
\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* This snippet kicks in when the instruction pointer is positioned at
|
/* This snippet kicks in when the instruction pointer is positioned at
|
||||||
_start and does the usual forkserver stuff, not very different from
|
_start and does the usual forkserver stuff, not very different from
|
||||||
regular instrumentation injected via afl-as.h. */
|
regular instrumentation injected via afl-as.h. */
|
||||||
|
|
||||||
#define AFL_UNICORN_CPU_SNIPPET2 do { \
|
#define AFL_UNICORN_CPU_SNIPPET2 \
|
||||||
if(unlikely(afl_first_instr == 0)) { \
|
do { \
|
||||||
afl_setup(); \
|
\
|
||||||
afl_forkserver(env); \
|
if (unlikely(afl_first_instr == 0)) { \
|
||||||
afl_first_instr = 1; \
|
\
|
||||||
} \
|
afl_setup(env->uc); \
|
||||||
afl_maybe_log(tb->pc); \
|
afl_forkserver(env); \
|
||||||
|
afl_first_instr = 1; \
|
||||||
|
\
|
||||||
|
} \
|
||||||
|
afl_maybe_log(env->uc, tb->pc); \
|
||||||
|
\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* We use one additional file descriptor to relay "needs translation"
|
/* We use one additional file descriptor to relay "needs translation"
|
||||||
@ -66,37 +74,31 @@
|
|||||||
|
|
||||||
#define TSL_FD (FORKSRV_FD - 1)
|
#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: */
|
/* Set in the child process in forkserver mode: */
|
||||||
|
|
||||||
static unsigned char afl_fork_child;
|
static unsigned char afl_fork_child;
|
||||||
static unsigned int afl_forksrv_pid;
|
static unsigned int afl_forksrv_pid;
|
||||||
|
|
||||||
/* Instrumentation ratio: */
|
|
||||||
|
|
||||||
static unsigned int afl_inst_rms = MAP_SIZE;
|
|
||||||
|
|
||||||
/* Function declarations. */
|
/* Function declarations. */
|
||||||
|
|
||||||
static void afl_setup(void);
|
static void afl_setup(struct uc_struct* uc);
|
||||||
static void afl_forkserver(CPUArchState*);
|
static void afl_forkserver(CPUArchState*);
|
||||||
static inline void afl_maybe_log(unsigned long);
|
static inline void afl_maybe_log(struct uc_struct* uc, unsigned long);
|
||||||
|
|
||||||
static void afl_wait_tsl(CPUArchState*, int);
|
static void afl_wait_tsl(CPUArchState*, int);
|
||||||
static void afl_request_tsl(target_ulong, target_ulong, uint64_t);
|
static void afl_request_tsl(target_ulong, target_ulong, uint64_t);
|
||||||
|
|
||||||
static TranslationBlock *tb_find_slow(CPUArchState*, target_ulong,
|
static TranslationBlock* tb_find_slow(CPUArchState*, target_ulong, target_ulong,
|
||||||
target_ulong, uint64_t);
|
uint64_t);
|
||||||
|
|
||||||
/* Data structure passed around by the translate handlers: */
|
/* Data structure passed around by the translate handlers: */
|
||||||
|
|
||||||
struct afl_tsl {
|
struct afl_tsl {
|
||||||
|
|
||||||
target_ulong pc;
|
target_ulong pc;
|
||||||
target_ulong cs_base;
|
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. */
|
/* 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),
|
char *id_str = getenv(SHM_ENV_VAR), *inst_r = getenv("AFL_INST_RATIO");
|
||||||
*inst_r = getenv("AFL_INST_RATIO");
|
|
||||||
|
|
||||||
int shm_id;
|
int shm_id;
|
||||||
|
|
||||||
@ -121,31 +122,45 @@ static void afl_setup(void) {
|
|||||||
if (r > 100) r = 100;
|
if (r > 100) r = 100;
|
||||||
if (!r) r = 1;
|
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) {
|
if (id_str) {
|
||||||
|
|
||||||
shm_id = atoi(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
|
/* 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. */
|
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. */
|
/* 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];
|
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
|
/* Tell the parent that we're alive. If the parent doesn't want
|
||||||
to talk, assume that we're not running in forkserver mode. */
|
to talk, assume that we're not running in forkserver mode. */
|
||||||
@ -159,7 +174,7 @@ static void afl_forkserver(CPUArchState *env) {
|
|||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
pid_t child_pid;
|
pid_t child_pid;
|
||||||
int status, t_fd[2];
|
int status, t_fd[2];
|
||||||
|
|
||||||
/* Whoops, parent dead? */
|
/* Whoops, parent dead? */
|
||||||
|
|
||||||
@ -205,48 +220,36 @@ static void afl_forkserver(CPUArchState *env) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* The equivalent of the tuple logging routine from afl-as.h. */
|
/* 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;
|
static __thread unsigned long prev_loc;
|
||||||
|
|
||||||
// DEBUG
|
u8* afl_area_ptr = uc->afl_area_ptr;
|
||||||
//printf("IN AFL_MAYBE_LOG 0x%lx\n", cur_loc);
|
|
||||||
|
|
||||||
// MODIFIED FOR UNICORN MODE -> We want to log all addresses,
|
if (!afl_area_ptr) return;
|
||||||
// so the checks for 'start < addr < end' are removed
|
|
||||||
if(!afl_area_ptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// DEBUG
|
|
||||||
//printf("afl_area_ptr = %p\n", afl_area_ptr);
|
|
||||||
|
|
||||||
/* Looks like QEMU always maps to fixed locations, so ASAN is not a
|
/* Looks like QEMU always maps to fixed locations, so ASAN is not a
|
||||||
concern. Phew. But instruction addresses may be aligned. Let's mangle
|
concern. Phew. But instruction addresses may be aligned. Let's mangle
|
||||||
the value to get something quasi-uniform. */
|
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;
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
/* Implement probabilistic instrumentation by looking at scrambled block
|
/* Implement probabilistic instrumentation by looking at scrambled block
|
||||||
address. This keeps the instrumented locations stable across runs. */
|
address. This keeps the instrumented locations stable across runs. */
|
||||||
|
|
||||||
// DEBUG
|
if (cur_loc >= uc->afl_inst_rms) return;
|
||||||
//printf("afl_inst_rms = 0x%lx\n", afl_inst_rms);
|
|
||||||
|
|
||||||
if (cur_loc >= afl_inst_rms) return;
|
register uintptr_t afl_idx = cur_loc ^ prev_loc;
|
||||||
|
|
||||||
// DEBUG
|
INC_AFL_AREA(afl_idx);
|
||||||
//printf("cur_loc = 0x%lx\n", cur_loc);
|
|
||||||
|
|
||||||
afl_area_ptr[cur_loc ^ prev_loc]++;
|
|
||||||
prev_loc = cur_loc >> 1;
|
prev_loc = cur_loc >> 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* This code is invoked whenever QEMU decides that it doesn't have a
|
/* 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,
|
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
|
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;
|
if (!afl_fork_child) return;
|
||||||
|
|
||||||
t.pc = pc;
|
t.pc = pc;
|
||||||
t.cs_base = cb;
|
t.cs_base = cb;
|
||||||
t.flags = flags;
|
t.flags = flags;
|
||||||
|
|
||||||
if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* This is the other side of the same channel. Since timeouts are handled by
|
/* 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. */
|
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;
|
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. */
|
/* 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))
|
if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) break;
|
||||||
break;
|
|
||||||
|
|
||||||
tb_find_slow(env, t.pc, t.cs_base, t.flags);
|
tb_find_slow(env, t.pc, t.cs_base, t.flags);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(fd);
|
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