mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-24 22:53:24 +00:00
Compare commits
244 Commits
Author | SHA1 | Date | |
---|---|---|---|
23f7bee81c | |||
ac795ae1e1 | |||
aa6a50c2b4 | |||
61c8304f24 | |||
01ad0f3c6a | |||
db342c9c81 | |||
f1d8a01047 | |||
3753f56c25 | |||
d32b1d6b0c | |||
e5bdba4b9f | |||
c725cb71de | |||
8ff5063545 | |||
e549102563 | |||
b7d90a9e31 | |||
adeb0d18b1 | |||
16e3e2a7f7 | |||
0484d9b024 | |||
d60bbff0d9 | |||
966eba50a6 | |||
ffd8fae22a | |||
cb0fe044dd | |||
4f3b5f8adc | |||
3bb12b456f | |||
a10a627622 | |||
2777784f4f | |||
4bd0d4cbaf | |||
2d92bb483e | |||
071edb1a2d | |||
7f36290703 | |||
6cc59a38be | |||
87eb44abe4 | |||
a0c3011673 | |||
69f3095045 | |||
d678d59372 | |||
b2feada293 | |||
bff02dae0d | |||
851231c846 | |||
f21a5c42c1 | |||
d471fc9509 | |||
9f22a151f3 | |||
0c2478cae6 | |||
b865fc6080 | |||
d44b650cd4 | |||
aa12e46013 | |||
2c18fbbb2e | |||
4131965d48 | |||
5fab0fa51f | |||
9c9232b4e8 | |||
0d6e571237 | |||
40ba8814b3 | |||
a0a917ad87 | |||
8628839c55 | |||
5001779984 | |||
93d91dd7a1 | |||
791c5c171d | |||
a723156740 | |||
0868ea8348 | |||
5a6ad71f3f | |||
47f2650a32 | |||
74a6044b3f | |||
e82ce95251 | |||
e226d1bbb3 | |||
54c1087340 | |||
eb1e8619eb | |||
c96fca6833 | |||
976cb3e36c | |||
9b3d8c327d | |||
7b907e45ad | |||
e8d580f54d | |||
2dd5a02061 | |||
9844e1a856 | |||
44be521ab8 | |||
c9819e3b94 | |||
16d6f35aa6 | |||
c9854ec8cb | |||
c429021de1 | |||
41ad23041b | |||
3e5ac0af52 | |||
f848562732 | |||
3342aa751d | |||
a2f40aa285 | |||
f34a860d5f | |||
af9aeb89d4 | |||
be5274d4a9 | |||
b6dc529bc3 | |||
0aa93afeb8 | |||
79d75d8e42 | |||
96c526cb78 | |||
02f3319256 | |||
8f538e77ed | |||
1e76079e93 | |||
0e736276e6 | |||
08ef8d6b78 | |||
3977d50b55 | |||
3bcfbf5038 | |||
f0c7967fbf | |||
8bdb40b763 | |||
f0bc2e0e8b | |||
108e588e88 | |||
333509bb0a | |||
c269c3977c | |||
c3a8052a16 | |||
d0a61279b8 | |||
7259075b71 | |||
14fd477147 | |||
05e2f577f6 | |||
a29b360d55 | |||
75d6a8b701 | |||
ad7a7fcf07 | |||
82c05630ba | |||
bd0a23de73 | |||
4619a1395b | |||
0c38850f95 | |||
07884e0054 | |||
bdadbb7207 | |||
e389eb9842 | |||
5cf0655071 | |||
f81ef4abf4 | |||
6036cf8437 | |||
1cad645400 | |||
36846836ed | |||
79f1a44a01 | |||
c2127e3ff7 | |||
2ad495ad0a | |||
8e051fd075 | |||
af628b16d1 | |||
c219502f0f | |||
a5da9ce42c | |||
79e02c2a9b | |||
3a461944ec | |||
78d96c4dc8 | |||
ee0ca07f3c | |||
7ae7b0f373 | |||
e2b4bc9310 | |||
6c9777de13 | |||
2f7e57f6aa | |||
5c239451cf | |||
35ca51c5a8 | |||
047f3436e9 | |||
5d181950eb | |||
48a1a29baa | |||
c05d392cd9 | |||
cc7c651dc9 | |||
e6ef2ee338 | |||
a090b2013f | |||
564f491566 | |||
2daeeab844 | |||
4ab90e739f | |||
745bc083d1 | |||
7674dac1a1 | |||
fb2a6b6941 | |||
70fe872940 | |||
a252943236 | |||
8c133b607c | |||
2785c8b197 | |||
a81b5aa921 | |||
8ad78f5b65 | |||
ac9cfd89da | |||
c67c4ce757 | |||
974aab6cf6 | |||
b957218a3a | |||
f629f4e341 | |||
871c3c91ec | |||
100aac4dd3 | |||
d941da33ae | |||
62767a42dc | |||
89cf94f0e6 | |||
17211253b2 | |||
6998489b26 | |||
4290cb5877 | |||
801f2449ec | |||
aaf5fcd98a | |||
5edfb7ba85 | |||
a5cb522f01 | |||
3195119dad | |||
d6fe6b9537 | |||
c0f9fba6d6 | |||
1a713ff420 | |||
89af2ef7a9 | |||
907c5d4276 | |||
5dd35f5281 | |||
857229654e | |||
4c47b242eb | |||
938512a6b9 | |||
7444cfa450 | |||
f091b8d692 | |||
7d97ffb1e8 | |||
80bdbf7be0 | |||
686719cdca | |||
6caec2169c | |||
5212481352 | |||
d999725de2 | |||
145c673a80 | |||
c5017945f7 | |||
5c4c49d9ca | |||
cebde1f9e6 | |||
0298ae82b0 | |||
512f53984c | |||
e3a5c31307 | |||
dd2fd80274 | |||
ffc1fc655f | |||
fe477e96ae | |||
98559ea8b0 | |||
f31d8b8401 | |||
389e348826 | |||
98fd50f78f | |||
95561ec5a7 | |||
fe9da70705 | |||
95c77c8486 | |||
e45333bcf9 | |||
c906c042be | |||
9bd1e19d7f | |||
6ce9230ed6 | |||
1d60c39191 | |||
70651d60bd | |||
385312c658 | |||
87a607c7d0 | |||
1ba5d1008e | |||
129a5adaf1 | |||
d827bc4580 | |||
64e46dcefc | |||
c0b3127b9d | |||
7cfa690d1c | |||
22a3c7f7d0 | |||
16ffbb37f5 | |||
ea05f3f4cd | |||
91f2f057e4 | |||
d44cf1344d | |||
756206e4d7 | |||
2ff6e5023f | |||
223bd70f1f | |||
dd3f4bb41c | |||
f3e783d343 | |||
f4cac37b04 | |||
5b2634f711 | |||
267b085f80 | |||
b6643743d6 | |||
17cbb03ba7 | |||
2cd4624779 | |||
e11665564b | |||
93cebd6c7f | |||
a124540e50 | |||
c465e48e27 | |||
c2c65fd9c1 |
@ -33,13 +33,13 @@ if CLANG_FORMAT_BIN is None:
|
||||
o, _ = p.communicate()
|
||||
o = str(o, "utf-8")
|
||||
o = re.sub(r".*ersion ", "", o)
|
||||
#o = o[len("clang-format version "):].strip()
|
||||
o = o[:o.find(".")]
|
||||
# o = o[len("clang-format version "):].strip()
|
||||
o = o[: o.find(".")]
|
||||
o = int(o)
|
||||
except:
|
||||
print ("clang-format-11 is needed. Aborted.")
|
||||
print("clang-format-11 is needed. Aborted.")
|
||||
exit(1)
|
||||
#if o < 7:
|
||||
# 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:
|
||||
@ -52,8 +52,8 @@ if CLANG_FORMAT_BIN is None:
|
||||
# print ("clang-format 7 or above is needed. Aborted.")
|
||||
# exit(1)
|
||||
else:
|
||||
CLANG_FORMAT_BIN = 'clang-format-11'
|
||||
|
||||
CLANG_FORMAT_BIN = "clang-format-11"
|
||||
|
||||
COLUMN_LIMIT = 80
|
||||
for line in fmt.split("\n"):
|
||||
line = line.split(":")
|
||||
@ -69,26 +69,47 @@ def custom_format(filename):
|
||||
in_define = False
|
||||
last_line = None
|
||||
out = ""
|
||||
|
||||
|
||||
for line in src.split("\n"):
|
||||
if line.lstrip().startswith("#"):
|
||||
if line[line.find("#")+1:].lstrip().startswith("define"):
|
||||
if line[line.find("#") + 1 :].lstrip().startswith("define"):
|
||||
in_define = True
|
||||
|
||||
if "/*" in line and not line.strip().startswith("/*") and line.endswith("*/") and len(line) < (COLUMN_LIMIT-2):
|
||||
|
||||
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:]
|
||||
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:]))
|
||||
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() != "":
|
||||
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() != "":
|
||||
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() != "":
|
||||
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("\\"):
|
||||
@ -97,14 +118,15 @@ def custom_format(filename):
|
||||
out += line + "\n"
|
||||
last_line = line
|
||||
|
||||
return (out)
|
||||
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("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)
|
||||
|
||||
@ -120,4 +142,3 @@ for filename in args:
|
||||
f.write(code)
|
||||
else:
|
||||
print(code)
|
||||
|
||||
|
27
.github/workflows/build_aflplusplus_docker.yaml
vendored
Normal file
27
.github/workflows/build_aflplusplus_docker.yaml
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
name: Publish Docker Images
|
||||
on:
|
||||
push:
|
||||
branches: [ stable ]
|
||||
paths:
|
||||
- Dockerfile
|
||||
pull_request:
|
||||
branches: [ stable ]
|
||||
paths:
|
||||
- Dockerfile
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker images to Dockerhub
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Login to Dockerhub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
- name: Publish aflpp to Registry
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: aflplusplus/aflplusplus:latest
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -3,8 +3,8 @@ name: CI
|
||||
on:
|
||||
push:
|
||||
branches: [ stable, dev ]
|
||||
pull_request:
|
||||
branches: [ stable, dev ]
|
||||
# pull_request:
|
||||
# branches: [ stable, dev ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@ -3,8 +3,8 @@ name: "CodeQL"
|
||||
on:
|
||||
push:
|
||||
branches: [ stable, dev ]
|
||||
pull_request:
|
||||
branches: [ stable, dev ]
|
||||
# pull_request:
|
||||
# branches: [ stable, dev ]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
|
30
.github/workflows/rust_custom_mutator.yml
vendored
Normal file
30
.github/workflows/rust_custom_mutator.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: Rust Custom Mutators
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ stable, dev ]
|
||||
pull_request:
|
||||
branches: [ stable, dev ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test Rust Custom Mutator Support
|
||||
runs-on: '${{ matrix.os }}'
|
||||
defaults:
|
||||
run:
|
||||
working-directory: custom_mutators/rust
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust Toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Check Code Compiles
|
||||
run: cargo check
|
||||
- name: Run General Tests
|
||||
run: cargo test
|
||||
- name: Run Tests for afl_internals feature flag
|
||||
run: cd custom_mutator && cargo test --features=afl_internals
|
10
Dockerfile
10
Dockerfile
@ -11,6 +11,8 @@ LABEL "about"="AFLplusplus docker image"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
env NO_ARCH_OPT 1
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get -y install --no-install-suggests --no-install-recommends \
|
||||
automake \
|
||||
@ -48,16 +50,16 @@ RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 0
|
||||
|
||||
ENV LLVM_CONFIG=llvm-config-12
|
||||
ENV AFL_SKIP_CPUFREQ=1
|
||||
ENV AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1
|
||||
|
||||
RUN git clone https://github.com/vanhauser-thc/afl-cov /afl-cov
|
||||
RUN git clone --depth=1 https://github.com/vanhauser-thc/afl-cov /afl-cov
|
||||
RUN cd /afl-cov && make install && cd ..
|
||||
|
||||
COPY . /AFLplusplus
|
||||
WORKDIR /AFLplusplus
|
||||
|
||||
RUN export REAL_CXX=g++-10 && export CC=gcc-10 && \
|
||||
export CXX=g++-10 && make clean && \
|
||||
make distrib CFLAGS="-O3 -funroll-loops -D_FORTIFY_SOURCE=2" && make install && make clean
|
||||
RUN export CC=gcc-10 && export CXX=g++-10 && make clean && \
|
||||
make distrib && make install && make clean
|
||||
|
||||
RUN echo 'alias joe="jupp --wordwrap"' >> ~/.bashrc
|
||||
RUN echo 'export PS1="[afl++]$PS1"' >> ~/.bashrc
|
||||
|
24
GNUmakefile
24
GNUmakefile
@ -24,7 +24,7 @@ BIN_PATH = $(PREFIX)/bin
|
||||
HELPER_PATH = $(PREFIX)/lib/afl
|
||||
DOC_PATH = $(PREFIX)/share/doc/afl
|
||||
MISC_PATH = $(PREFIX)/share/afl
|
||||
MAN_PATH = $(PREFIX)/man/man8
|
||||
MAN_PATH = $(PREFIX)/share/man/man8
|
||||
|
||||
PROGNAME = afl
|
||||
VERSION = $(shell grep '^$(HASH)define VERSION ' ../config.h | cut -d '"' -f2)
|
||||
@ -57,8 +57,6 @@ ifdef MSAN_BUILD
|
||||
override LDFLAGS += -fsanitize=memory
|
||||
endif
|
||||
|
||||
|
||||
|
||||
ifeq "$(findstring android, $(shell $(CC) --version 2>/dev/null))" ""
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
CFLAGS_FLTO ?= -flto=full
|
||||
@ -77,17 +75,17 @@ ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -fno-move-loop-invariants -
|
||||
SPECIAL_PERFORMANCE += -fno-move-loop-invariants -fdisable-tree-cunrolli
|
||||
endif
|
||||
|
||||
ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
ifndef SOURCE_DATE_EPOCH
|
||||
HAVE_MARCHNATIVE = 1
|
||||
CFLAGS_OPT += -march=native
|
||||
endif
|
||||
endif
|
||||
#ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -march=native -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1"
|
||||
# ifndef SOURCE_DATE_EPOCH
|
||||
# HAVE_MARCHNATIVE = 1
|
||||
# CFLAGS_OPT += -march=native
|
||||
# endif
|
||||
#endif
|
||||
|
||||
ifneq "$(shell uname)" "Darwin"
|
||||
ifeq "$(HAVE_MARCHNATIVE)" "1"
|
||||
SPECIAL_PERFORMANCE += -march=native
|
||||
endif
|
||||
#ifeq "$(HAVE_MARCHNATIVE)" "1"
|
||||
# SPECIAL_PERFORMANCE += -march=native
|
||||
#endif
|
||||
# OS X does not like _FORTIFY_SOURCE=2
|
||||
ifndef DEBUG
|
||||
CFLAGS_OPT += -D_FORTIFY_SOURCE=2
|
||||
@ -519,7 +517,7 @@ code-format:
|
||||
ifndef AFL_NO_X86
|
||||
test_build: afl-cc afl-gcc afl-as afl-showmap
|
||||
@echo "[*] Testing the CC wrapper afl-cc and its instrumentation output..."
|
||||
@unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_ASAN AFL_USE_MSAN AFL_CC; ASAN_OPTIONS=detect_leaks=0 AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc test-instr.c -o test-instr 2>&1 || (echo "Oops, afl-cc failed"; exit 1 )
|
||||
@unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_ASAN AFL_USE_MSAN; ASAN_OPTIONS=detect_leaks=0 AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc test-instr.c -o test-instr 2>&1 || (echo "Oops, afl-cc failed"; exit 1 )
|
||||
ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
|
||||
echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||
@rm -f test-instr
|
||||
|
@ -43,7 +43,8 @@ endif
|
||||
LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' | sed 's/svn//' )
|
||||
LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//' )
|
||||
LLVM_MINOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/.*\.//' | sed 's/git//' | sed 's/svn//' | sed 's/ .*//' )
|
||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-3]|^19' && echo 1 || echo 0 )
|
||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^3\.[0-3]|^[0-2]\.' && echo 1 || echo 0 )
|
||||
LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[3-9]' && echo 1 || echo 0 )
|
||||
LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 )
|
||||
LLVM_10_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]|^10\.[1-9]|^10\.0.[1-9]' && echo 1 || echo 0 )
|
||||
LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]' && echo 1 || echo 0 )
|
||||
@ -58,7 +59,11 @@ ifeq "$(LLVMVER)" ""
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_UNSUPPORTED)" "1"
|
||||
$(warning llvm_mode only supports llvm versions 3.4 up to 12)
|
||||
$(error llvm_mode only supports llvm from version 3.4 onwards)
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_TOO_NEW)" "1"
|
||||
$(warning you are using an in-development llvm version - this might break llvm_mode!)
|
||||
endif
|
||||
|
||||
LLVM_TOO_OLD=1
|
||||
|
84
README.md
84
README.md
@ -2,11 +2,9 @@
|
||||
|
||||
<img align="right" src="https://raw.githubusercontent.com/andreafioraldi/AFLplusplus-website/master/static/logo_256x256.png" alt="AFL++ Logo">
|
||||
|
||||

|
||||
Release Version: [3.11c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
|
||||
Release Version: [3.00c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
|
||||
Github Version: 3.01a
|
||||
Github Version: 3.12a
|
||||
|
||||
Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)
|
||||
|
||||
@ -23,11 +21,18 @@
|
||||
mutations, more and better instrumentation, custom module support, etc.
|
||||
|
||||
If you want to use afl++ for your academic work, check the [papers page](https://aflplus.plus/papers/)
|
||||
on the website.
|
||||
on the website. To cite our work, look at the [Cite](#cite) section.
|
||||
For comparisons use the fuzzbench `aflplusplus` setup, or use `afl-clang-fast`
|
||||
with `AFL_LLVM_CMPLOG=1`.
|
||||
|
||||
## Major changes in afl++ 3.0
|
||||
## Major changes in afl++ 3.00 + 3.10
|
||||
|
||||
With afl++ 3.0 we introduced changes that break some previous afl and afl++
|
||||
With afl++ 3.10 we introduced the following changes from previous behaviours:
|
||||
* The '+' feature of the '-t' option now means to auto-calculate the timeout
|
||||
with the value given being the maximum timeout. The original meaning of
|
||||
"skipping timeouts instead of abort" is now inherent to the -t option.
|
||||
|
||||
With afl++ 3.00 we introduced changes that break some previous afl and afl++
|
||||
behaviours and defaults:
|
||||
|
||||
* There are no llvm_mode and gcc_plugin subdirectories anymore and there is
|
||||
@ -170,7 +175,7 @@ If you want to build afl++ yourself you have many options.
|
||||
The easiest choice is to build and install everything:
|
||||
|
||||
```shell
|
||||
sudo apt install build-essential python3-dev automake flex bison libglib2.0-dev libpixman-1-dev clang python3-setuptools clang llvm llvm-dev libstdc++-dev
|
||||
sudo apt install build-essential python3-dev automake flex bison libglib2.0-dev libpixman-1-dev python3-setuptools clang lld llvm llvm-dev libstdc++-dev
|
||||
make distrib
|
||||
sudo make install
|
||||
```
|
||||
@ -221,7 +226,7 @@ These build options exist:
|
||||
* AFL_NO_X86 - if compiling on non-intel/amd platforms
|
||||
* LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g. Debian)
|
||||
|
||||
e.g.: make ASAN_BUILD=1
|
||||
e.g.: `make ASAN_BUILD=1`
|
||||
|
||||
## Good examples and writeups
|
||||
|
||||
@ -233,6 +238,7 @@ Here are some good writeups to show how to effectively use AFL++:
|
||||
* [https://securitylab.github.com/research/fuzzing-software-2](https://securitylab.github.com/research/fuzzing-software-2)
|
||||
* [https://securitylab.github.com/research/fuzzing-sockets-FTP](https://securitylab.github.com/research/fuzzing-sockets-FTP)
|
||||
* [https://securitylab.github.com/research/fuzzing-sockets-FreeRDP](https://securitylab.github.com/research/fuzzing-sockets-FreeRDP)
|
||||
* [https://securitylab.github.com/research/fuzzing-apache-1](https://securitylab.github.com/research/fuzzing-apache-1)
|
||||
|
||||
If you are interested in fuzzing structured data (where you define what the
|
||||
structure is), these links have you covered:
|
||||
@ -298,7 +304,7 @@ Clickable README links for the chosen compiler:
|
||||
* [LTO mode - afl-clang-lto](instrumentation/README.lto.md)
|
||||
* [LLVM mode - afl-clang-fast](instrumentation/README.llvm.md)
|
||||
* [GCC_PLUGIN mode - afl-gcc-fast](instrumentation/README.gcc_plugin.md)
|
||||
* GCC/CLANG mode (afl-gcc/afl-clang) have no README as they have no own features
|
||||
* GCC/CLANG modes (afl-gcc/afl-clang) have no README as they have no own features
|
||||
|
||||
You can select the mode for the afl-cc compiler by:
|
||||
1. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++,
|
||||
@ -393,10 +399,19 @@ How to do this is described below.
|
||||
|
||||
Then build the target. (Usually with `make`)
|
||||
|
||||
**NOTE**: sometimes configure and build systems are fickle and do not like
|
||||
stderr output (and think this means a test failure) - which is something
|
||||
afl++ like to do to show statistics. It is recommended to disable them via
|
||||
`export AFL_QUIET=1`.
|
||||
**NOTES**
|
||||
|
||||
1. sometimes configure and build systems are fickle and do not like
|
||||
stderr output (and think this means a test failure) - which is something
|
||||
afl++ likes to do to show statistics. It is recommended to disable them via
|
||||
`export AFL_QUIET=1`.
|
||||
|
||||
2. sometimes configure and build systems error on warnings - these should be
|
||||
disabled (e.g. `--disable-werror` for some configure scripts).
|
||||
|
||||
3. in case the configure/build system complains about afl++'s compiler and
|
||||
aborts then set `export AFL_NOOPT=1` which will then just behave like the
|
||||
real compiler. This option has to be unset again before building the target!
|
||||
|
||||
##### configure
|
||||
|
||||
@ -478,8 +493,9 @@ default.
|
||||
#### c) Minimizing all corpus files
|
||||
|
||||
The shorter the input files that still traverse the same path
|
||||
within the target, the better the fuzzing will be. This is done with `afl-tmin`
|
||||
however it is a long process as this has to be done for every file:
|
||||
within the target, the better the fuzzing will be. This minimization
|
||||
is done with `afl-tmin` however it is a long process as this has to
|
||||
be done for every file:
|
||||
|
||||
```
|
||||
mkdir input
|
||||
@ -530,12 +546,10 @@ If you need to stop and re-start the fuzzing, use the same command line options
|
||||
mutation mode!) and switch the input directory with a dash (`-`):
|
||||
`afl-fuzz -i - -o output -- bin/target -d @@`
|
||||
|
||||
Note that afl-fuzz enforces memory limits to prevent the system to run out
|
||||
of memory. By default this is 50MB for a process. If this is too little for
|
||||
the target (which you can usually see by afl-fuzz bailing with the message
|
||||
that it could not connect to the forkserver), then you can increase this
|
||||
with the `-m` option, the value is in MB. To disable any memory limits
|
||||
(beware!) set `-m none` - which is usually required for ASAN compiled targets.
|
||||
Memory limits are not enforced by afl-fuzz by default and the system may run
|
||||
out of memory. You can decrease the memory with the `-m` option, the value is
|
||||
in MB. If this is too small for the target, you can usually see this by
|
||||
afl-fuzz bailing with the message that it could not connect to the forkserver.
|
||||
|
||||
Adding a dictionary is helpful. See the directory [dictionaries/](dictionaries/) if
|
||||
something is already included for your data format, and tell afl-fuzz to load
|
||||
@ -548,7 +562,9 @@ afl-fuzz has a variety of options that help to workaround target quirks like
|
||||
specific locations for the input file (`-f`), not performing deterministic
|
||||
fuzzing (`-d`) and many more. Check out `afl-fuzz -h`.
|
||||
|
||||
afl-fuzz never stops fuzzing. To terminate afl++ simply press Control-C.
|
||||
By default afl-fuzz never stops fuzzing. To terminate afl++ simply press Control-C
|
||||
or send a signal SIGINT. You can limit the number of executions or approximate runtime
|
||||
in seconds with options also.
|
||||
|
||||
When you start afl-fuzz you will see a user interface that shows what the status
|
||||
is:
|
||||
@ -730,6 +746,9 @@ campaigns as these are much shorter runnings.
|
||||
1. Always:
|
||||
* LTO has a much longer compile time which is diametrical to short fuzzing -
|
||||
hence use afl-clang-fast instead.
|
||||
* If you compile with CMPLOG then you can save fuzzing time and reuse that
|
||||
compiled target for both the -c option and the main fuzz target.
|
||||
This will impact the speed by ~15% though.
|
||||
* `AFL_FAST_CAL` - Enable fast calibration, this halfs the time the saturated
|
||||
corpus needs to be loaded.
|
||||
* `AFL_CMPLOG_ONLY_NEW` - only perform cmplog on new found paths, not the
|
||||
@ -749,6 +768,10 @@ campaigns as these are much shorter runnings.
|
||||
* for CMPLOG targets, 60% for `-l 2`, 40% for `-l 3`
|
||||
|
||||
4. Do *not* run any `-M` modes, just running `-S` modes is better for CI fuzzing.
|
||||
`-M` enables deterministic fuzzing, old queue handling etc. which is good for
|
||||
a fuzzing campaign but not good for short CI runs.
|
||||
|
||||
How this can look like can e.g. be seen at afl++'s setup in Google's [oss-fuzz](https://github.com/google/oss-fuzz/blob/4bb61df7905c6005000f5766e966e6fe30ab4559/infra/base-images/base-builder/compile_afl#L69).
|
||||
|
||||
## Fuzzing binary-only targets
|
||||
|
||||
@ -786,8 +809,7 @@ If [afl-dyninst](https://github.com/vanhauser-thc/afl-dyninst) works for
|
||||
your binary, then you can use afl-fuzz normally and it will have twice
|
||||
the speed compared to qemu_mode (but slower than persistent mode).
|
||||
Note that several other binary rewriters exist, all with their advantages and
|
||||
caveats. As rewriting a binary is much faster than Qemu this is a highly
|
||||
recommended approach!
|
||||
caveats.
|
||||
|
||||
### Unicorn
|
||||
|
||||
@ -1163,8 +1185,18 @@ Thank you!
|
||||
|
||||
## Cite
|
||||
|
||||
If you use AFLpluplus to compare to your work, please use either `afl-clang-lto`
|
||||
or `afl-clang-fast` with `AFL_LLVM_CMPLOG=1` for building targets and
|
||||
`afl-fuzz` with the command line option `-l 2` for fuzzing.
|
||||
The most effective setup is the `aflplusplus` default configuration on Google's [fuzzbench](https://github.com/google/fuzzbench/tree/master/fuzzers/aflplusplus).
|
||||
|
||||
If you use AFLplusplus in scientific work, consider citing [our paper](https://www.usenix.org/conference/woot20/presentation/fioraldi) presented at WOOT'20:
|
||||
```
|
||||
|
||||
+ Andrea Fioraldi, Dominik Maier, Heiko Eißfeldt, and Marc Heuse. “AFL++: Combining incremental steps of fuzzing research”. In 14th USENIX Workshop on Offensive Technologies (WOOT 20). USENIX Association, Aug. 2020.
|
||||
|
||||
Bibtex:
|
||||
|
||||
```bibtex
|
||||
@inproceedings {AFLplusplus-Woot20,
|
||||
author = {Andrea Fioraldi and Dominik Maier and Heiko Ei{\ss}feldt and Marc Heuse},
|
||||
title = {{AFL++}: Combining Incremental Steps of Fuzzing Research},
|
||||
|
13
TODO.md
13
TODO.md
@ -2,20 +2,20 @@
|
||||
|
||||
## Roadmap 3.00+
|
||||
|
||||
- AFL_MAP_SIZE for qemu_mode and unicorn_mode
|
||||
- CPU affinity for many cores? There seems to be an issue > 96 cores
|
||||
- afl-plot to support multiple plot_data
|
||||
- afl_custom_fuzz_splice_optin()
|
||||
- afl_custom_splice()
|
||||
- intel-pt tracer
|
||||
- better autodetection of shifting runtime timeout values
|
||||
- cmplog: use colorization input for havoc?
|
||||
- cmplog: too much tainted bytes, directly add to dict and skip?
|
||||
|
||||
|
||||
## Further down the road
|
||||
|
||||
afl-fuzz:
|
||||
- setting min_len/max_len/start_offset/end_offset limits for mutation output
|
||||
- add __sanitizer_cov_trace_cmp* support via shmem
|
||||
|
||||
llvm_mode:
|
||||
- add __sanitizer_cov_trace_cmp* support
|
||||
|
||||
qemu_mode:
|
||||
- non colliding instrumentation
|
||||
@ -26,9 +26,12 @@ qemu_mode:
|
||||
- add/implement AFL_QEMU_INST_LIBLIST and AFL_QEMU_NOINST_PROGRAM
|
||||
- add/implement AFL_QEMU_INST_REGIONS as a list of _START/_END addresses
|
||||
|
||||
|
||||
## Ideas
|
||||
|
||||
- LTO/sancov: write current edge to prev_loc and use that information when
|
||||
using cmplog or __sanitizer_cov_trace_cmp*. maybe we can deduct by follow
|
||||
up edge numbers that both following cmp paths have been found and then
|
||||
disable working on this edge id -> cmplog_intelligence branch
|
||||
- use cmplog colorization taint result for havoc locations?
|
||||
|
||||
|
22
afl-cmin
22
afl-cmin
@ -287,6 +287,10 @@ BEGIN {
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (0 == system( "test -d "in_dir"/default" )) {
|
||||
in_dir = in_dir "/default"
|
||||
}
|
||||
|
||||
if (0 == system( "test -d "in_dir"/queue" )) {
|
||||
in_dir = in_dir "/queue"
|
||||
}
|
||||
@ -342,8 +346,10 @@ BEGIN {
|
||||
} else {
|
||||
stat_format = "-f '%z %N'" # *BSD, MacOS
|
||||
}
|
||||
cmdline = "cd "in_dir" && find . \\( ! -name . -a -type d -prune \\) -o -type f -exec stat "stat_format" \\{\\} \\; | sort -k1n -k2r"
|
||||
cmdline = "ls "in_dir" | (cd "in_dir" && xargs stat "stat_format" 2>/dev/null) | sort -k1n -k2r"
|
||||
cmdline = "(cd "in_dir" && find . \\( ! -name . -a -type d -prune \\) -o -type f -exec stat "stat_format" \\{\\} + | sort -k1n -k2r)"
|
||||
#cmdline = "ls "in_dir" | (cd "in_dir" && xargs stat "stat_format" 2>/dev/null) | sort -k1n -k2r"
|
||||
#cmdline = "(cd "in_dir" && stat "stat_format" *) | sort -k1n -k2r"
|
||||
#cmdline = "(cd "in_dir" && ls | xargs stat "stat_format" ) | sort -k1n -k2r"
|
||||
while (cmdline | getline) {
|
||||
sub(/^[0-9]+ (\.\/)?/,"",$0)
|
||||
infilesSmallToBig[i++] = $0
|
||||
@ -354,12 +360,12 @@ BEGIN {
|
||||
|
||||
# Make sure that we're not dealing with a directory.
|
||||
|
||||
if (0 == system("test -d "in_dir"/"first_file)) {
|
||||
if (0 == system("test -d ""\""in_dir"/"first_file"\"")) {
|
||||
print "[-] Error: The input directory is empty or contains subdirectories - please fix." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (0 == system("ln "in_dir"/"first_file" "trace_dir"/.link_test")) {
|
||||
if (0 == system("ln \""in_dir"/"first_file"\" "trace_dir"/.link_test")) {
|
||||
cp_tool = "ln"
|
||||
} else {
|
||||
cp_tool = "cp"
|
||||
@ -374,7 +380,7 @@ BEGIN {
|
||||
if (!stdin_file) {
|
||||
system( "AFL_CMIN_ALLOW_ANY=1 "AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -- \""target_bin"\" "prog_args_string" <\""in_dir"/"first_file"\"")
|
||||
} else {
|
||||
system("cp "in_dir"/"first_file" "stdin_file)
|
||||
system("cp \""in_dir"/"first_file"\" "stdin_file)
|
||||
system( "AFL_CMIN_ALLOW_ANY=1 "AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"/.run_test\" -Z "extra_par" -A \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
|
||||
}
|
||||
|
||||
@ -411,8 +417,8 @@ BEGIN {
|
||||
retval = system( AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string)
|
||||
} else {
|
||||
print " Processing "in_count" files (forkserver mode)..."
|
||||
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" </dev/null"
|
||||
retval = system( AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string" </dev/null")
|
||||
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -A \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null"
|
||||
retval = system( AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -A \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
|
||||
}
|
||||
|
||||
if (retval && !AFL_CMIN_CRASHES_ONLY) {
|
||||
@ -496,7 +502,7 @@ BEGIN {
|
||||
|
||||
# copy file unless already done
|
||||
if (! (fn in file_already_copied)) {
|
||||
system(cp_tool" "in_dir"/"fn" "out_dir"/"fn)
|
||||
system(cp_tool" \""in_dir"/"fn"\" \""out_dir"/"fn"\"")
|
||||
file_already_copied[fn] = ""
|
||||
++out_count
|
||||
#printf "tuple nr %d (%d cnt=%d) -> %s\n",tcnt,key,key_count[key],fn > trace_dir"/.log"
|
||||
|
@ -223,6 +223,7 @@ if [ ! -d "$IN_DIR" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test -d "$IN_DIR/default" && IN_DIR="$IN_DIR/default"
|
||||
test -d "$IN_DIR/queue" && IN_DIR="$IN_DIR/queue"
|
||||
|
||||
find "$OUT_DIR" -name 'id[:_]*' -maxdepth 1 -exec rm -- {} \; 2>/dev/null
|
||||
|
11
afl-plot
11
afl-plot
@ -99,7 +99,7 @@ if [ ! -d "$outputdir" ]; then
|
||||
|
||||
fi
|
||||
|
||||
rm -f "$outputdir/high_freq.png" "$outputdir/low_freq.png" "$outputdir/exec_speed.png"
|
||||
rm -f "$outputdir/high_freq.png" "$outputdir/low_freq.png" "$outputdir/exec_speed.png" "$outputdir/edges.png"
|
||||
mv -f "$outputdir/index.html" "$outputdir/index.html.orig" 2>/dev/null
|
||||
|
||||
echo "[*] Generating plots..."
|
||||
@ -152,6 +152,12 @@ set ytics auto
|
||||
plot '$inputdir/plot_data' using 1:11 with filledcurve x1 title '' linecolor rgb '#0090ff' fillstyle transparent solid 0.2 noborder, \\
|
||||
'$inputdir/plot_data' using 1:11 with lines title ' execs/sec' linecolor rgb '#0090ff' linewidth 3 smooth bezier;
|
||||
|
||||
set terminal png truecolor enhanced size 1000,300 butt
|
||||
set output '$outputdir/edges.png'
|
||||
|
||||
set ytics auto
|
||||
plot '$inputdir/plot_data' using 1:13 with lines title ' edges' linecolor rgb '#0090ff' linewidth 3
|
||||
|
||||
_EOF_
|
||||
|
||||
) | gnuplot
|
||||
@ -172,6 +178,7 @@ cat >"$outputdir/index.html" <<_EOF_
|
||||
<tr><td><b>Generated on:</b></td><td>`date`</td></tr>
|
||||
</table>
|
||||
<p>
|
||||
<img src="edges.png" width=1000 height=300>
|
||||
<img src="high_freq.png" width=1000 height=300><p>
|
||||
<img src="low_freq.png" width=1000 height=200><p>
|
||||
<img src="exec_speed.png" width=1000 height=200>
|
||||
@ -183,7 +190,7 @@ _EOF_
|
||||
# sensitive, this seems like a reasonable trade-off.
|
||||
|
||||
chmod 755 "$outputdir"
|
||||
chmod 644 "$outputdir/high_freq.png" "$outputdir/low_freq.png" "$outputdir/exec_speed.png" "$outputdir/index.html"
|
||||
chmod 644 "$outputdir/high_freq.png" "$outputdir/low_freq.png" "$outputdir/exec_speed.png" "$outputdir/edges.png" "$outputdir/index.html"
|
||||
|
||||
echo "[+] All done - enjoy your charts!"
|
||||
|
||||
|
@ -39,7 +39,7 @@ if [ "$PLATFORM" = "Linux" ] ; then
|
||||
echo Settings applied.
|
||||
dmesg | egrep -q 'nospectre_v2|spectre_v2=off' || {
|
||||
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=0 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 srbds=off noexec=off noexec32=off tsx=on tsx_async_abort=off arm64.nopauth audit=0 hardened_usercopy=off ssbd=force-off"'
|
||||
}
|
||||
DONE=1
|
||||
fi
|
||||
@ -49,6 +49,12 @@ if [ "$PLATFORM" = "FreeBSD" ] ; then
|
||||
sysctl kern.elf64.aslr.enable=0
|
||||
} > /dev/null
|
||||
echo Settings applied.
|
||||
cat <<EOF
|
||||
In order to suppress core file generation during fuzzing it is recommended to set
|
||||
me:\\
|
||||
:coredumpsize=0:
|
||||
in the ~/.login_conf file for the user used for fuzzing.
|
||||
EOF
|
||||
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 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.'
|
||||
@ -60,8 +66,14 @@ if [ "$PLATFORM" = "OpenBSD" ] ; then
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "DragonFly" ] ; then
|
||||
echo
|
||||
echo 'System security features cannot be disabled on DragonFly.'
|
||||
#/sbin/sysctl kern.corefile=/dev/null
|
||||
#echo Settings applied.
|
||||
cat <<EOF
|
||||
In order to suppress core file generation during fuzzing it is recommended to set
|
||||
me:\\
|
||||
:coredumpsize=0:
|
||||
in the ~/.login_conf file for the user used for fuzzing.
|
||||
EOF
|
||||
DONE=1
|
||||
fi
|
||||
if [ "$PLATFORM" = "NetBSD" ] ; then
|
||||
@ -88,7 +100,7 @@ fi
|
||||
if [ "$PLATFORM" = "Haiku" ] ; then
|
||||
SETTINGS=~/config/settings/system/debug_server/settings
|
||||
[ -r ${SETTINGS} ] && grep -qE "default_action\s+kill" ${SETTINGS} && { echo "Nothing to do"; } || { \
|
||||
echo We change the debug_server default_action from user to silenty kill; \
|
||||
echo We change the debug_server default_action from user to silently kill; \
|
||||
[ ! -r ${SETTINGS} ] && echo "default_action kill" >${SETTINGS} || { mv ${SETTINGS} s.tmp; sed -e "s/default_action\s\s*user/default_action kill/" s.tmp > ${SETTINGS}; rm s.tmp; }; \
|
||||
echo Settings applied.; \
|
||||
}
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include "libhfcommon/util.h"
|
||||
|
||||
#define PROG_NAME "honggfuzz"
|
||||
#define PROG_VERSION "2.3"
|
||||
#define PROG_VERSION "2.4"
|
||||
|
||||
/* Name of the template which will be replaced with the proper name of the file */
|
||||
#define _HF_FILE_PLACEHOLDER "___FILE___"
|
||||
@ -208,6 +208,7 @@ typedef struct {
|
||||
const char* crashDir;
|
||||
const char* covDirNew;
|
||||
bool saveUnique;
|
||||
bool saveSmaller;
|
||||
size_t dynfileqMaxSz;
|
||||
size_t dynfileqCnt;
|
||||
dynfile_t* dynfileqCurrent;
|
||||
@ -279,9 +280,9 @@ typedef struct {
|
||||
cmpfeedback_t* cmpFeedbackMap;
|
||||
int cmpFeedbackFd;
|
||||
bool cmpFeedback;
|
||||
const char* blacklistFile;
|
||||
uint64_t* blacklist;
|
||||
size_t blacklistCnt;
|
||||
const char* blocklistFile;
|
||||
uint64_t* blocklist;
|
||||
size_t blocklistCnt;
|
||||
bool skipFeedbackOnTimeout;
|
||||
uint64_t maxCov[4];
|
||||
dynFileMethod_t dynFileMethod;
|
||||
|
@ -77,11 +77,11 @@ static inline uint64_t util_rndGet(uint64_t min, uint64_t max) {
|
||||
}
|
||||
static inline uint64_t util_rnd64() { return rand_below(afl_struct, 1 << 30); }
|
||||
|
||||
static inline size_t input_getRandomInputAsBuf(run_t *run, const uint8_t **buf) {
|
||||
*buf = queue_input;
|
||||
static inline const uint8_t* input_getRandomInputAsBuf(run_t* run, size_t* len) {
|
||||
*len = queue_input_size;
|
||||
run->dynfile->data = queue_input;
|
||||
run->dynfile->size = queue_input_size;
|
||||
return queue_input_size;
|
||||
return queue_input;
|
||||
}
|
||||
static inline void input_setSize(run_t* run, size_t sz) {
|
||||
run->dynfile->size = sz;
|
||||
|
@ -1 +0,0 @@
|
||||
.
|
3
custom_mutators/honggfuzz/libhfcommon/common.h
Normal file
3
custom_mutators/honggfuzz/libhfcommon/common.h
Normal file
@ -0,0 +1,3 @@
|
||||
#ifndef LOG_E
|
||||
#define LOG_E LOG_F
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
10
custom_mutators/rust/.gitignore
vendored
Normal file
10
custom_mutators/rust/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
8
custom_mutators/rust/Cargo.toml
Normal file
8
custom_mutators/rust/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"custom_mutator-sys",
|
||||
"custom_mutator",
|
||||
"example",
|
||||
# Lain needs a nightly toolchain
|
||||
# "example_lain",
|
||||
]
|
11
custom_mutators/rust/README.md
Normal file
11
custom_mutators/rust/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Rust Custom Mutators
|
||||
|
||||
Bindings to create custom mutators in Rust.
|
||||
|
||||
These bindings are documented with rustdoc. To view the documentation run
|
||||
```cargo doc -p custom_mutator --open```.
|
||||
|
||||
A minimal example can be found in `example`. Build it using `cargo build --example example_mutator`.
|
||||
|
||||
An example using [lain](https://github.com/microsoft/lain) for structured fuzzing can be found in `example_lain`.
|
||||
Since lain requires a nightly rust toolchain, you need to set one up before you can play with it.
|
12
custom_mutators/rust/custom_mutator-sys/Cargo.toml
Normal file
12
custom_mutators/rust/custom_mutator-sys/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "custom_mutator-sys"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.56"
|
42
custom_mutators/rust/custom_mutator-sys/build.rs
Normal file
42
custom_mutators/rust/custom_mutator-sys/build.rs
Normal file
@ -0,0 +1,42 @@
|
||||
extern crate bindgen;
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
// this code is largely taken straight from the handbook: https://github.com/fitzgen/bindgen-tutorial-bzip2-sys
|
||||
fn main() {
|
||||
// Tell cargo to invalidate the built crate whenever the wrapper changes
|
||||
println!("cargo:rerun-if-changed=wrapper.h");
|
||||
|
||||
// The bindgen::Builder is the main entry point
|
||||
// to bindgen, and lets you build up options for
|
||||
// the resulting bindings.
|
||||
let bindings = bindgen::Builder::default()
|
||||
// The input header we would like to generate
|
||||
// bindings for.
|
||||
.header("wrapper.h")
|
||||
.whitelist_type("afl_state_t")
|
||||
.blacklist_type(r"u\d+")
|
||||
.opaque_type(r"_.*")
|
||||
.opaque_type("FILE")
|
||||
.opaque_type("in_addr(_t)?")
|
||||
.opaque_type("in_port(_t)?")
|
||||
.opaque_type("sa_family(_t)?")
|
||||
.opaque_type("sockaddr_in(_t)?")
|
||||
.opaque_type("time_t")
|
||||
.rustfmt_bindings(true)
|
||||
.size_t_is_usize(true)
|
||||
// Tell cargo to invalidate the built crate whenever any of the
|
||||
// included header files changed.
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||
// Finish the builder and generate the bindings.
|
||||
.generate()
|
||||
// Unwrap the Result and panic on failure.
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
// Write the bindings to the $OUT_DIR/bindings.rs file.
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
5
custom_mutators/rust/custom_mutator-sys/src/lib.rs
Normal file
5
custom_mutators/rust/custom_mutator-sys/src/lib.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
4
custom_mutators/rust/custom_mutator-sys/wrapper.h
Normal file
4
custom_mutators/rust/custom_mutator-sys/wrapper.h
Normal file
@ -0,0 +1,4 @@
|
||||
#include "../../../include/afl-fuzz.h"
|
||||
#include "../../../include/common.h"
|
||||
#include "../../../include/config.h"
|
||||
#include "../../../include/debug.h"
|
13
custom_mutators/rust/custom_mutator/Cargo.toml
Normal file
13
custom_mutators/rust/custom_mutator/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "custom_mutator"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
afl_internals = ["custom_mutator-sys"]
|
||||
|
||||
[dependencies]
|
||||
custom_mutator-sys = { path = "../custom_mutator-sys", optional=true }
|
740
custom_mutators/rust/custom_mutator/src/lib.rs
Normal file
740
custom_mutators/rust/custom_mutator/src/lib.rs
Normal file
@ -0,0 +1,740 @@
|
||||
#![cfg(unix)]
|
||||
//! Somewhat safe and somewhat ergonomic bindings for creating [AFL++](https://github.com/AFLplusplus/AFLplusplus) [custom mutators](https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/custom_mutators.md) in Rust.
|
||||
//!
|
||||
//! # Usage
|
||||
//! AFL++ custom mutators are expected to be dynamic libraries which expose a set of symbols.
|
||||
//! Check out [`CustomMutator`] to see which functions of the API are supported.
|
||||
//! Then use [`export_mutator`] to export the correct symbols for your mutator.
|
||||
//! In order to use the mutator, your crate needs to be a library crate and have a `crate-type` of `cdylib`.
|
||||
//! Putting
|
||||
//! ```yaml
|
||||
//! [lib]
|
||||
//! crate-type = ["cdylib"]
|
||||
//! ```
|
||||
//! into your `Cargo.toml` should do the trick.
|
||||
//! The final executable can be found in `target/(debug|release)/your_crate_name.so`.
|
||||
//! # Example
|
||||
//! See [`export_mutator`] for an example.
|
||||
//!
|
||||
//! # On `panic`s
|
||||
//! This binding is panic-safe in that it will prevent panics from unwinding into AFL++. Any panic will `abort` at the boundary between the custom mutator and AFL++.
|
||||
//!
|
||||
//! # Access to AFL++ internals
|
||||
//! This crate has an optional feature "afl_internals", which gives access to AFL++'s internal state.
|
||||
//! The state is passed to [`CustomMutator::init`], when the feature is activated.
|
||||
//!
|
||||
//! _This is completely unsafe and uses automatically generated types extracted from the AFL++ source._
|
||||
use std::{fmt::Debug, path::Path};
|
||||
|
||||
#[cfg(feature = "afl_internals")]
|
||||
#[doc(hidden)]
|
||||
pub use custom_mutator_sys::afl_state;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[doc(hidden)]
|
||||
pub trait RawCustomMutator {
|
||||
#[cfg(feature = "afl_internals")]
|
||||
fn init(afl: &'static afl_state, seed: u32) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
fn init(seed: u32) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Option<&'b [u8]>;
|
||||
|
||||
fn fuzz_count(&mut self, buffer: &[u8]) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn queue_new_entry(&mut self, filename_new_queue: &Path, _filename_orig_queue: Option<&Path>) {}
|
||||
|
||||
fn queue_get(&mut self, filename: &Path) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn describe(&mut self, max_description: usize) -> Option<&str> {
|
||||
Some(default_mutator_describe::<Self>(max_description))
|
||||
}
|
||||
|
||||
fn introspection(&mut self) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
|
||||
/*fn post_process(&self, buffer: &[u8], unsigned char **out_buf)-> usize;
|
||||
int afl_custom_init_trim(&self, buffer: &[u8]);
|
||||
size_t afl_custom_trim(&self, unsigned char **out_buf);
|
||||
int afl_custom_post_trim(&self, unsigned char success);
|
||||
size_t afl_custom_havoc_mutation(&self, buffer: &[u8], unsigned char **out_buf, size_t max_size);
|
||||
unsigned char afl_custom_havoc_mutation_probability(&self);*/
|
||||
}
|
||||
|
||||
/// Wrappers for the custom mutator which provide the bridging between the C API and CustomMutator.
|
||||
/// These wrappers are not intended to be used directly, rather export_mutator will use them to publish the custom mutator C API.
|
||||
#[doc(hidden)]
|
||||
pub mod wrappers {
|
||||
#[cfg(feature = "afl_internals")]
|
||||
use custom_mutator_sys::afl_state;
|
||||
|
||||
use std::{
|
||||
any::Any,
|
||||
convert::TryInto,
|
||||
ffi::{c_void, CStr, OsStr},
|
||||
mem::ManuallyDrop,
|
||||
os::{raw::c_char, unix::ffi::OsStrExt},
|
||||
panic::catch_unwind,
|
||||
path::Path,
|
||||
process::abort,
|
||||
ptr::null,
|
||||
slice,
|
||||
};
|
||||
|
||||
use crate::RawCustomMutator;
|
||||
|
||||
/// A structure to be used as the data pointer for our custom mutator. This was used as additional storage and is kept for now in case its needed later.
|
||||
/// Also has some convenience functions for FFI conversions (from and to ptr) and tries to make misuse hard (see [`FFIContext::from`]).
|
||||
struct FFIContext<M: RawCustomMutator> {
|
||||
mutator: M,
|
||||
/// buffer for storing the description returned by [`RawCustomMutator::describe`] as a CString
|
||||
description_buffer: Vec<u8>,
|
||||
/// buffer for storing the introspection returned by [`RawCustomMutator::introspect`] as a CString
|
||||
introspection_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<M: RawCustomMutator> FFIContext<M> {
|
||||
fn from(ptr: *mut c_void) -> ManuallyDrop<Box<Self>> {
|
||||
assert!(!ptr.is_null());
|
||||
ManuallyDrop::new(unsafe { Box::from_raw(ptr as *mut Self) })
|
||||
}
|
||||
|
||||
fn into_ptr(self: Box<Self>) -> *const c_void {
|
||||
Box::into_raw(self) as *const c_void
|
||||
}
|
||||
|
||||
#[cfg(feature = "afl_internals")]
|
||||
fn new(afl: &'static afl_state, seed: u32) -> Box<Self> {
|
||||
Box::new(Self {
|
||||
mutator: M::init(afl, seed),
|
||||
description_buffer: Vec::new(),
|
||||
introspection_buffer: Vec::new(),
|
||||
})
|
||||
}
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
fn new(seed: u32) -> Box<Self> {
|
||||
Box::new(Self {
|
||||
mutator: M::init(seed),
|
||||
description_buffer: Vec::new(),
|
||||
introspection_buffer: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// panic handler called for every panic
|
||||
fn panic_handler(method: &str, panic_info: Box<dyn Any + Send + 'static>) -> ! {
|
||||
use std::ops::Deref;
|
||||
let cause = panic_info
|
||||
.downcast_ref::<String>()
|
||||
.map(String::deref)
|
||||
.unwrap_or_else(|| {
|
||||
panic_info
|
||||
.downcast_ref::<&str>()
|
||||
.copied()
|
||||
.unwrap_or("<cause unknown>")
|
||||
});
|
||||
eprintln!("A panic occurred at {}: {}", method, cause);
|
||||
abort()
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
pub fn afl_custom_init_<M: RawCustomMutator>(seed: u32) -> *const c_void {
|
||||
match catch_unwind(|| FFIContext::<M>::new(seed).into_ptr()) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_init", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
#[cfg(feature = "afl_internals")]
|
||||
pub fn afl_custom_init_<M: RawCustomMutator>(
|
||||
afl: Option<&'static afl_state>,
|
||||
seed: u32,
|
||||
) -> *const c_void {
|
||||
match catch_unwind(|| {
|
||||
let afl = afl.expect("mutator func called with NULL afl");
|
||||
FFIContext::<M>::new(afl, seed).into_ptr()
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_init", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub unsafe fn afl_custom_fuzz_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
buf: *mut u8,
|
||||
buf_size: usize,
|
||||
out_buf: *mut *const u8,
|
||||
add_buf: *mut u8,
|
||||
add_buf_size: usize,
|
||||
max_size: usize,
|
||||
) -> usize {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if buf.is_null() {
|
||||
panic!("null buf passed to afl_custom_fuzz")
|
||||
}
|
||||
if out_buf.is_null() {
|
||||
panic!("null out_buf passed to afl_custom_fuzz")
|
||||
}
|
||||
let buff_slice = slice::from_raw_parts_mut(buf, buf_size);
|
||||
let add_buff_slice = if add_buf.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(slice::from_raw_parts(add_buf, add_buf_size))
|
||||
};
|
||||
match context
|
||||
.mutator
|
||||
.fuzz(buff_slice, add_buff_slice, max_size.try_into().unwrap())
|
||||
{
|
||||
Some(buffer) => {
|
||||
*out_buf = buffer.as_ptr();
|
||||
buffer.len().try_into().unwrap()
|
||||
}
|
||||
None => {
|
||||
// return the input buffer with 0-length to let AFL skip this mutation attempt
|
||||
*out_buf = buf;
|
||||
0
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_fuzz", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub unsafe fn afl_custom_fuzz_count_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
buf: *const u8,
|
||||
buf_size: usize,
|
||||
) -> u32 {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if buf.is_null() {
|
||||
panic!("null buf passed to afl_custom_fuzz")
|
||||
}
|
||||
let buf_slice = slice::from_raw_parts(buf, buf_size);
|
||||
// see https://doc.rust-lang.org/nomicon/borrow-splitting.html
|
||||
let ctx = &mut **context;
|
||||
let mutator = &mut ctx.mutator;
|
||||
mutator.fuzz_count(buf_slice)
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_fuzz_count", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub fn afl_custom_queue_new_entry_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
filename_new_queue: *const c_char,
|
||||
filename_orig_queue: *const c_char,
|
||||
) {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if filename_new_queue.is_null() {
|
||||
panic!("received null filename_new_queue in afl_custom_queue_new_entry");
|
||||
}
|
||||
let filename_new_queue = Path::new(OsStr::from_bytes(
|
||||
unsafe { CStr::from_ptr(filename_new_queue) }.to_bytes(),
|
||||
));
|
||||
let filename_orig_queue = if !filename_orig_queue.is_null() {
|
||||
Some(Path::new(OsStr::from_bytes(
|
||||
unsafe { CStr::from_ptr(filename_orig_queue) }.to_bytes(),
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
context
|
||||
.mutator
|
||||
.queue_new_entry(filename_new_queue, filename_orig_queue);
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_queue_new_entry", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub unsafe fn afl_custom_deinit_<M: RawCustomMutator>(data: *mut c_void) {
|
||||
match catch_unwind(|| {
|
||||
// drop the context
|
||||
ManuallyDrop::into_inner(FFIContext::<M>::from(data));
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_deinit", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub fn afl_custom_introspection_<M: RawCustomMutator>(data: *mut c_void) -> *const c_char {
|
||||
match catch_unwind(|| {
|
||||
let context = &mut *FFIContext::<M>::from(data);
|
||||
if let Some(res) = context.mutator.introspection() {
|
||||
let buf = &mut context.introspection_buffer;
|
||||
buf.clear();
|
||||
buf.extend_from_slice(res.as_bytes());
|
||||
buf.push(0);
|
||||
// unwrapping here, as the error case should be extremely rare
|
||||
CStr::from_bytes_with_nul(&buf).unwrap().as_ptr()
|
||||
} else {
|
||||
null()
|
||||
}
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_introspection", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub fn afl_custom_describe_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
max_description_len: usize,
|
||||
) -> *const c_char {
|
||||
match catch_unwind(|| {
|
||||
let context = &mut *FFIContext::<M>::from(data);
|
||||
if let Some(res) = context.mutator.describe(max_description_len) {
|
||||
let buf = &mut context.description_buffer;
|
||||
buf.clear();
|
||||
buf.extend_from_slice(res.as_bytes());
|
||||
buf.push(0);
|
||||
// unwrapping here, as the error case should be extremely rare
|
||||
CStr::from_bytes_with_nul(&buf).unwrap().as_ptr()
|
||||
} else {
|
||||
null()
|
||||
}
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_describe", err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub fn afl_custom_queue_get_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
filename: *const c_char,
|
||||
) -> u8 {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
assert!(!filename.is_null());
|
||||
|
||||
context.mutator.queue_get(Path::new(OsStr::from_bytes(
|
||||
unsafe { CStr::from_ptr(filename) }.to_bytes(),
|
||||
))) as u8
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_queue_get", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// exports the given Mutator as a custom mutator as the C interface that AFL++ expects.
|
||||
/// It is not possible to call this macro multiple times, because it would define the custom mutator symbols multiple times.
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate custom_mutator;
|
||||
/// # #[cfg(feature = "afl_internals")]
|
||||
/// # use custom_mutator::afl_state;
|
||||
/// # use custom_mutator::CustomMutator;
|
||||
/// struct MyMutator;
|
||||
/// impl CustomMutator for MyMutator {
|
||||
/// /// ...
|
||||
/// # type Error = ();
|
||||
/// # #[cfg(feature = "afl_internals")]
|
||||
/// # fn init(_afl_state: &afl_state, _seed: u32) -> Result<Self,()> {unimplemented!()}
|
||||
/// # #[cfg(not(feature = "afl_internals"))]
|
||||
/// # fn init(_seed: u32) -> Result<Self, Self::Error> {unimplemented!()}
|
||||
/// # fn fuzz<'b,'s:'b>(&'s mut self, _buffer: &'b mut [u8], _add_buff: Option<&[u8]>, _max_size: usize) -> Result<Option<&'b [u8]>, Self::Error> {unimplemented!()}
|
||||
/// }
|
||||
/// export_mutator!(MyMutator);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! export_mutator {
|
||||
($mutator_type:ty) => {
|
||||
#[cfg(feature = "afl_internals")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_init(
|
||||
afl: ::std::option::Option<&'static $crate::afl_state>,
|
||||
seed: ::std::os::raw::c_uint,
|
||||
) -> *const ::std::os::raw::c_void {
|
||||
$crate::wrappers::afl_custom_init_::<$mutator_type>(afl, seed as u32)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_init(
|
||||
_afl: *const ::std::os::raw::c_void,
|
||||
seed: ::std::os::raw::c_uint,
|
||||
) -> *const ::std::os::raw::c_void {
|
||||
$crate::wrappers::afl_custom_init_::<$mutator_type>(seed as u32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_fuzz_count(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
buf: *const u8,
|
||||
buf_size: usize,
|
||||
) -> u32 {
|
||||
unsafe {
|
||||
$crate::wrappers::afl_custom_fuzz_count_::<$mutator_type>(data, buf, buf_size)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_fuzz(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
buf: *mut u8,
|
||||
buf_size: usize,
|
||||
out_buf: *mut *const u8,
|
||||
add_buf: *mut u8,
|
||||
add_buf_size: usize,
|
||||
max_size: usize,
|
||||
) -> usize {
|
||||
unsafe {
|
||||
$crate::wrappers::afl_custom_fuzz_::<$mutator_type>(
|
||||
data,
|
||||
buf,
|
||||
buf_size,
|
||||
out_buf,
|
||||
add_buf,
|
||||
add_buf_size,
|
||||
max_size,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_queue_new_entry(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
filename_new_queue: *const ::std::os::raw::c_char,
|
||||
filename_orig_queue: *const ::std::os::raw::c_char,
|
||||
) {
|
||||
$crate::wrappers::afl_custom_queue_new_entry_::<$mutator_type>(
|
||||
data,
|
||||
filename_new_queue,
|
||||
filename_orig_queue,
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_queue_get(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
filename: *const ::std::os::raw::c_char,
|
||||
) -> u8 {
|
||||
$crate::wrappers::afl_custom_queue_get_::<$mutator_type>(data, filename)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_introspection(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
) -> *const ::std::os::raw::c_char {
|
||||
$crate::wrappers::afl_custom_introspection_::<$mutator_type>(data)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_describe(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
max_description_len: usize,
|
||||
) -> *const ::std::os::raw::c_char {
|
||||
$crate::wrappers::afl_custom_describe_::<$mutator_type>(data, max_description_len)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_deinit(data: *mut ::std::os::raw::c_void) {
|
||||
unsafe { $crate::wrappers::afl_custom_deinit_::<$mutator_type>(data) }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// this sanity test is supposed to just find out whether an empty mutator being exported by the macro compiles
|
||||
mod sanity_test {
|
||||
#[cfg(feature = "afl_internals")]
|
||||
use super::afl_state;
|
||||
|
||||
use super::{export_mutator, RawCustomMutator};
|
||||
|
||||
struct ExampleMutator;
|
||||
|
||||
impl RawCustomMutator for ExampleMutator {
|
||||
#[cfg(feature = "afl_internals")]
|
||||
fn init(_afl: &afl_state, _seed: u32) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
fn init(_seed: u32) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
_buffer: &'b mut [u8],
|
||||
_add_buff: Option<&[u8]>,
|
||||
_max_size: usize,
|
||||
) -> Option<&'b [u8]> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
export_mutator!(ExampleMutator);
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
/// A custom mutator.
|
||||
/// [`CustomMutator::handle_error`] will be called in case any method returns an [`Result::Err`].
|
||||
pub trait CustomMutator {
|
||||
/// The error type. All methods must return the same error type.
|
||||
type Error: Debug;
|
||||
|
||||
/// The method which handles errors.
|
||||
/// By default, this method will log the error to stderr if the environment variable "`AFL_CUSTOM_MUTATOR_DEBUG`" is set and non-empty.
|
||||
/// After logging the error, execution will continue on a best-effort basis.
|
||||
///
|
||||
/// This default behaviour can be customized by implementing this method.
|
||||
fn handle_error(err: Self::Error) {
|
||||
if std::env::var("AFL_CUSTOM_MUTATOR_DEBUG")
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
eprintln!("Error in custom mutator: {:?}", err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "afl_internals")]
|
||||
fn init(afl: &'static afl_state, seed: u32) -> Result<Self, Self::Error>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
fn init(seed: u32) -> Result<Self, Self::Error>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn fuzz_count(&mut self, buffer: &[u8]) -> Result<u32, Self::Error> {
|
||||
Ok(1)
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Result<Option<&'b [u8]>, Self::Error>;
|
||||
|
||||
fn queue_new_entry(
|
||||
&mut self,
|
||||
filename_new_queue: &Path,
|
||||
filename_orig_queue: Option<&Path>,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn queue_get(&mut self, filename: &Path) -> Result<bool, Self::Error> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn describe(&mut self, max_description: usize) -> Result<Option<&str>, Self::Error> {
|
||||
Ok(Some(default_mutator_describe::<Self>(max_description)))
|
||||
}
|
||||
|
||||
fn introspection(&mut self) -> Result<Option<&str>, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> RawCustomMutator for M
|
||||
where
|
||||
M: CustomMutator,
|
||||
M::Error: Debug,
|
||||
{
|
||||
#[cfg(feature = "afl_internals")]
|
||||
fn init(afl: &'static afl_state, seed: u32) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
match Self::init(afl, seed) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
panic!("Error in afl_custom_init")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
fn init(seed: u32) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
match Self::init(seed) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
panic!("Error in afl_custom_init")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fuzz_count(&mut self, buffer: &[u8]) -> u32 {
|
||||
match self.fuzz_count(buffer) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Option<&'b [u8]> {
|
||||
match self.fuzz(buffer, add_buff, max_size) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_new_entry(&mut self, filename_new_queue: &Path, filename_orig_queue: Option<&Path>) {
|
||||
match self.queue_new_entry(filename_new_queue, filename_orig_queue) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn queue_get(&mut self, filename: &Path) -> bool {
|
||||
match self.queue_get(filename) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn describe(&mut self, max_description: usize) -> Option<&str> {
|
||||
match self.describe(max_description) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn introspection(&mut self) -> Option<&str> {
|
||||
match self.introspection() {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// the default value to return from [`CustomMutator::describe`].
|
||||
fn default_mutator_describe<T: ?Sized>(max_len: usize) -> &'static str {
|
||||
truncate_str_unicode_safe(std::any::type_name::<T>(), max_len)
|
||||
}
|
||||
|
||||
#[cfg(all(test, not(feature = "afl_internals")))]
|
||||
mod default_mutator_describe {
|
||||
struct MyMutator;
|
||||
use super::CustomMutator;
|
||||
impl CustomMutator for MyMutator {
|
||||
type Error = ();
|
||||
|
||||
fn init(_: u32) -> Result<Self, Self::Error> {
|
||||
Ok(Self)
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
_: &'b mut [u8],
|
||||
_: Option<&[u8]>,
|
||||
_: usize,
|
||||
) -> Result<Option<&'b [u8]>, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_describe() {
|
||||
assert_eq!(
|
||||
MyMutator::init(0).unwrap().describe(64).unwrap().unwrap(),
|
||||
"custom_mutator::default_mutator_describe::MyMutator"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// little helper function to truncate a `str` to a maximum of bytes while retaining unicode safety
|
||||
fn truncate_str_unicode_safe(s: &str, max_len: usize) -> &str {
|
||||
if s.len() <= max_len {
|
||||
s
|
||||
} else {
|
||||
if let Some((last_index, _)) = s
|
||||
.char_indices()
|
||||
.take_while(|(index, _)| *index <= max_len)
|
||||
.last()
|
||||
{
|
||||
&s[..last_index]
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod truncate_test {
|
||||
use super::truncate_str_unicode_safe;
|
||||
|
||||
#[test]
|
||||
fn test_truncate() {
|
||||
for (max_len, input, expected_output) in &[
|
||||
(0usize, "a", ""),
|
||||
(1, "a", "a"),
|
||||
(1, "ä", ""),
|
||||
(2, "ä", "ä"),
|
||||
(3, "äa", "äa"),
|
||||
(4, "äa", "äa"),
|
||||
(1, "👎", ""),
|
||||
(2, "👎", ""),
|
||||
(3, "👎", ""),
|
||||
(4, "👎", "👎"),
|
||||
(1, "abc", "a"),
|
||||
(2, "abc", "ab"),
|
||||
] {
|
||||
let actual_output = truncate_str_unicode_safe(input, *max_len);
|
||||
assert_eq!(
|
||||
&actual_output, expected_output,
|
||||
"{:#?} truncated to {} bytes should be {:#?}, but is {:#?}",
|
||||
input, max_len, expected_output, actual_output
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
15
custom_mutators/rust/example/Cargo.toml
Normal file
15
custom_mutators/rust/example/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "example_mutator"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
custom_mutator = { path = "../custom_mutator" }
|
||||
|
||||
[[example]]
|
||||
name = "example_mutator"
|
||||
path = "./src/example_mutator.rs"
|
||||
crate-type = ["cdylib"]
|
50
custom_mutators/rust/example/src/example_mutator.rs
Normal file
50
custom_mutators/rust/example/src/example_mutator.rs
Normal file
@ -0,0 +1,50 @@
|
||||
#![cfg(unix)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use custom_mutator::{export_mutator, CustomMutator};
|
||||
|
||||
struct ExampleMutator;
|
||||
|
||||
impl CustomMutator for ExampleMutator {
|
||||
type Error = ();
|
||||
|
||||
fn init(seed: u32) -> Result<Self, Self::Error> {
|
||||
Ok(Self)
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Result<Option<&'b [u8]>, Self::Error> {
|
||||
buffer.reverse();
|
||||
Ok(Some(buffer))
|
||||
}
|
||||
}
|
||||
|
||||
struct OwnBufferExampleMutator {
|
||||
own_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CustomMutator for OwnBufferExampleMutator {
|
||||
type Error = ();
|
||||
|
||||
fn init(seed: u32) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
own_buffer: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Result<Option<&'b [u8]>, ()> {
|
||||
self.own_buffer.reverse();
|
||||
Ok(Some(self.own_buffer.as_slice()))
|
||||
}
|
||||
}
|
||||
|
||||
export_mutator!(ExampleMutator);
|
16
custom_mutators/rust/example_lain/Cargo.toml
Normal file
16
custom_mutators/rust/example_lain/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "example_lain"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
custom_mutator = { path = "../custom_mutator" }
|
||||
lain="0.5"
|
||||
|
||||
[[example]]
|
||||
name = "example_lain"
|
||||
path = "./src/lain_mutator.rs"
|
||||
crate-type = ["cdylib"]
|
1
custom_mutators/rust/example_lain/rust-toolchain
Normal file
1
custom_mutators/rust/example_lain/rust-toolchain
Normal file
@ -0,0 +1 @@
|
||||
nightly
|
61
custom_mutators/rust/example_lain/src/lain_mutator.rs
Normal file
61
custom_mutators/rust/example_lain/src/lain_mutator.rs
Normal file
@ -0,0 +1,61 @@
|
||||
#![cfg(unix)]
|
||||
|
||||
use custom_mutator::{export_mutator, CustomMutator};
|
||||
use lain::{
|
||||
mutator::Mutator,
|
||||
prelude::*,
|
||||
rand::{rngs::StdRng, SeedableRng},
|
||||
};
|
||||
|
||||
#[derive(Debug, Mutatable, NewFuzzed, BinarySerialize)]
|
||||
struct MyStruct {
|
||||
field_1: u8,
|
||||
|
||||
#[lain(bits = 3)]
|
||||
field_2: u8,
|
||||
|
||||
#[lain(bits = 5)]
|
||||
field_3: u8,
|
||||
|
||||
#[lain(min = 5, max = 10000)]
|
||||
field_4: u32,
|
||||
|
||||
#[lain(ignore)]
|
||||
ignored_field: u64,
|
||||
}
|
||||
|
||||
struct LainMutator {
|
||||
mutator: Mutator<StdRng>,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CustomMutator for LainMutator {
|
||||
type Error = ();
|
||||
|
||||
fn init(seed: u32) -> Result<Self, ()> {
|
||||
Ok(Self {
|
||||
mutator: Mutator::new(StdRng::seed_from_u64(seed as u64)),
|
||||
buffer: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
_buffer: &'b mut [u8],
|
||||
_add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Result<Option<&'b [u8]>, ()> {
|
||||
// we just sample an instance of MyStruct, ignoring the current input
|
||||
let instance = MyStruct::new_fuzzed(&mut self.mutator, None);
|
||||
let size = instance.serialized_size();
|
||||
if size > max_size {
|
||||
return Err(());
|
||||
}
|
||||
self.buffer.clear();
|
||||
self.buffer.reserve(size);
|
||||
instance.binary_serialize::<_, BigEndian>(&mut self.buffer);
|
||||
Ok(Some(self.buffer.as_slice()))
|
||||
}
|
||||
}
|
||||
|
||||
export_mutator!(LainMutator);
|
@ -8,61 +8,105 @@
|
||||
Want to stay in the loop on major new features? Join our mailing list by
|
||||
sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
|
||||
### Version ++3.11c (release)
|
||||
- afl-fuzz:
|
||||
- better auto detection of map size
|
||||
- fix sanitizer settings (bug since 3.10c)
|
||||
- fix an off-by-one overwrite in cmplog
|
||||
- add non-unicode variants from unicode-looking dictionary entries
|
||||
- Rust custom mutator API improvements
|
||||
- Imported crash stats painted yellow on resume (only new ones are red)
|
||||
- afl-cc:
|
||||
- added AFL_NOOPT that will just pass everything to the normal
|
||||
gcc/clang compiler without any changes - to pass weird configure
|
||||
scripts
|
||||
- fixed a crash that can occur with ASAN + CMPLOG together plus
|
||||
better support for unicode (thanks to @stbergmann for reporting!)
|
||||
- fixed a crash in LAF transform for empty strings
|
||||
- handle erroneous setups in which multiple afl-compiler-rt are
|
||||
compiled into the target. This now also supports dlopen()
|
||||
instrumented libs loaded before the forkserver and even after the
|
||||
forkserver is started (then with collisions though)
|
||||
- the compiler rt was added also in object building (-c) which
|
||||
should have been fixed years ago but somewhere got lost :(
|
||||
- Renamed CTX to CALLER, added correct/real CTX implementation to
|
||||
CLASSIC
|
||||
- qemu_mode:
|
||||
- added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks!
|
||||
- if no new/updated checkout is wanted, build with:
|
||||
NO_CHECKOUT=1 ./build_qemu_support.sh
|
||||
- we no longer perform a "git drop"
|
||||
- afl-cmin: support filenames with spaces
|
||||
|
||||
### Version ++3.01a (dev)
|
||||
|
||||
### Version ++3.10c (release)
|
||||
- Mac OS ARM64 support
|
||||
- Android support fixed and updated by Joey Jiaojg - thanks!
|
||||
- New selective instrumentation option with __AFL_COVERAGE_* commands
|
||||
to be placed in the source code.
|
||||
Check out instrumentation/README.instrument_list.md
|
||||
- afl-fuzz
|
||||
- Making AFL_MAP_SIZE obsolete - afl-fuzz now learns on start the
|
||||
target map size
|
||||
- Making AFL_MAP_SIZE (mostly) obsolete - afl-fuzz now learns on
|
||||
start the target map size
|
||||
- upgraded cmplog/redqueen: solving for floating point, solving
|
||||
transformations (e.g. toupper, tolower, to/from hex, xor,
|
||||
arithmetics, etc.). this is costly hence new command line option
|
||||
-l that sets the intensity (values 1 to 3). recommended is 1 or 2.
|
||||
- added `AFL_CMPLOG_ONLY_NEW` to not use cmplog on initial testcases from
|
||||
`-i` or resumes (as these have most likely already been done)
|
||||
arithmetics, etc.). This is costly hence new command line option
|
||||
`-l` that sets the intensity (values 1 to 3). Recommended is 2.
|
||||
- added `AFL_CMPLOG_ONLY_NEW` to not use cmplog on initial seeds
|
||||
from `-i` or resumes (these have most likely already been done)
|
||||
- fix crash for very, very fast targets+systems (thanks to mhlakhani
|
||||
for reporting)
|
||||
- on restarts (-i)/autoresume (AFL_AUTORESUME) the stats are now
|
||||
reloaded and used, thanks to Vimal Joseph for this PR!
|
||||
- if determinstic mode is active (-D, or -M without -d) then we sync
|
||||
after every queue entry as this can take very long time otherwise
|
||||
- on restarts (`-i`)/autoresume (AFL_AUTORESUME) the stats are now
|
||||
reloaded and used, thanks to Vimal Joseph for this patch!
|
||||
- changed the meaning of '+' of the '-t' option, it now means to
|
||||
auto-calculate the timeout with the value given being the max
|
||||
timeout. The original meaning of skipping timeouts instead of
|
||||
abort is now inherent to the -t option.
|
||||
- if deterministic mode is active (`-D`, or `-M` without `-d`) then
|
||||
we sync after every queue entry as this can take very long time
|
||||
otherwise
|
||||
- added minimum SYNC_TIME to include/config.h (30 minutes default)
|
||||
- better detection if a target needs a large shared map
|
||||
- fix for -Z
|
||||
- fix for `-Z`
|
||||
- fixed a few crashes
|
||||
- switched to an even faster RNG
|
||||
- added hghwng's patch for faster trace map analysis
|
||||
- printing suggestions for mistyped `AFL_` env variables
|
||||
- added Rust bindings for custom mutators (thanks @julihoh)
|
||||
- afl-cc
|
||||
- allow instrumenting LLVMFuzzerTestOneInput
|
||||
- fixed endless loop for allow/blocklist lines starting with a
|
||||
comment (thanks to Zherya for reporting)
|
||||
- cmplog/redqueen now also tracks floating point, _ExtInt() + 128bit
|
||||
- cmplog/redqueen can now process basic libc++ and libstdc++
|
||||
std::string comparisons (though no position or length type variants)
|
||||
- added support for __afl_coverage_interesting() for LTO and
|
||||
and our own PCGUARD (llvm 10.0.1+), read more about this function
|
||||
and selective coverage in instrumentation/README.instrument_list.md
|
||||
std::string comparisons (no position or length type variants)
|
||||
- added support for __afl_coverage_interesting() for LTO and our
|
||||
own PCGUARD (llvm 10.0.1+), read more about this function and
|
||||
selective coverage in instrumentation/README.instrument_list.md
|
||||
- added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard
|
||||
support (less performant than our own), GCC for old afl-gcc and
|
||||
CLANG for old afl-clang
|
||||
- fixed a potential crash in the LAF feature
|
||||
- workaround for llvm bitcast lto bug
|
||||
- workaround for llvm 13
|
||||
- qemuafl
|
||||
- ported QASan to qemuafl! see qemu_mode/libqasan/README.md
|
||||
- QASan (address sanitizer for Qemu) ported to qemuafl!
|
||||
See qemu_mode/libqasan/README.md
|
||||
- solved some persistent mode bugs (thanks Dil4rd)
|
||||
- solved an issue when dumping the memory maps (thanks wizche)
|
||||
- Android support for QASan
|
||||
- unicornafl
|
||||
- Substential speed gains in python bindings for certain use cases
|
||||
- Substantial speed gains in python bindings for certain use cases
|
||||
- Improved rust bindings
|
||||
- Added a new example harness to compare python, c, and rust bindings
|
||||
- Added a new example harness to compare python, c and rust bindings
|
||||
- afl-cmin and afl-showmap now support the -f option
|
||||
- afl_plot now also generates a graph on the discovered edges
|
||||
- changed default: no memory limit for afl-cmin and afl-cmin.bash
|
||||
- warn on any _AFL and __AFL env vars
|
||||
- LLVM mode is now compiled with -j4, unicorn with all cores. qemu was
|
||||
already building with all cores, the gcc plugin needs only one.
|
||||
- warn on any _AFL and __AFL env vars.
|
||||
- set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars
|
||||
- added dummy Makefile to instrumentation/
|
||||
- Updated utils/afl_frida to be 5% faster, 7% on x86_x64
|
||||
- Added AFL_KILL_SIGNAL env variable (thanks @v-p-b)
|
||||
- Added `AFL_KILL_SIGNAL` env variable (thanks @v-p-b)
|
||||
- @Edznux added a nice documentation on how to use rpc.statsd with
|
||||
afl++ in docs/rpc_statsd.md, thanks!
|
||||
|
||||
|
@ -4,6 +4,11 @@ This file describes how you can implement custom mutations to be used in AFL.
|
||||
For now, we support C/C++ library and Python module, collectivelly named as the
|
||||
custom mutator.
|
||||
|
||||
There is also experimental support for Rust in `custom_mutators/rust`.
|
||||
Please refer to that directory for documentation.
|
||||
Run ```cargo doc -p custom_mutator --open``` in that directory to view the
|
||||
documentation in your web browser.
|
||||
|
||||
Implemented by
|
||||
- C/C++ library (`*.so`): Khaled Yakdan from Code Intelligence (<yakdan@code-intelligence.de>)
|
||||
- Python module: Christian Holler from Mozilla (<choller@mozilla.com>)
|
||||
|
122
docs/docs.md
Normal file
122
docs/docs.md
Normal file
@ -0,0 +1,122 @@
|
||||
# Restructure afl++'s documentation
|
||||
|
||||
## About us
|
||||
|
||||
We are dedicated to everything around fuzzing, our main and most well known
|
||||
contribution is the fuzzer `afl++` which is part of all major Unix
|
||||
distributions (e.g. Debian, Arch, FreeBSD, etc.) and is deployed on Google's
|
||||
oss-fuzz and clusterfuzz. It is rated the top fuzzer on Google's fuzzbench.
|
||||
|
||||
We are four individuals from Europe supported by a large community.
|
||||
|
||||
All our tools are open source.
|
||||
|
||||
## About the afl++ fuzzer project
|
||||
|
||||
afl++ inherited it's documentation from the original Google afl project.
|
||||
Since then it has been massively improved - feature and performance wise -
|
||||
and although the documenation has likewise been continued it has grown out
|
||||
of proportion.
|
||||
The documentation is done by non-natives to the English language, plus
|
||||
none of us has a writer background.
|
||||
|
||||
We see questions on afl++ usage on mailing lists (e.g. afl-users), discord
|
||||
channels, web forums and as issues in our repository.
|
||||
|
||||
This only increases as afl++ has been on the top of Google's fuzzbench
|
||||
statistics (which measures the performance of fuzzers) and is now being
|
||||
integrated in Google's oss-fuzz and clusterfuzz - and is in many Unix
|
||||
packaging repositories, e.g. Debian, FreeBSD, etc.
|
||||
|
||||
afl++ now has 44 (!) documentation files with 13k total lines of content.
|
||||
This is way too much.
|
||||
|
||||
Hence afl++ needs a complete overhaul of it's documentation, both on a
|
||||
organisation/structural level as well as the content.
|
||||
|
||||
Overall the following actions have to be performed:
|
||||
* Create a better structure of documentation so it is easier to find the
|
||||
information that is being looked for, combining and/or splitting up the
|
||||
existing documents as needed.
|
||||
* Rewrite some documentation to remove duplication. Several information is
|
||||
present several times in the documentation. These should be removed to
|
||||
where needed so that we have as little bloat as possible.
|
||||
* The documents have been written and modified by a lot of different people,
|
||||
most of them non-native English speaker. Hence an overall review where
|
||||
parts should be rewritten has to be performed and then the rewrite done.
|
||||
* Create a cheat-sheet for a very short best-setup build and run of afl++
|
||||
* Pictures explain more than 1000 words. We need at least 4 images that
|
||||
explain the workflow with afl++:
|
||||
- the build workflow
|
||||
- the fuzzing workflow
|
||||
- the fuzzing campaign management workflow
|
||||
- the overall workflow that is an overview of the above
|
||||
- maybe more? where the technical writes seems it necessary for
|
||||
understanding.
|
||||
|
||||
Requirements:
|
||||
* Documentation has to be in Markdown format
|
||||
* Images have to be either in SVG or PNG format.
|
||||
* All documentation should be (moved) in(to) docs/
|
||||
|
||||
The project does not require writing new documentation or tutorials beside the
|
||||
cheat sheet. The technical information for the cheat sheet will be provided by
|
||||
us.
|
||||
|
||||
## Metrics
|
||||
|
||||
afl++ is a the highest performant fuzzer publicly available - but is also the
|
||||
most feature rich and complex. With the publicity of afl++' success and
|
||||
deployment in Google projects internally and externally and availability as
|
||||
a package on most Linux distributions we see more and more issues being
|
||||
created and help requests on our Discord channel that would not be
|
||||
necessary if people would have read through all our documentation - which
|
||||
is unrealistic.
|
||||
|
||||
We expect the the new documenation after this project to be cleaner, easier
|
||||
accessible and lighter to digest by our users, resulting in much less
|
||||
help requests. On the other hand the amount of users using afl++ should
|
||||
increase as well as it will be more accessible which would also increase
|
||||
questions again - but overall resulting in a reduction of help requests.
|
||||
|
||||
In numbers: we currently have per week on average 5 issues on Github,
|
||||
10 questions on discord and 1 on mailing lists that would not be necessary
|
||||
with perfect documentation and perfect people.
|
||||
|
||||
We would consider this project a success if afterwards we only have
|
||||
2 issues on Github and 3 questions on discord anymore that would be answered
|
||||
by reading the documentation. The mailing list is usually used by the most
|
||||
novice users and we don't expect any less questions there.
|
||||
|
||||
## Project Budget
|
||||
|
||||
We have zero experience with technical writers, so this is very hard for us
|
||||
to calculate. We expect it to be a lot of work though because of the amount
|
||||
of documentation we have that needs to be restructured and partially rewritten
|
||||
(44 documents with 13k total lines of content).
|
||||
|
||||
We assume the daily rate of a very good and experienced technical writer in
|
||||
times of a pandemic to be ~500$ (according to web research), and calculate
|
||||
the overall amout of work to be around 20 days for everything incl. the
|
||||
graphics (but again - this is basically just guessing).
|
||||
|
||||
Technical Writer 10000$
|
||||
Volunteer stipends 0$ (waved)
|
||||
T-Shirts for the top 10 contributors and helpers to this documentation project:
|
||||
10 afl++ logo t-shirts 20$ each 200$
|
||||
10 shipping cost of t-shirts 10$ each 100$
|
||||
|
||||
Total: 10.300$
|
||||
(in the submission form 10.280$ was entered)
|
||||
|
||||
## Additional Information
|
||||
|
||||
We have participated in Google Summer of Code in 2020 and hope to be selected
|
||||
again in 2021.
|
||||
|
||||
We have no experience with a technical writer, but we will support that person
|
||||
with video calls, chats, emails and messaging, provide all necessary information
|
||||
and write technical contents that is required for the success of this project.
|
||||
It is clear to us that a technical writer knows how to write, but cannot know
|
||||
the technical details in a complex tooling like in afl++. This guidance, input,
|
||||
etc. has to come from us.
|
@ -5,6 +5,10 @@
|
||||
users or for some types of custom fuzzing setups. See [README.md](README.md) for the general
|
||||
instruction manual.
|
||||
|
||||
Note that most tools will warn on any unknown AFL environment variables.
|
||||
This is for warning on typos that can happen. If you want to disable this
|
||||
check then set the `AFL_IGNORE_UNKNOWN_ENVS` environment variable.
|
||||
|
||||
## 1) Settings for all compilers
|
||||
|
||||
Starting with afl++ 3.0 there is only one compiler: afl-cc
|
||||
@ -18,11 +22,21 @@ To select the different instrumentation modes this can be done by
|
||||
`MODE` can be one of `LTO` (afl-clang-lto*), `LLVM` (afl-clang-fast*), `GCC_PLUGIN`
|
||||
(afl-g*-fast) or `GCC` (afl-gcc/afl-g++).
|
||||
|
||||
|
||||
Because (with the exception of the --afl-MODE command line option) the
|
||||
compile-time tools do not accept afl specific command-line options, they
|
||||
make fairly broad use of environmental variables instead:
|
||||
|
||||
- Some build/configure scripts break with afl++ compilers. To be able to
|
||||
pass them, do:
|
||||
```
|
||||
export CC=afl-cc
|
||||
export CXX=afl-c++
|
||||
export AFL_NOOPT=1
|
||||
./configure --disable-shared --disabler-werror
|
||||
unset AFL_NOOPT
|
||||
make
|
||||
```
|
||||
|
||||
- Most afl tools do not print any output if stdout/stderr are redirected.
|
||||
If you want to get the output into a file then set the `AFL_DEBUG`
|
||||
environment variable.
|
||||
@ -156,7 +170,6 @@ Then there are a few specific features that are only available in instrumentatio
|
||||
This defaults to 1
|
||||
- `AFL_LLVM_LTO_DONTWRITEID` prevents that the highest location ID written
|
||||
into the instrumentation is set in a global variable
|
||||
- `AFL_LLVM_LTO_CALLGRAPH` will not generate a binary but a callgraph instead
|
||||
|
||||
See [instrumentation/README.lto.md](../instrumentation/README.lto.md) for more information.
|
||||
|
||||
@ -449,6 +462,7 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
`banner` corresponds to the name of the fuzzer provided through `-M/-S`.
|
||||
`afl_version` corresponds to the currently running afl version (e.g `++3.0c`).
|
||||
Default (empty/non present) will add no tags to the metrics.
|
||||
See [rpc_statsd.md](rpc_statsd.md) for more information.
|
||||
|
||||
- Setting `AFL_CRASH_EXITCODE` sets the exit code afl treats as crash.
|
||||
For example, if `AFL_CRASH_EXITCODE='-1'` is set, each input resulting
|
||||
@ -515,6 +529,12 @@ The QEMU wrapper used to instrument binary-only code supports several settings:
|
||||
stack pointer in which QEMU can find the return address when `start addr` is
|
||||
hit.
|
||||
|
||||
- With `AFL_USE_QASAN` you can enable QEMU AddressSanitizer for dynamically
|
||||
linked binaries.
|
||||
|
||||
- With `AFL_QEMU_FORCE_DFL` you force QEMU to ignore the registered signal
|
||||
handlers of the target.
|
||||
|
||||
## 6) Settings for afl-cmin
|
||||
|
||||
The corpus minimization script offers very little customization:
|
||||
|
@ -3,6 +3,42 @@
|
||||
In the following, we describe a variety of ideas that could be implemented
|
||||
for future AFL++ versions.
|
||||
|
||||
# GSoC 2021
|
||||
|
||||
All GSoC 2021 projects will be in the Rust development language!
|
||||
|
||||
## UI for libaflrs
|
||||
|
||||
Write a user interface to libaflrs, the upcoming backend of afl++.
|
||||
This might look like the afl-fuzz UI, but you can improve on it - and should!
|
||||
|
||||
## Schedulers for libaflrs
|
||||
|
||||
Schedulers is a mechanism that selects items from the fuzzing corpus based
|
||||
on strategy and randomness. One scheduler might focus on long paths,
|
||||
another on rarity of edges disocvered, still another on a combination on
|
||||
things. Some of the schedulers in afl++ have to be ported, but you are free
|
||||
to come up with your own if you want to - and see how it performs.
|
||||
|
||||
## Forkserver support for libaflrs
|
||||
|
||||
The current libaflrs implementation fuzzes in-memory, however obviously we
|
||||
want to support afl instrumented binaries as well.
|
||||
Hence a forkserver support needs to be implemented - forking off the target
|
||||
and talking to the target via a socketpair and the communication protocol
|
||||
within.
|
||||
|
||||
## More Observers for libaflrs
|
||||
|
||||
An observer is measuring functionality that looks at the target being fuzzed
|
||||
and documents something about it. In traditional fuzzing this is the coverage
|
||||
in the target, however we want to add various more observers, e.g. stack depth,
|
||||
heap usage, etc. - this is a topic for an experienced Rust developer.
|
||||
|
||||
# Generic ideas and wishlist - NOT PART OF GSoC 2021 !
|
||||
|
||||
The below list is not part of GSoC 2021.
|
||||
|
||||
## Analysis software
|
||||
|
||||
Currently analysis is done by using afl-plot, which is rather outdated.
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Remote monitoring with StatsD
|
||||
|
||||
StatsD allows you to receive and aggregate metrics from a wide range of application and retransmit them to the backend of your choice.
|
||||
StatsD allows you to receive and aggregate metrics from a wide range of applications and retransmit them to the backend of your choice.
|
||||
This enables you to create nice and readable dashboards containing all the information you need on your fuzzer instances.
|
||||
No need to write your own statistics parsing system, deploy and maintain it to all your instances, sync with your graph rendering system...
|
||||
|
||||
@ -45,7 +45,7 @@ For more information on these env vars, check out `docs/env_variables.md`.
|
||||
|
||||
The simplest way of using this feature is to use any metric provider and change the host/port of your StatsD daemon,
|
||||
with `AFL_STATSD_HOST` and `AFL_STATSD_PORT`, if required (defaults are `localhost` and port `8125`).
|
||||
To get started, here are some instruction with free and open source tools.
|
||||
To get started, here are some instructions with free and open source tools.
|
||||
The following setup is based on Prometheus, statsd_exporter and Grafana.
|
||||
Grafana here is not mandatory, but gives you some nice graphs and features.
|
||||
|
||||
@ -131,7 +131,7 @@ mappings:
|
||||
|
||||
Run `docker-compose up -d`.
|
||||
|
||||
Everything should be now setup, you are now able to run your fuzzers with
|
||||
Everything should now be setup, you are now able to run your fuzzers with
|
||||
|
||||
```
|
||||
AFL_STATSD_TAGS_FLAVOR=dogstatsd AFL_STATSD=1 afl-fuzz -M test-fuzzer-1 -i i -o o ./bin/my-application @@
|
||||
@ -139,5 +139,5 @@ AFL_STATSD_TAGS_FLAVOR=dogstatsd AFL_STATSD=1 afl-fuzz -S test-fuzzer-2 -i i -o
|
||||
...
|
||||
```
|
||||
|
||||
This setup may be modified before use in production environment. Depending on your needs: addind passwords, creating volumes for storage,
|
||||
This setup may be modified before use in a production environment. Depending on your needs: adding passwords, creating volumes for storage,
|
||||
tweaking the metrics gathering to get host metrics (CPU, RAM ...).
|
||||
|
@ -570,6 +570,7 @@ typedef struct afl_state {
|
||||
blocks_eff_total, /* Blocks subject to effector maps */
|
||||
blocks_eff_select, /* Blocks selected as fuzzable */
|
||||
start_time, /* Unix start time (ms) */
|
||||
last_sync_time, /* Time of last sync */
|
||||
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) */
|
||||
@ -649,6 +650,7 @@ typedef struct afl_state {
|
||||
u32 cmplog_max_filesize;
|
||||
u32 cmplog_lvl;
|
||||
u32 colorize_success;
|
||||
u8 cmplog_enable_arith, cmplog_enable_transform;
|
||||
|
||||
struct afl_pass_stat *pass_stats;
|
||||
struct cmp_map * orig_cmp_map;
|
||||
@ -1060,6 +1062,7 @@ u8 has_new_bits_unclassified(afl_state_t *, u8 *);
|
||||
void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32);
|
||||
void load_extras(afl_state_t *, u8 *);
|
||||
void dedup_extras(afl_state_t *);
|
||||
void deunicode_extras(afl_state_t *);
|
||||
void add_extra(afl_state_t *afl, u8 *mem, u32 len);
|
||||
void maybe_add_auto(afl_state_t *, u8 *, u32);
|
||||
void save_auto(afl_state_t *);
|
||||
@ -1070,8 +1073,8 @@ void destroy_extras(afl_state_t *);
|
||||
|
||||
void load_stats_file(afl_state_t *);
|
||||
void write_setup_file(afl_state_t *, u32, char **);
|
||||
void write_stats_file(afl_state_t *, double, double, double);
|
||||
void maybe_update_plot_file(afl_state_t *, double, double);
|
||||
void write_stats_file(afl_state_t *, u32, double, double, double);
|
||||
void maybe_update_plot_file(afl_state_t *, u32, double, double);
|
||||
void show_stats(afl_state_t *);
|
||||
void show_init_stats(afl_state_t *);
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#define STRINGIFY_VAL_SIZE_MAX (16)
|
||||
|
||||
void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin);
|
||||
void print_suggested_envs(char *mispelled_env);
|
||||
void check_environment_vars(char **env);
|
||||
|
||||
char **argv_cpy_dup(int argc, char **argv);
|
||||
|
@ -10,7 +10,7 @@
|
||||
Dominik Maier <mail@dmnk.co>
|
||||
|
||||
Copyright 2016, 2017 Google Inc. All rights reserved.
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2021 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.
|
||||
@ -25,8 +25,8 @@
|
||||
|
||||
/* Version string: */
|
||||
|
||||
// c = release, d = volatile github dev, e = experimental branch
|
||||
#define VERSION "++3.01a"
|
||||
// c = release, a = volatile github dev, e = experimental branch
|
||||
#define VERSION "++3.11c"
|
||||
|
||||
/******************************************************
|
||||
* *
|
||||
@ -36,26 +36,27 @@
|
||||
|
||||
/* CMPLOG/REDQUEEN TUNING
|
||||
*
|
||||
* Here you can tuning and solving options for cmplog.
|
||||
* Here you can modify tuning and solving options for CMPLOG.
|
||||
* Note that these are run-time options for afl-fuzz, no target
|
||||
* recompilation required.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Enable transform following (XOR/ADD/SUB manipulations, hex en/decoding) */
|
||||
// #define CMPLOG_TRANSFORM
|
||||
/* if TRANSFORM is enabled with '-l T', this additionally enables base64
|
||||
encoding/decoding */
|
||||
// #define CMPLOG_SOLVE_TRANSFORM_BASE64
|
||||
|
||||
/* if TRANSFORM is enabled, this additionally enables base64 en/decoding */
|
||||
// #define CMPLOG_TRANSFORM_BASE64
|
||||
/* If a redqueen pass finds more than one solution, try to combine them? */
|
||||
#define CMPLOG_COMBINE
|
||||
|
||||
/* Minimum % of the corpus to perform cmplog on. Default: 20% */
|
||||
#define CMPLOG_CORPUS_PERCENT 20U
|
||||
/* Minimum % of the corpus to perform cmplog on. Default: 10% */
|
||||
#define CMPLOG_CORPUS_PERCENT 5U
|
||||
|
||||
/* Number of potential posititions from which we decide the cmplog becomes
|
||||
useless, default 16384 */
|
||||
#define CMPLOG_POSITIONS_MAX 16384U
|
||||
/* Number of potential positions from which we decide if cmplog becomes
|
||||
useless, default 8096 */
|
||||
#define CMPLOG_POSITIONS_MAX (12 * 1024)
|
||||
|
||||
/* Maximum allowed fails per CMP value. Default: 32 * 3 */
|
||||
/* Maximum allowed fails per CMP value. Default: 128 */
|
||||
#define CMPLOG_FAIL_MAX 96
|
||||
|
||||
/* Now non-cmplog configuration options */
|
||||
@ -279,6 +280,11 @@
|
||||
|
||||
#define SYNC_INTERVAL 8
|
||||
|
||||
/* Sync time (minimum time between syncing in ms, time is halfed for -M main
|
||||
nodes) - default is 30 minutes: */
|
||||
|
||||
#define SYNC_TIME (30 * 60 * 1000)
|
||||
|
||||
/* Output directory reuse grace period (minutes): */
|
||||
|
||||
#define OUTPUT_GRACE 25
|
||||
|
@ -61,6 +61,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_FORKSRV_INIT_TMOUT",
|
||||
"AFL_HARDEN",
|
||||
"AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES",
|
||||
"AFL_IGNORE_UNKNOWN_ENVS",
|
||||
"AFL_IMPORT_FIRST",
|
||||
"AFL_INST_LIBS",
|
||||
"AFL_INST_RATIO",
|
||||
@ -79,7 +80,9 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_LLVM_BLOCKLIST",
|
||||
"AFL_LLVM_CMPLOG",
|
||||
"AFL_LLVM_INSTRIM",
|
||||
"AFL_LLVM_CALLER",
|
||||
"AFL_LLVM_CTX",
|
||||
"AFL_LLVM_CTX_K",
|
||||
"AFL_LLVM_DICT2FILE",
|
||||
"AFL_LLVM_DOCUMENT_IDS",
|
||||
"AFL_LLVM_INSTRIM_LOOPHEAD",
|
||||
@ -102,9 +105,8 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_LLVM_INSTRUMENT_FILE",
|
||||
"AFL_LLVM_SKIP_NEVERZERO",
|
||||
"AFL_NO_AFFINITY",
|
||||
"AFL_LLVM_LTO_CALLGRAPH",
|
||||
"AFL_LLVM_LTO_DONTWRITEID",
|
||||
"AFL_LLVM_LTO_STARTID",
|
||||
"AFL_LLVM_LTO_DONTWRITEID",
|
||||
"AFL_NO_ARITH",
|
||||
"AFL_NO_AUTODICT",
|
||||
"AFL_NO_BUILTIN",
|
||||
@ -118,10 +120,12 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_NO_PYTHON",
|
||||
"AFL_UNTRACER_FILE",
|
||||
"AFL_LLVM_USE_TRACE_PC",
|
||||
"AFL_NO_X86", // not really an env but we dont want to warn on it
|
||||
"AFL_MAP_SIZE",
|
||||
"AFL_MAPSIZE",
|
||||
"AFL_MAX_DET_EXTRAS",
|
||||
"AFL_NO_X86", // not really an env but we dont want to warn on it
|
||||
"AFL_NOOPT",
|
||||
"AFL_PASSTHROUGH",
|
||||
"AFL_PATH",
|
||||
"AFL_PERFORMANCE_FILE",
|
||||
"AFL_PRELOAD",
|
||||
@ -131,6 +135,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_QEMU_DEBUG_MAPS",
|
||||
"AFL_QEMU_DISABLE_CACHE",
|
||||
"AFL_QEMU_DRIVER_NO_HOOK",
|
||||
"AFL_QEMU_FORCE_DFL",
|
||||
"AFL_QEMU_PERSISTENT_ADDR",
|
||||
"AFL_QEMU_PERSISTENT_CNT",
|
||||
"AFL_QEMU_PERSISTENT_GPR",
|
||||
@ -140,6 +145,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_QEMU_PERSISTENT_RETADDR_OFFSET",
|
||||
"AFL_QEMU_PERSISTENT_EXITS",
|
||||
"AFL_QEMU_INST_RANGES",
|
||||
"AFL_QEMU_EXCLUDE_RANGES",
|
||||
"AFL_QEMU_SNAPSHOT",
|
||||
"AFL_QUIET",
|
||||
"AFL_RANDOM_ALLOC_CANARY",
|
||||
|
@ -38,7 +38,7 @@ typedef long double max_align_t;
|
||||
|
||||
#include "MarkNodes.h"
|
||||
#include "afl-llvm-common.h"
|
||||
#include "llvm-ngram-coverage.h"
|
||||
#include "llvm-alternative-coverage.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
@ -135,7 +135,7 @@ struct InsTrim : public ModulePass {
|
||||
unsigned int PrevLocSize = 0;
|
||||
char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE");
|
||||
if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE");
|
||||
char *ctx_str = getenv("AFL_LLVM_CTX");
|
||||
char *caller_str = getenv("AFL_LLVM_CALLER");
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
unsigned int ngram_size = 0;
|
||||
@ -197,9 +197,9 @@ struct InsTrim : public ModulePass {
|
||||
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
||||
GlobalVariable *AFLPrevLoc;
|
||||
GlobalVariable *AFLContext = NULL;
|
||||
LoadInst * PrevCtx = NULL; // for CTX sensitive coverage
|
||||
LoadInst * PrevCaller = NULL; // for CALLER sensitive coverage
|
||||
|
||||
if (ctx_str)
|
||||
if (caller_str)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
AFLContext = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
|
||||
@ -398,11 +398,11 @@ struct InsTrim : public ModulePass {
|
||||
unsigned int cur_loc;
|
||||
|
||||
// Context sensitive coverage
|
||||
if (ctx_str && &BB == &F.getEntryBlock()) {
|
||||
if (caller_str && &BB == &F.getEntryBlock()) {
|
||||
|
||||
PrevCtx = IRB.CreateLoad(AFLContext);
|
||||
PrevCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
PrevCaller = IRB.CreateLoad(AFLContext);
|
||||
PrevCaller->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
// does the function have calls? and is any of the calls larger than
|
||||
// one basic block?
|
||||
@ -441,7 +441,7 @@ struct InsTrim : public ModulePass {
|
||||
|
||||
}
|
||||
|
||||
} // END of ctx_str
|
||||
} // END of caller_str
|
||||
|
||||
if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; }
|
||||
|
||||
@ -485,9 +485,9 @@ struct InsTrim : public ModulePass {
|
||||
#endif
|
||||
PrevLocTrans = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
|
||||
|
||||
if (ctx_str)
|
||||
if (caller_str)
|
||||
PrevLocTrans =
|
||||
IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty);
|
||||
IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCaller), Int32Ty);
|
||||
|
||||
/* Load SHM pointer */
|
||||
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
|
||||
@ -535,16 +535,17 @@ struct InsTrim : public ModulePass {
|
||||
IRB.CreateStore(Incr, MapPtrIdx)
|
||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
|
||||
if (ctx_str && has_calls) {
|
||||
if (caller_str && has_calls) {
|
||||
|
||||
// in CTX mode we have to restore the original context for the
|
||||
// in CALLER mode we have to restore the original context for the
|
||||
// caller - she might be calling other functions which need the
|
||||
// correct CTX
|
||||
// correct CALLER
|
||||
Instruction *Inst = BB.getTerminator();
|
||||
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
|
||||
|
||||
IRBuilder<> Post_IRB(Inst);
|
||||
StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
|
||||
StoreInst * RestoreCtx =
|
||||
Post_IRB.CreateStore(PrevCaller, AFLContext);
|
||||
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
# CmpLog instrumentation
|
||||
|
||||
The CmpLog instrumentation enables the logging of the comparisons operands in a
|
||||
The CmpLog instrumentation enables logging of comparison operands in a
|
||||
shared memory.
|
||||
|
||||
These values can be used by various mutators built on top of it.
|
||||
At the moment we support the RedQueen mutator (input-2-state instructions only).
|
||||
At the moment we support the RedQueen mutator (input-2-state instructions only),
|
||||
for details see [the RedQueen paper](https://www.syssec.ruhr-uni-bochum.de/media/emma/veroeffentlichungen/2018/12/17/NDSS19-Redqueen.pdf).
|
||||
|
||||
## Build
|
||||
|
||||
@ -13,7 +14,7 @@ program.
|
||||
|
||||
The first version is built using the regular AFL++ instrumentation.
|
||||
|
||||
The second one, the CmpLog binary, with setting AFL_LLVM_CMPLOG during the compilation.
|
||||
The second one, the CmpLog binary, is built with setting AFL_LLVM_CMPLOG during the compilation.
|
||||
|
||||
For example:
|
||||
|
||||
@ -26,11 +27,12 @@ export AFL_LLVM_CMPLOG=1
|
||||
./configure --cc=~/path/to/afl-clang-fast
|
||||
make
|
||||
cp ./program ./program.cmplog
|
||||
unset AFL_LLVM_CMPLOG
|
||||
```
|
||||
|
||||
## Use
|
||||
|
||||
AFL++ has the new -c option that needs to be used to specify the CmpLog binary (the second
|
||||
AFL++ has the new `-c` option that needs to be used to specify the CmpLog binary (the second
|
||||
build).
|
||||
|
||||
For example:
|
||||
|
@ -4,14 +4,19 @@
|
||||
|
||||
This is an LLVM-based implementation of the context sensitive branch coverage.
|
||||
|
||||
Basically every function gets its own ID and that ID is combined with the
|
||||
edges of the called functions.
|
||||
Basically every function gets its own ID and, every time when an edge is logged,
|
||||
all the IDs in the callstack are hashed and combined with the edge transition
|
||||
hash to augment the classic edge coverage with the information about the
|
||||
calling context.
|
||||
|
||||
So if both function A and function B call a function C, the coverage
|
||||
collected in C will be different.
|
||||
|
||||
In math the coverage is collected as follows:
|
||||
`map[current_location_ID ^ previous_location_ID >> 1 ^ previous_callee_ID] += 1`
|
||||
`map[current_location_ID ^ previous_location_ID >> 1 ^ hash_callstack_IDs] += 1`
|
||||
|
||||
The callstack hash is produced XOR-ing the function IDs to avoid explosion with
|
||||
recursive functions.
|
||||
|
||||
## Usage
|
||||
|
||||
@ -20,3 +25,14 @@ Set the `AFL_LLVM_INSTRUMENT=CTX` or `AFL_LLVM_CTX=1` environment variable.
|
||||
It is highly recommended to increase the MAP_SIZE_POW2 definition in
|
||||
config.h to at least 18 and maybe up to 20 for this as otherwise too
|
||||
many map collisions occur.
|
||||
|
||||
## Caller Branch Coverage
|
||||
|
||||
If the context sensitive coverage introduces too may collisions and becoming
|
||||
detrimental, the user can choose to augment edge coverage with just the
|
||||
called function ID, instead of the entire callstack hash.
|
||||
|
||||
In math the coverage is collected as follows:
|
||||
`map[current_location_ID ^ previous_location_ID >> 1 ^ previous_callee_ID] += 1`
|
||||
|
||||
Set the `AFL_LLVM_INSTRUMENT=CALLER` or `AFL_LLVM_CALLER=1` environment variable.
|
||||
|
@ -3,16 +3,20 @@
|
||||
See [../README.md](../README.md) for the general instruction manual.
|
||||
See [README.llvm.md](README.llvm.md) for the LLVM-based instrumentation.
|
||||
|
||||
This document describes how to build and use `afl-gcc-fast` and `afl-g++-fast`,
|
||||
which instrument the target with the help of gcc plugins.
|
||||
|
||||
TLDR:
|
||||
* `apt-get install gcc-VERSION-plugin-dev`
|
||||
* `make`
|
||||
* gcc and g++ must point to the gcc-VERSION you you have to set AFL_CC/AFL_CXX
|
||||
* check the version of your gcc compiler: `gcc --version`
|
||||
* `apt-get install gcc-VERSION-plugin-dev` or similar to install headers for gcc plugins
|
||||
* `gcc` and `g++` must match the gcc-VERSION you installed headers for. You can set `AFL_CC`/`AFL_CXX`
|
||||
to point to these!
|
||||
* just use afl-gcc-fast/afl-g++-fast normally like you would afl-clang-fast
|
||||
* `make`
|
||||
* just use `afl-gcc-fast`/`afl-g++-fast` normally like you would do with `afl-clang-fast`
|
||||
|
||||
## 1) Introduction
|
||||
|
||||
The code in this directory allows you to instrument programs for AFL using
|
||||
The code in this directory allows to instrument programs for AFL using
|
||||
true compiler-level instrumentation, instead of the more crude
|
||||
assembly-level rewriting approach taken by afl-gcc and afl-clang. This has
|
||||
several interesting properties:
|
||||
@ -27,10 +31,10 @@ several interesting properties:
|
||||
|
||||
- The instrumentation is CPU-independent. At least in principle, you should
|
||||
be able to rely on it to fuzz programs on non-x86 architectures (after
|
||||
building afl-fuzz with AFL_NOX86=1).
|
||||
building `afl-fuzz` with `AFL_NOX86=1`).
|
||||
|
||||
- Because the feature relies on the internals of GCC, it is gcc-specific
|
||||
and will *not* work with LLVM (see ../llvm_mode for an alternative).
|
||||
and will *not* work with LLVM (see [README.llvm.md](README.llvm.md) for an alternative).
|
||||
|
||||
Once this implementation is shown to be sufficiently robust and portable, it
|
||||
will probably replace afl-gcc. For now, it can be built separately and
|
||||
@ -41,29 +45,32 @@ The idea and much of the implementation comes from Laszlo Szekeres.
|
||||
## 2) How to use
|
||||
|
||||
In order to leverage this mechanism, you need to have modern enough GCC
|
||||
(>= version 4.5.0) and the plugin headers installed on your system. That
|
||||
(>= version 4.5.0) and the plugin development headers installed on your system. That
|
||||
should be all you need. On Debian machines, these headers can be acquired by
|
||||
installing the `gcc-VERSION-plugin-dev` packages.
|
||||
|
||||
To build the instrumentation itself, type 'make'. This will generate binaries
|
||||
called afl-gcc-fast and afl-g++-fast in the parent directory.
|
||||
To build the instrumentation itself, type `make`. This will generate binaries
|
||||
called `afl-gcc-fast` and `afl-g++-fast` in the parent directory.
|
||||
|
||||
The gcc and g++ compiler links have to point to gcc-VERSION - or set these
|
||||
by pointing the environment variables AFL_CC/AFL_CXX to them.
|
||||
If the CC/CXX have been overridden, those compilers will be used from
|
||||
those wrappers without using AFL_CXX/AFL_CC settings.
|
||||
by pointing the environment variables `AFL_CC`/`AFL_CXX` to them.
|
||||
If the `CC`/`CXX` environment variables have been set, those compilers will be
|
||||
preferred over those from the `AFL_CC`/`AFL_CXX` settings.
|
||||
|
||||
Once this is done, you can instrument third-party code in a way similar to the
|
||||
standard operating mode of AFL, e.g.:
|
||||
|
||||
CC=/path/to/afl/afl-gcc-fast ./configure [...options...]
|
||||
```
|
||||
CC=/path/to/afl/afl-gcc-fast
|
||||
CXX=/path/to/afl/afl-g++-fast
|
||||
export CC CXX
|
||||
./configure [...options...]
|
||||
make
|
||||
```
|
||||
Note: We also used `CXX` to set the C++ compiler to `afl-g++-fast` for C++ code.
|
||||
|
||||
Be sure to also include CXX set to afl-g++-fast for C++ code.
|
||||
|
||||
The tool honors roughly the same environmental variables as afl-gcc (see
|
||||
[env_variables.md](../docs/env_variables.md). This includes AFL_INST_RATIO,
|
||||
AFL_USE_ASAN, AFL_HARDEN, and AFL_DONT_OPTIMIZE.
|
||||
The tool honors roughly the same environmental variables as `afl-gcc` (see
|
||||
[env_variables.md](../docs/env_variables.md). This includes `AFL_INST_RATIO`,
|
||||
`AFL_USE_ASAN`, `AFL_HARDEN`, and `AFL_DONT_OPTIMIZE`.
|
||||
|
||||
Note: if you want the GCC plugin to be installed on your system for all
|
||||
users, you need to build it before issuing 'make install' in the parent
|
||||
@ -72,7 +79,7 @@ directory.
|
||||
## 3) Gotchas, feedback, bugs
|
||||
|
||||
This is an early-stage mechanism, so field reports are welcome. You can send bug
|
||||
reports to afl@aflplus.plus
|
||||
reports to afl@aflplus.plus.
|
||||
|
||||
## 4) Bonus feature #1: deferred initialization
|
||||
|
||||
@ -88,7 +95,7 @@ file before getting to the fuzzed data.
|
||||
In such cases, it's beneficial to initialize the forkserver a bit later, once
|
||||
most of the initialization work is already done, but before the binary attempts
|
||||
to read the fuzzed input and parse it; in some cases, this can offer a 10x+
|
||||
performance gain. You can implement delayed initialization in LLVM mode in a
|
||||
performance gain. You can implement delayed initialization in GCC mode in a
|
||||
fairly simple way.
|
||||
|
||||
First, locate a suitable location in the code where the delayed cloning can
|
||||
@ -117,7 +124,7 @@ With the location selected, add this code in the appropriate spot:
|
||||
```
|
||||
|
||||
You don't need the #ifdef guards, but they will make the program still work as
|
||||
usual when compiled with a tool other than afl-gcc-fast/afl-clang-fast.
|
||||
usual when compiled with a compiler other than afl-gcc-fast/afl-clang-fast.
|
||||
|
||||
Finally, recompile the program with afl-gcc-fast (afl-gcc or afl-clang will
|
||||
*not* generate a deferred-initialization binary) - and you should be all set!
|
||||
@ -127,7 +134,7 @@ Finally, recompile the program with afl-gcc-fast (afl-gcc or afl-clang will
|
||||
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
|
||||
single long-lived process can be reused to try out multiple test cases,
|
||||
eliminating the need for repeated fork() calls and the associated OS overhead.
|
||||
eliminating the need for repeated `fork()` calls and the associated OS overhead.
|
||||
|
||||
The basic structure of the program that does this would be:
|
||||
|
||||
@ -160,5 +167,9 @@ wary of memory leaks and the state of file descriptors.
|
||||
When running in this mode, the execution paths will inherently vary a bit
|
||||
depending on whether the input loop is being entered for the first time or
|
||||
executed again. To avoid spurious warnings, the feature implies
|
||||
AFL_NO_VAR_CHECK and hides the "variable path" warnings in the UI.
|
||||
`AFL_NO_VAR_CHECK` and hides the "variable path" warnings in the UI.
|
||||
|
||||
## 6) Bonus feature #3: selective instrumentation
|
||||
|
||||
It can be more effective to fuzzing to only instrument parts of the code.
|
||||
For details see [README.instrument_list.md](README.instrument_list.md).
|
||||
|
@ -47,10 +47,10 @@ A special function is `__afl_coverage_interesting`.
|
||||
To use this, you must define `void __afl_coverage_interesting(u8 val, u32 id);`.
|
||||
Then you can use this function globally, where the `val` parameter can be set
|
||||
by you, the `id` parameter is for afl-fuzz and will be overwritten.
|
||||
Note that useful parameters are for `val` are: 1, 2, 3, 4, 8, 16, 32, 64, 128.
|
||||
Note that useful parameters for `val` are: 1, 2, 3, 4, 8, 16, 32, 64, 128.
|
||||
A value of e.g. 33 will be seen as 32 for coverage purposes.
|
||||
|
||||
## 3) Selective instrumenation with AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST
|
||||
## 3) Selective instrumentation with AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST
|
||||
|
||||
This feature is equivalent to llvm 12 sancov feature and allows to specify
|
||||
on a filename and/or function name level to instrument these or skip them.
|
||||
|
@ -88,16 +88,35 @@ apt-get install -y clang-12 clang-tools-12 libc++1-12 libc++-12-dev \
|
||||
### Building llvm yourself (version 12)
|
||||
|
||||
Building llvm from github takes quite some long time and is not painless:
|
||||
```
|
||||
```sh
|
||||
sudo apt install binutils-dev # this is *essential*!
|
||||
git clone https://github.com/llvm/llvm-project
|
||||
git clone --depth=1 https://github.com/llvm/llvm-project
|
||||
cd llvm-project
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DLLVM_ENABLE_PROJECTS='clang;clang-tools-extra;compiler-rt;libclc;libcxx;libcxxabi;libunwind;lld' -DCMAKE_BUILD_TYPE=Release -DLLVM_BINUTILS_INCDIR=/usr/include/ ../llvm/
|
||||
make -j $(nproc)
|
||||
export PATH=`pwd`/bin:$PATH
|
||||
export LLVM_CONFIG=`pwd`/bin/llvm-config
|
||||
|
||||
# Add -G Ninja if ninja-build installed
|
||||
# "Building with ninja significantly improves your build time, especially with
|
||||
# incremental builds, and improves your memory usage."
|
||||
cmake \
|
||||
-DCLANG_INCLUDE_DOCS="OFF" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_BINUTILS_INCDIR=/usr/include/ \
|
||||
-DLLVM_BUILD_LLVM_DYLIB="ON" \
|
||||
-DLLVM_ENABLE_BINDINGS="OFF" \
|
||||
-DLLVM_ENABLE_PROJECTS='clang;compiler-rt;libcxx;libcxxabi;libunwind;lld' \
|
||||
-DLLVM_ENABLE_WARNINGS="OFF" \
|
||||
-DLLVM_INCLUDE_BENCHMARKS="OFF" \
|
||||
-DLLVM_INCLUDE_DOCS="OFF" \
|
||||
-DLLVM_INCLUDE_EXAMPLES="OFF" \
|
||||
-DLLVM_INCLUDE_TESTS="OFF" \
|
||||
-DLLVM_LINK_LLVM_DYLIB="ON" \
|
||||
-DLLVM_TARGETS_TO_BUILD="host" \
|
||||
../llvm/
|
||||
cmake --build . --parallel
|
||||
export PATH="$(pwd)/bin:$PATH"
|
||||
export LLVM_CONFIG="$(pwd)/bin/llvm-config"
|
||||
export LD_LIBRARY_PATH="$(llvm-config --libdir)${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
cd /path/to/AFLplusplus/
|
||||
make
|
||||
sudo make install
|
||||
|
@ -16,7 +16,7 @@ at a very little cost (one instruction per edge).
|
||||
(The alternative of saturated counters has been tested also and proved to be
|
||||
inferior in terms of path discovery.)
|
||||
|
||||
This is implemented in afl-gcc, however for llvm_mode this is optional if
|
||||
This is implemented in afl-gcc and afl-gcc-fast, however for llvm_mode this is optional if
|
||||
the llvm version is below 9 - as there is a perfomance bug that is only fixed
|
||||
in version 9 and onwards.
|
||||
|
||||
|
@ -10,8 +10,8 @@ by Jinghan Wang, et. al.
|
||||
Note that the original implementation (available
|
||||
[here](https://github.com/bitsecurerlab/afl-sensitive))
|
||||
is built on top of AFL's QEMU mode.
|
||||
This is essentially a port that uses LLVM vectorized instructions to achieve
|
||||
the same results when compiling source code.
|
||||
This is essentially a port that uses LLVM vectorized instructions (available from
|
||||
llvm versions 4.0.1 and higher) to achieve the same results when compiling source code.
|
||||
|
||||
In math the branch coverage is performed as follows:
|
||||
`map[current_location ^ prev_location[0] >> 1 ^ prev_location[1] >> 1 ^ ... up to n-1`] += 1`
|
||||
|
@ -1,18 +1,16 @@
|
||||
===========================================
|
||||
Using afl++ without inlined instrumentation
|
||||
===========================================
|
||||
## Using afl++ without inlined instrumentation
|
||||
|
||||
This file describes how you can disable inlining of instrumentation.
|
||||
|
||||
|
||||
By default, the GCC plugin will duplicate the effects of calling
|
||||
__afl_trace (see afl-gcc-rt.o.c) in instrumented code, instead of
|
||||
`__afl_trace` (see `afl-gcc-rt.o.c`) in instrumented code, instead of
|
||||
issuing function calls.
|
||||
|
||||
The calls are presumed to be slower, more so because the rt file
|
||||
itself is not optimized by the compiler.
|
||||
|
||||
Setting AFL_GCC_OUT_OF_LINE=1 in the environment while compiling code
|
||||
Setting `AFL_GCC_OUT_OF_LINE=1` in the environment while compiling code
|
||||
with the plugin will disable this inlining, issuing calls to the
|
||||
unoptimized runtime instead.
|
||||
|
||||
|
@ -16,7 +16,7 @@ Examples can be found in [utils/persistent_mode](../utils/persistent_mode).
|
||||
## 2) TLDR;
|
||||
|
||||
Example `fuzz_target.c`:
|
||||
```
|
||||
```c
|
||||
#include "what_you_need_for_your_target.h"
|
||||
|
||||
__AFL_FUZZ_INIT();
|
||||
@ -60,14 +60,14 @@ The speed increase is usually x10 to x20.
|
||||
If you want to be able to compile the target without afl-clang-fast/lto then
|
||||
add this just after the includes:
|
||||
|
||||
```
|
||||
```c
|
||||
#ifndef __AFL_FUZZ_TESTCASE_LEN
|
||||
ssize_t fuzz_len;
|
||||
#define __AFL_FUZZ_TESTCASE_LEN fuzz_len
|
||||
unsigned char fuzz_buf[1024000];
|
||||
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
|
||||
#define __AFL_FUZZ_INIT() void sync(void);
|
||||
#define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ?
|
||||
#define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
|
||||
#define __AFL_INIT() sync()
|
||||
#endif
|
||||
```
|
||||
@ -75,7 +75,7 @@ add this just after the includes:
|
||||
## 3) Deferred initialization
|
||||
|
||||
AFL tries to optimize performance by executing the targeted binary just once,
|
||||
stopping it just before main(), and then cloning this "main" process to get
|
||||
stopping it just before `main()`, and then cloning this "main" process to get
|
||||
a steady supply of targets to fuzz.
|
||||
|
||||
Although this approach eliminates much of the OS-, linker- and libc-level
|
||||
@ -97,7 +97,7 @@ a location after:
|
||||
- The creation of any vital threads or child processes - since the forkserver
|
||||
can't clone them easily.
|
||||
|
||||
- The initialization of timers via setitimer() or equivalent calls.
|
||||
- The initialization of timers via `setitimer()` or equivalent calls.
|
||||
|
||||
- The creation of temporary files, network sockets, offset-sensitive file
|
||||
descriptors, and similar shared-state resources - but only provided that
|
||||
@ -150,9 +150,9 @@ the impact of memory leaks and similar glitches; 1000 is a good starting point,
|
||||
and going much higher increases the likelihood of hiccups without giving you
|
||||
any real performance benefits.
|
||||
|
||||
A more detailed template is shown in ../utils/persistent_mode/.
|
||||
Similarly to the previous mode, the feature works only with afl-clang-fast; #ifdef
|
||||
guards can be used to suppress it when using other compilers.
|
||||
A more detailed template is shown in `../utils/persistent_mode/.`
|
||||
Similarly to the previous mode, the feature works only with afl-clang-fast;
|
||||
`#ifdef` guards can be used to suppress it when using other compilers.
|
||||
|
||||
Note that as with the previous mode, the feature is easy to misuse; if you
|
||||
do not fully reset the critical state, you may end up with false positives or
|
||||
@ -161,7 +161,7 @@ wary of memory leaks and of the state of file descriptors.
|
||||
|
||||
PS. Because there are task switches still involved, the mode isn't as fast as
|
||||
"pure" in-process fuzzing offered, say, by LLVM's LibFuzzer; but it is a lot
|
||||
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.
|
||||
|
||||
## 5) Shared memory fuzzing
|
||||
@ -174,17 +174,17 @@ Setting this up is very easy:
|
||||
|
||||
After the includes set the following macro:
|
||||
|
||||
```
|
||||
```c
|
||||
__AFL_FUZZ_INIT();
|
||||
```
|
||||
Directly at the start of main - or if you are using the deferred forkserver
|
||||
with `__AFL_INIT()` then *after* `__AFL_INIT? :
|
||||
```
|
||||
with `__AFL_INIT()` then *after* `__AFL_INIT()` :
|
||||
```c
|
||||
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
|
||||
```
|
||||
|
||||
Then as first line after the `__AFL_LOOP` while loop:
|
||||
```
|
||||
```c
|
||||
int len = __AFL_FUZZ_TESTCASE_LEN;
|
||||
```
|
||||
and that is all!
|
||||
|
@ -733,7 +733,7 @@ bool ModuleSanitizerCoverage::instrumentModule(
|
||||
Var->getInitializer())) {
|
||||
|
||||
HasStr2 = true;
|
||||
Str2 = Array->getAsString().str();
|
||||
Str2 = Array->getRawDataValues().str();
|
||||
|
||||
}
|
||||
|
||||
@ -809,7 +809,7 @@ bool ModuleSanitizerCoverage::instrumentModule(
|
||||
Var->getInitializer())) {
|
||||
|
||||
HasStr1 = true;
|
||||
Str1 = Array->getAsString().str();
|
||||
Str1 = Array->getRawDataValues().str();
|
||||
|
||||
}
|
||||
|
||||
@ -849,15 +849,18 @@ bool ModuleSanitizerCoverage::instrumentModule(
|
||||
thestring = Str2;
|
||||
|
||||
optLen = thestring.length();
|
||||
if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; }
|
||||
|
||||
if (isMemcmp || isStrncmp || isStrncasecmp) {
|
||||
|
||||
Value * op2 = callInst->getArgOperand(2);
|
||||
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
|
||||
|
||||
if (ilen) {
|
||||
|
||||
uint64_t literalLength = optLen;
|
||||
optLen = ilen->getZExtValue();
|
||||
if (optLen < 2) { continue; }
|
||||
if (literalLength + 1 == optLen) { // add null byte
|
||||
thestring.append("\0", 1);
|
||||
addedNull = true;
|
||||
@ -872,17 +875,21 @@ bool ModuleSanitizerCoverage::instrumentModule(
|
||||
// was not already added
|
||||
if (!isMemcmp) {
|
||||
|
||||
if (addedNull == false) {
|
||||
if (addedNull == false && thestring[optLen - 1] != '\0') {
|
||||
|
||||
thestring.append("\0", 1); // add null byte
|
||||
optLen++;
|
||||
|
||||
}
|
||||
|
||||
// ensure we do not have garbage
|
||||
size_t offset = thestring.find('\0', 0);
|
||||
if (offset + 1 < optLen) optLen = offset + 1;
|
||||
thestring = thestring.substr(0, optLen);
|
||||
if (!isStdString) {
|
||||
|
||||
// ensure we do not have garbage
|
||||
size_t offset = thestring.find('\0', 0);
|
||||
if (offset + 1 < optLen) optLen = offset + 1;
|
||||
thestring = thestring.substr(0, optLen);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1088,7 +1088,7 @@ void ModuleSanitizerCoverage::InjectTraceForSwitch(
|
||||
|
||||
}
|
||||
|
||||
llvm::sort(Initializers.begin() + 2, Initializers.end(),
|
||||
llvm::sort(drop_begin(Initializers, 2),
|
||||
[](const Constant *A, const Constant *B) {
|
||||
|
||||
return cast<ConstantInt>(A)->getLimitedValue() <
|
||||
@ -1136,10 +1136,10 @@ void ModuleSanitizerCoverage::InjectTraceForGep(
|
||||
for (auto GEP : GepTraceTargets) {
|
||||
|
||||
IRBuilder<> IRB(GEP);
|
||||
for (auto I = GEP->idx_begin(); I != GEP->idx_end(); ++I)
|
||||
if (!isa<ConstantInt>(*I) && (*I)->getType()->isIntegerTy())
|
||||
for (Use &Idx : GEP->indices())
|
||||
if (!isa<ConstantInt>(Idx) && Idx->getType()->isIntegerTy())
|
||||
IRB.CreateCall(SanCovTraceGepFunction,
|
||||
{IRB.CreateIntCast(*I, IntptrTy, true)});
|
||||
{IRB.CreateIntCast(Idx, IntptrTy, true)});
|
||||
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "cmplog.h"
|
||||
#include "llvm-ngram-coverage.h"
|
||||
#include "llvm-alternative-coverage.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -34,6 +34,7 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#ifndef __HAIKU__
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
@ -96,10 +97,12 @@ int __afl_selective_coverage_temp = 1;
|
||||
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX];
|
||||
PREV_LOC_T __afl_prev_caller[CTX_MAX_K];
|
||||
u32 __afl_prev_ctx;
|
||||
u32 __afl_cmp_counter;
|
||||
#else
|
||||
__thread PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX];
|
||||
__thread PREV_LOC_T __afl_prev_caller[CTX_MAX_K];
|
||||
__thread u32 __afl_prev_ctx;
|
||||
__thread u32 __afl_cmp_counter;
|
||||
#endif
|
||||
@ -122,6 +125,21 @@ static u8 is_persistent;
|
||||
|
||||
static u8 _is_sancov;
|
||||
|
||||
/* Debug? */
|
||||
|
||||
static u32 __afl_debug;
|
||||
|
||||
/* Already initialized markers */
|
||||
|
||||
u32 __afl_already_initialized_shm;
|
||||
u32 __afl_already_initialized_forkserver;
|
||||
u32 __afl_already_initialized_first;
|
||||
u32 __afl_already_initialized_second;
|
||||
|
||||
/* Dummy pipe for area_is_valid() */
|
||||
|
||||
static int __afl_dummy_fd[2] = {2, 2};
|
||||
|
||||
/* ensure we kill the child on termination */
|
||||
|
||||
void at_exit(int signal) {
|
||||
@ -171,7 +189,7 @@ static void __afl_map_shm_fuzz() {
|
||||
|
||||
char *id_str = getenv(SHM_FUZZ_ENV_VAR);
|
||||
|
||||
if (getenv("AFL_DEBUG")) {
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr, "DEBUG: fuzzcase shmem %s\n", id_str ? id_str : "none");
|
||||
|
||||
@ -217,7 +235,7 @@ static void __afl_map_shm_fuzz() {
|
||||
__afl_fuzz_len = (u32 *)map;
|
||||
__afl_fuzz_ptr = map + sizeof(u32);
|
||||
|
||||
if (getenv("AFL_DEBUG")) {
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr, "DEBUG: successfully got fuzzing shared memory\n");
|
||||
|
||||
@ -237,6 +255,9 @@ static void __afl_map_shm_fuzz() {
|
||||
|
||||
static void __afl_map_shm(void) {
|
||||
|
||||
if (__afl_already_initialized_shm) return;
|
||||
__afl_already_initialized_shm = 1;
|
||||
|
||||
// if we are not running in afl ensure the map exists
|
||||
if (!__afl_area_ptr) { __afl_area_ptr = __afl_area_ptr_dummy; }
|
||||
|
||||
@ -244,8 +265,12 @@ static void __afl_map_shm(void) {
|
||||
|
||||
if (__afl_final_loc) {
|
||||
|
||||
if (__afl_final_loc % 32)
|
||||
__afl_final_loc = (((__afl_final_loc + 31) >> 5) << 5);
|
||||
if (__afl_final_loc % 64) {
|
||||
|
||||
__afl_final_loc = (((__afl_final_loc + 63) >> 6) << 6);
|
||||
|
||||
}
|
||||
|
||||
__afl_map_size = __afl_final_loc;
|
||||
|
||||
if (__afl_final_loc > MAP_SIZE) {
|
||||
@ -290,18 +315,23 @@ static void __afl_map_shm(void) {
|
||||
early-stage __afl_area_initial region that is needed to allow some really
|
||||
hacky .init code to work correctly in projects such as OpenSSL. */
|
||||
|
||||
if (getenv("AFL_DEBUG"))
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr,
|
||||
"DEBUG: id_str %s, __afl_area_ptr %p, __afl_area_initial %p, "
|
||||
"__afl_map_addr 0x%llx, MAP_SIZE %u, __afl_final_loc %u, "
|
||||
"DEBUG: (1) id_str %s, __afl_area_ptr %p, __afl_area_initial %p, "
|
||||
"__afl_area_ptr_dummy 0x%p, __afl_map_addr 0x%llx, MAP_SIZE %u, "
|
||||
"__afl_final_loc %u, "
|
||||
"max_size_forkserver %u/0x%x\n",
|
||||
id_str == NULL ? "<null>" : id_str, __afl_area_ptr,
|
||||
__afl_area_initial, __afl_map_addr, MAP_SIZE, __afl_final_loc,
|
||||
FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE);
|
||||
__afl_area_initial, __afl_area_ptr_dummy, __afl_map_addr, MAP_SIZE,
|
||||
__afl_final_loc, FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE);
|
||||
|
||||
}
|
||||
|
||||
if (id_str) {
|
||||
|
||||
if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial) {
|
||||
if (__afl_area_ptr && __afl_area_ptr != __afl_area_initial &&
|
||||
__afl_area_ptr != __afl_area_ptr_dummy) {
|
||||
|
||||
if (__afl_map_addr) {
|
||||
|
||||
@ -346,17 +376,18 @@ static void __afl_map_shm(void) {
|
||||
|
||||
}
|
||||
|
||||
close(shm_fd);
|
||||
shm_fd = -1;
|
||||
|
||||
if (shm_base == MAP_FAILED) {
|
||||
|
||||
close(shm_fd);
|
||||
shm_fd = -1;
|
||||
|
||||
fprintf(stderr, "mmap() failed\n");
|
||||
perror("mmap for map");
|
||||
|
||||
if (__afl_map_addr)
|
||||
send_forkserver_error(FS_ERROR_MAP_ADDR);
|
||||
else
|
||||
send_forkserver_error(FS_ERROR_MMAP);
|
||||
perror("mmap for map");
|
||||
|
||||
exit(2);
|
||||
|
||||
@ -435,6 +466,19 @@ static void __afl_map_shm(void) {
|
||||
|
||||
__afl_area_ptr_backup = __afl_area_ptr;
|
||||
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr,
|
||||
"DEBUG: (2) id_str %s, __afl_area_ptr %p, __afl_area_initial %p, "
|
||||
"__afl_area_ptr_dummy 0x%p, __afl_map_addr 0x%llx, MAP_SIZE "
|
||||
"%u, __afl_final_loc %u, "
|
||||
"max_size_forkserver %u/0x%x\n",
|
||||
id_str == NULL ? "<null>" : id_str, __afl_area_ptr,
|
||||
__afl_area_initial, __afl_area_ptr_dummy, __afl_map_addr, MAP_SIZE,
|
||||
__afl_final_loc, FS_OPT_MAX_MAPSIZE, FS_OPT_MAX_MAPSIZE);
|
||||
|
||||
}
|
||||
|
||||
if (__afl_selective_coverage) {
|
||||
|
||||
if (__afl_map_size > MAP_INITIAL_SIZE) {
|
||||
@ -463,7 +507,7 @@ static void __afl_map_shm(void) {
|
||||
|
||||
id_str = getenv(CMPLOG_SHM_ENV_VAR);
|
||||
|
||||
if (getenv("AFL_DEBUG")) {
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr, "DEBUG: cmplog id_str %s\n",
|
||||
id_str == NULL ? "<null>" : id_str);
|
||||
@ -472,6 +516,12 @@ static void __afl_map_shm(void) {
|
||||
|
||||
if (id_str) {
|
||||
|
||||
if ((__afl_dummy_fd[1] = open("/dev/null", O_WRONLY)) < 0) {
|
||||
|
||||
if (pipe(__afl_dummy_fd) < 0) { __afl_dummy_fd[1] = 1; }
|
||||
|
||||
}
|
||||
|
||||
#ifdef USEMMAP
|
||||
const char * shm_file_path = id_str;
|
||||
int shm_fd = -1;
|
||||
@ -522,6 +572,58 @@ static void __afl_map_shm(void) {
|
||||
|
||||
}
|
||||
|
||||
/* unmap SHM. */
|
||||
|
||||
static void __afl_unmap_shm(void) {
|
||||
|
||||
if (!__afl_already_initialized_shm) return;
|
||||
|
||||
char *id_str = getenv(SHM_ENV_VAR);
|
||||
|
||||
if (id_str) {
|
||||
|
||||
#ifdef USEMMAP
|
||||
|
||||
munmap((void *)__afl_area_ptr, __afl_map_size);
|
||||
|
||||
#else
|
||||
|
||||
shmdt((void *)__afl_area_ptr);
|
||||
|
||||
#endif
|
||||
|
||||
} else if ((!__afl_area_ptr || __afl_area_ptr == __afl_area_initial) &&
|
||||
|
||||
__afl_map_addr) {
|
||||
|
||||
munmap((void *)__afl_map_addr, __afl_map_size);
|
||||
|
||||
}
|
||||
|
||||
__afl_area_ptr = __afl_area_ptr_dummy;
|
||||
|
||||
id_str = getenv(CMPLOG_SHM_ENV_VAR);
|
||||
|
||||
if (id_str) {
|
||||
|
||||
#ifdef USEMMAP
|
||||
|
||||
munmap((void *)__afl_cmp_map, __afl_map_size);
|
||||
|
||||
#else
|
||||
|
||||
shmdt((void *)__afl_cmp_map);
|
||||
|
||||
#endif
|
||||
|
||||
__afl_cmp_map = NULL;
|
||||
|
||||
}
|
||||
|
||||
__afl_already_initialized_shm = 0;
|
||||
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static void __afl_start_snapshots(void) {
|
||||
|
||||
@ -550,7 +652,7 @@ static void __afl_start_snapshots(void) {
|
||||
|
||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) { _exit(1); }
|
||||
|
||||
if (getenv("AFL_DEBUG")) {
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr, "target forkserver recv: %08x\n", was_killed);
|
||||
|
||||
@ -727,6 +829,9 @@ static void __afl_start_snapshots(void) {
|
||||
|
||||
static void __afl_start_forkserver(void) {
|
||||
|
||||
if (__afl_already_initialized_forkserver) return;
|
||||
__afl_already_initialized_forkserver = 1;
|
||||
|
||||
struct sigaction orig_action;
|
||||
sigaction(SIGTERM, NULL, &orig_action);
|
||||
old_sigterm_handler = orig_action.sa_handler;
|
||||
@ -777,7 +882,7 @@ static void __afl_start_forkserver(void) {
|
||||
|
||||
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
|
||||
|
||||
if (getenv("AFL_DEBUG")) {
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr, "target forkserver recv: %08x\n", was_killed);
|
||||
|
||||
@ -1012,11 +1117,14 @@ void __afl_manual_init(void) {
|
||||
__afl_sharedmem_fuzzing = 0;
|
||||
if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_ptr_dummy;
|
||||
|
||||
if (getenv("AFL_DEBUG"))
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr,
|
||||
"DEBUG: disabled instrumentation because of "
|
||||
"AFL_DISABLE_LLVM_INSTRUMENTATION\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!init_done) {
|
||||
@ -1056,6 +1164,11 @@ __attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) {
|
||||
|
||||
__attribute__((constructor(1))) void __afl_auto_second(void) {
|
||||
|
||||
if (__afl_already_initialized_second) return;
|
||||
__afl_already_initialized_second = 1;
|
||||
|
||||
if (getenv("AFL_DEBUG")) { __afl_debug = 1; }
|
||||
|
||||
if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return;
|
||||
u8 *ptr;
|
||||
|
||||
@ -1080,17 +1193,18 @@ __attribute__((constructor(1))) void __afl_auto_second(void) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} // ptr memleak report is a false positive
|
||||
|
||||
/* preset __afl_area_ptr #1 - at constructor level 0 global variables have
|
||||
not been set */
|
||||
|
||||
__attribute__((constructor(0))) void __afl_auto_first(void) {
|
||||
|
||||
if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return;
|
||||
u8 *ptr;
|
||||
if (__afl_already_initialized_first) return;
|
||||
__afl_already_initialized_first = 1;
|
||||
|
||||
ptr = (u8 *)malloc(2097152);
|
||||
if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return;
|
||||
u8 *ptr = (u8 *)malloc(MAP_INITIAL_SIZE);
|
||||
|
||||
if (ptr && (ssize_t)ptr != -1) {
|
||||
|
||||
@ -1099,7 +1213,7 @@ __attribute__((constructor(0))) void __afl_auto_first(void) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} // ptr memleak report is a false positive
|
||||
|
||||
/* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard.
|
||||
It remains non-operational in the traditional, plugin-backed LLVM mode.
|
||||
@ -1167,11 +1281,13 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
|
||||
|
||||
_is_sancov = 1;
|
||||
|
||||
if (getenv("AFL_DEBUG")) {
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr,
|
||||
"Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges)\n",
|
||||
start, stop, stop - start);
|
||||
"Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges) "
|
||||
"after_fs=%u\n",
|
||||
start, stop, (unsigned long)(stop - start),
|
||||
__afl_already_initialized_forkserver);
|
||||
|
||||
}
|
||||
|
||||
@ -1187,6 +1303,40 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
|
||||
|
||||
}
|
||||
|
||||
/* instrumented code is loaded *after* our forkserver is up. this is a
|
||||
problem. We cannot prevent collisions then :( */
|
||||
if (__afl_already_initialized_forkserver &&
|
||||
__afl_final_loc + 1 + stop - start > __afl_map_size) {
|
||||
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr, "Warning: new instrumented code after the forkserver!\n");
|
||||
|
||||
}
|
||||
|
||||
__afl_final_loc = 2;
|
||||
|
||||
if (1 + stop - start > __afl_map_size) {
|
||||
|
||||
*(start++) = ++__afl_final_loc;
|
||||
|
||||
while (start < stop) {
|
||||
|
||||
if (R(100) < inst_ratio)
|
||||
*start = ++__afl_final_loc % __afl_map_size;
|
||||
else
|
||||
*start = 0;
|
||||
|
||||
start++;
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Make sure that the first element in the range is always set - we use that
|
||||
to avoid duplicate calls (which can happen as an artifact of the underlying
|
||||
implementation in LLVM). */
|
||||
@ -1204,6 +1354,28 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
|
||||
|
||||
}
|
||||
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr,
|
||||
"Done __sanitizer_cov_trace_pc_guard_init: __afl_final_loc = %u\n",
|
||||
__afl_final_loc);
|
||||
|
||||
}
|
||||
|
||||
if (__afl_already_initialized_shm && __afl_final_loc > __afl_map_size) {
|
||||
|
||||
if (__afl_debug) {
|
||||
|
||||
fprintf(stderr, "Reinit shm necessary (+%u)\n",
|
||||
__afl_final_loc - __afl_map_size);
|
||||
|
||||
}
|
||||
|
||||
__afl_unmap_shm();
|
||||
__afl_map_shm();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///// CmpLog instrumentation
|
||||
@ -1547,17 +1719,42 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
|
||||
|
||||
}
|
||||
|
||||
__attribute__((weak)) void *__asan_region_is_poisoned(void *beg, size_t size) {
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
// POSIX shenanigan to see if an area is mapped.
|
||||
// If it is mapped as X-only, we have a problem, so maybe we should add a check
|
||||
// to avoid to call it on .text addresses
|
||||
static int area_is_mapped(void *ptr, size_t len) {
|
||||
static int area_is_valid(void *ptr, size_t len) {
|
||||
|
||||
char *p = (char *)ptr;
|
||||
char *page = (char *)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) - 1));
|
||||
if (unlikely(__asan_region_is_poisoned(ptr, len))) { return 0; }
|
||||
|
||||
int r = msync(page, (p - page) + len, MS_ASYNC);
|
||||
if (r < 0) return errno != ENOMEM;
|
||||
return 1;
|
||||
long r = syscall(__afl_dummy_fd[1], SYS_write, ptr, len);
|
||||
|
||||
if (unlikely(r <= 0 || r > len)) { // fail - maybe hitting asan boundary?
|
||||
|
||||
char *p = (char *)ptr;
|
||||
long page_size = sysconf(_SC_PAGE_SIZE);
|
||||
char *page = (char *)((uintptr_t)p & ~(page_size - 1)) + page_size;
|
||||
if (page < p + len) { return 0; } // no isnt, return fail
|
||||
len -= (p + len - page);
|
||||
r = syscall(__afl_dummy_fd[1], SYS_write, p, len);
|
||||
|
||||
}
|
||||
|
||||
// partial writes - we return what was written.
|
||||
if (likely(r >= 0 && r <= len)) {
|
||||
|
||||
return (int)r;
|
||||
|
||||
} else {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1565,19 +1762,22 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
|
||||
|
||||
/*
|
||||
u32 i;
|
||||
if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return;
|
||||
if (area_is_valid(ptr1, 32) <= 0 || area_is_valid(ptr2, 32) <= 0) return;
|
||||
fprintf(stderr, "rtn arg0=");
|
||||
for (i = 0; i < 24; i++)
|
||||
for (i = 0; i < 32; i++)
|
||||
fprintf(stderr, "%02x", ptr1[i]);
|
||||
fprintf(stderr, " arg1=");
|
||||
for (i = 0; i < 24; i++)
|
||||
for (i = 0; i < 32; i++)
|
||||
fprintf(stderr, "%02x", ptr2[i]);
|
||||
fprintf(stderr, "\n");
|
||||
*/
|
||||
|
||||
if (unlikely(!__afl_cmp_map)) return;
|
||||
|
||||
if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return;
|
||||
int l1, l2;
|
||||
if ((l1 = area_is_valid(ptr1, 32)) <= 0 ||
|
||||
(l2 = area_is_valid(ptr2, 32)) <= 0)
|
||||
return;
|
||||
int len = MIN(l1, l2);
|
||||
|
||||
uintptr_t k = (uintptr_t)__builtin_return_address(0);
|
||||
k = (k >> 4) ^ (k << 8);
|
||||
@ -1588,17 +1788,17 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
|
||||
if (__afl_cmp_map->headers[k].type != CMP_TYPE_RTN) {
|
||||
|
||||
__afl_cmp_map->headers[k].type = CMP_TYPE_RTN;
|
||||
hits = 0;
|
||||
__afl_cmp_map->headers[k].hits = 1;
|
||||
__afl_cmp_map->headers[k].shape = 31;
|
||||
__afl_cmp_map->headers[k].shape = len - 1;
|
||||
hits = 0;
|
||||
|
||||
} else {
|
||||
|
||||
hits = __afl_cmp_map->headers[k].hits++;
|
||||
|
||||
if (__afl_cmp_map->headers[k].shape < 31) {
|
||||
if (__afl_cmp_map->headers[k].shape < len) {
|
||||
|
||||
__afl_cmp_map->headers[k].shape = 31;
|
||||
__afl_cmp_map->headers[k].shape = len - 1;
|
||||
|
||||
}
|
||||
|
||||
@ -1606,9 +1806,9 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) {
|
||||
|
||||
hits &= CMP_MAP_RTN_H - 1;
|
||||
__builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0,
|
||||
ptr1, 32);
|
||||
ptr1, len);
|
||||
__builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1,
|
||||
ptr2, 32);
|
||||
ptr2, len);
|
||||
|
||||
}
|
||||
|
||||
@ -1653,12 +1853,20 @@ static u8 *get_llvm_stdstring(u8 *string) {
|
||||
|
||||
void __cmplog_rtn_gcc_stdstring_cstring(u8 *stdstring, u8 *cstring) {
|
||||
|
||||
if (unlikely(!__afl_cmp_map)) return;
|
||||
if (area_is_valid(stdstring, 32) <= 0 || area_is_valid(cstring, 32) <= 0)
|
||||
return;
|
||||
|
||||
__cmplog_rtn_hook(get_gcc_stdstring(stdstring), cstring);
|
||||
|
||||
}
|
||||
|
||||
void __cmplog_rtn_gcc_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) {
|
||||
|
||||
if (unlikely(!__afl_cmp_map)) return;
|
||||
if (area_is_valid(stdstring1, 32) <= 0 || area_is_valid(stdstring2, 32) <= 0)
|
||||
return;
|
||||
|
||||
__cmplog_rtn_hook(get_gcc_stdstring(stdstring1),
|
||||
get_gcc_stdstring(stdstring2));
|
||||
|
||||
@ -1666,12 +1874,20 @@ void __cmplog_rtn_gcc_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) {
|
||||
|
||||
void __cmplog_rtn_llvm_stdstring_cstring(u8 *stdstring, u8 *cstring) {
|
||||
|
||||
if (unlikely(!__afl_cmp_map)) return;
|
||||
if (area_is_valid(stdstring, 32) <= 0 || area_is_valid(cstring, 32) <= 0)
|
||||
return;
|
||||
|
||||
__cmplog_rtn_hook(get_llvm_stdstring(stdstring), cstring);
|
||||
|
||||
}
|
||||
|
||||
void __cmplog_rtn_llvm_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) {
|
||||
|
||||
if (unlikely(!__afl_cmp_map)) return;
|
||||
if (area_is_valid(stdstring1, 32) <= 0 || area_is_valid(stdstring2, 32) <= 0)
|
||||
return;
|
||||
|
||||
__cmplog_rtn_hook(get_llvm_stdstring(stdstring1),
|
||||
get_llvm_stdstring(stdstring2));
|
||||
|
||||
|
@ -62,13 +62,15 @@ bool isIgnoreFunction(const llvm::Function *F) {
|
||||
"sancov.",
|
||||
"__ubsan_",
|
||||
"ign.",
|
||||
"__afl_",
|
||||
"__afl",
|
||||
"_fini",
|
||||
"__libc_csu",
|
||||
"__libc_",
|
||||
"__asan",
|
||||
"__msan",
|
||||
"__cmplog",
|
||||
"__sancov",
|
||||
"__cxx_",
|
||||
"_GLOBAL",
|
||||
"msan.",
|
||||
"LLVMFuzzerM",
|
||||
"LLVMFuzzerC",
|
||||
|
@ -90,7 +90,7 @@ void dict2file(int fd, u8 *mem, u32 len) {
|
||||
j = 1;
|
||||
for (i = 0; i < len; i++) {
|
||||
|
||||
if (isprint(mem[i])) {
|
||||
if (isprint(mem[i]) && mem[i] != '\\' && mem[i] != '"') {
|
||||
|
||||
line[j++] = mem[i];
|
||||
|
||||
@ -357,6 +357,7 @@ bool AFLdict2filePass::runOnModule(Module &M) {
|
||||
StringRef TmpStr;
|
||||
bool HasStr1;
|
||||
getConstantStringInfo(Str1P, TmpStr);
|
||||
|
||||
if (TmpStr.empty()) {
|
||||
|
||||
HasStr1 = false;
|
||||
@ -403,7 +404,7 @@ bool AFLdict2filePass::runOnModule(Module &M) {
|
||||
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
|
||||
|
||||
HasStr2 = true;
|
||||
Str2 = Array->getAsString().str();
|
||||
Str2 = Array->getRawDataValues().str();
|
||||
|
||||
}
|
||||
|
||||
@ -479,7 +480,7 @@ bool AFLdict2filePass::runOnModule(Module &M) {
|
||||
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
|
||||
|
||||
HasStr1 = true;
|
||||
Str1 = Array->getAsString().str();
|
||||
Str1 = Array->getRawDataValues().str();
|
||||
|
||||
}
|
||||
|
||||
@ -520,14 +521,18 @@ bool AFLdict2filePass::runOnModule(Module &M) {
|
||||
|
||||
optLen = thestring.length();
|
||||
|
||||
if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; }
|
||||
|
||||
if (isMemcmp || isStrncmp || isStrncasecmp) {
|
||||
|
||||
Value * op2 = callInst->getArgOperand(2);
|
||||
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
|
||||
|
||||
if (ilen) {
|
||||
|
||||
uint64_t literalLength = optLen;
|
||||
optLen = ilen->getZExtValue();
|
||||
if (optLen < 2) { continue; }
|
||||
if (literalLength + 1 == optLen) { // add null byte
|
||||
thestring.append("\0", 1);
|
||||
addedNull = true;
|
||||
@ -542,17 +547,21 @@ bool AFLdict2filePass::runOnModule(Module &M) {
|
||||
// was not already added
|
||||
if (!isMemcmp) {
|
||||
|
||||
if (addedNull == false) {
|
||||
if (addedNull == false && thestring[optLen - 1] != '\0') {
|
||||
|
||||
thestring.append("\0", 1); // add null byte
|
||||
optLen++;
|
||||
|
||||
}
|
||||
|
||||
// ensure we do not have garbage
|
||||
size_t offset = thestring.find('\0', 0);
|
||||
if (offset + 1 < optLen) optLen = offset + 1;
|
||||
thestring = thestring.substr(0, optLen);
|
||||
if (!isStdString) {
|
||||
|
||||
// ensure we do not have garbage
|
||||
size_t offset = thestring.find('\0', 0);
|
||||
if (offset + 1 < optLen) optLen = offset + 1;
|
||||
thestring = thestring.substr(0, optLen);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -519,7 +519,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
Var->getInitializer())) {
|
||||
|
||||
HasStr2 = true;
|
||||
Str2 = Array->getAsString().str();
|
||||
Str2 = Array->getRawDataValues().str();
|
||||
|
||||
}
|
||||
|
||||
@ -595,7 +595,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
Var->getInitializer())) {
|
||||
|
||||
HasStr1 = true;
|
||||
Str1 = Array->getAsString().str();
|
||||
Str1 = Array->getRawDataValues().str();
|
||||
|
||||
}
|
||||
|
||||
@ -635,15 +635,18 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
thestring = Str2;
|
||||
|
||||
optLen = thestring.length();
|
||||
if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; }
|
||||
|
||||
if (isMemcmp || isStrncmp || isStrncasecmp) {
|
||||
|
||||
Value * op2 = callInst->getArgOperand(2);
|
||||
ConstantInt *ilen = dyn_cast<ConstantInt>(op2);
|
||||
|
||||
if (ilen) {
|
||||
|
||||
uint64_t literalLength = optLen;
|
||||
optLen = ilen->getZExtValue();
|
||||
if (optLen < 2) { continue; }
|
||||
if (literalLength + 1 == optLen) { // add null byte
|
||||
thestring.append("\0", 1);
|
||||
addedNull = true;
|
||||
@ -658,17 +661,21 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
// was not already added
|
||||
if (!isMemcmp) {
|
||||
|
||||
if (addedNull == false) {
|
||||
if (addedNull == false && thestring[optLen - 1] != '\0') {
|
||||
|
||||
thestring.append("\0", 1); // add null byte
|
||||
optLen++;
|
||||
|
||||
}
|
||||
|
||||
// ensure we do not have garbage
|
||||
size_t offset = thestring.find('\0', 0);
|
||||
if (offset + 1 < optLen) optLen = offset + 1;
|
||||
thestring = thestring.substr(0, optLen);
|
||||
if (!isStdString) {
|
||||
|
||||
// ensure we do not have garbage
|
||||
size_t offset = thestring.find('\0', 0);
|
||||
if (offset + 1 < optLen) optLen = offset + 1;
|
||||
thestring = thestring.substr(0, optLen);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -924,9 +931,7 @@ bool AFLLTOPass::runOnModule(Module &M) {
|
||||
|
||||
if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) {
|
||||
|
||||
uint32_t write_loc = afl_global_id;
|
||||
|
||||
if (afl_global_id % 32) write_loc = (((afl_global_id + 32) >> 4) << 4);
|
||||
uint32_t write_loc = (((afl_global_id + 63) >> 6) << 6);
|
||||
|
||||
GlobalVariable *AFLFinalLoc = new GlobalVariable(
|
||||
M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_final_loc");
|
||||
|
@ -62,7 +62,7 @@ typedef long double max_align_t;
|
||||
#endif
|
||||
|
||||
#include "afl-llvm-common.h"
|
||||
#include "llvm-ngram-coverage.h"
|
||||
#include "llvm-alternative-coverage.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@ -82,9 +82,10 @@ class AFLCoverage : public ModulePass {
|
||||
|
||||
protected:
|
||||
uint32_t ngram_size = 0;
|
||||
uint32_t ctx_k = 0;
|
||||
uint32_t map_size = MAP_SIZE;
|
||||
uint32_t function_minimum_size = 1;
|
||||
char * ctx_str = NULL, *skip_nozero = NULL;
|
||||
char * ctx_str = NULL, *caller_str = NULL, *skip_nozero = NULL;
|
||||
|
||||
};
|
||||
|
||||
@ -183,10 +184,16 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
|
||||
|
||||
unsigned PrevLocSize = 0;
|
||||
unsigned PrevCallerSize = 0;
|
||||
|
||||
char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE");
|
||||
if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE");
|
||||
char *ctx_k_str = getenv("AFL_LLVM_CTX_K");
|
||||
if (!ctx_k_str) ctx_k_str = getenv("AFL_CTX_K");
|
||||
ctx_str = getenv("AFL_LLVM_CTX");
|
||||
caller_str = getenv("AFL_LLVM_CALLER");
|
||||
|
||||
bool instrument_ctx = ctx_str || caller_str;
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
/* Decide previous location vector size (must be a power of two) */
|
||||
@ -204,6 +211,31 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
if (ngram_size)
|
||||
PrevLocSize = ngram_size - 1;
|
||||
else
|
||||
PrevLocSize = 1;
|
||||
|
||||
/* Decide K-ctx vector size (must be a power of two) */
|
||||
VectorType *PrevCallerTy = NULL;
|
||||
|
||||
if (ctx_k_str)
|
||||
if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 1 || ctx_k > CTX_MAX_K)
|
||||
FATAL("Bad value of AFL_CTX_K (must be between 1 and CTX_MAX_K (%u))",
|
||||
CTX_MAX_K);
|
||||
|
||||
if (ctx_k == 1) {
|
||||
|
||||
ctx_k = 0;
|
||||
instrument_ctx = true;
|
||||
caller_str = ctx_k_str; // Enable CALLER instead
|
||||
|
||||
}
|
||||
|
||||
if (ctx_k) {
|
||||
|
||||
PrevCallerSize = ctx_k;
|
||||
instrument_ctx = true;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
if (ngram_size_str)
|
||||
#ifndef LLVM_VERSION_PATCH
|
||||
@ -217,8 +249,20 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
"%d.%d.%d!",
|
||||
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH);
|
||||
#endif
|
||||
if (ctx_k_str)
|
||||
#ifndef LLVM_VERSION_PATCH
|
||||
FATAL(
|
||||
"Sorry, K-CTX branch coverage is not supported with llvm version "
|
||||
"%d.%d.%d!",
|
||||
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0);
|
||||
#else
|
||||
FATAL(
|
||||
"Sorry, K-CTX branch coverage is not supported with llvm version "
|
||||
"%d.%d.%d!",
|
||||
LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH);
|
||||
#endif
|
||||
PrevLocSize = 1;
|
||||
#endif
|
||||
PrevLocSize = 1;
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
int PrevLocVecSize = PowerOf2Ceil(PrevLocSize);
|
||||
@ -231,6 +275,17 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
);
|
||||
#endif
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
int PrevCallerVecSize = PowerOf2Ceil(PrevCallerSize);
|
||||
if (ctx_k)
|
||||
PrevCallerTy = VectorType::get(IntLocTy, PrevCallerVecSize
|
||||
#if LLVM_VERSION_MAJOR >= 12
|
||||
,
|
||||
false
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
|
||||
/* Get globals for the SHM region and the previous location. Note that
|
||||
__afl_prev_loc is thread-local. */
|
||||
|
||||
@ -238,9 +293,10 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
new GlobalVariable(M, PointerType::get(Int8Ty, 0), false,
|
||||
GlobalValue::ExternalLinkage, 0, "__afl_area_ptr");
|
||||
GlobalVariable *AFLPrevLoc;
|
||||
GlobalVariable *AFLPrevCaller;
|
||||
GlobalVariable *AFLContext = NULL;
|
||||
|
||||
if (ctx_str)
|
||||
if (ctx_str || caller_str)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
AFLContext = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx");
|
||||
@ -274,6 +330,31 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
#endif
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ctx_k)
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
AFLPrevCaller = new GlobalVariable(
|
||||
M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
|
||||
/* Initializer */ nullptr, "__afl_prev_caller");
|
||||
#else
|
||||
AFLPrevCaller = new GlobalVariable(
|
||||
M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage,
|
||||
/* Initializer */ nullptr, "__afl_prev_caller",
|
||||
/* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel,
|
||||
/* AddressSpace */ 0, /* IsExternallyInitialized */ false);
|
||||
#endif
|
||||
else
|
||||
#endif
|
||||
#if defined(__ANDROID__) || defined(__HAIKU__)
|
||||
AFLPrevCaller =
|
||||
new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0,
|
||||
"__afl_prev_caller");
|
||||
#else
|
||||
AFLPrevCaller = new GlobalVariable(
|
||||
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller",
|
||||
0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||
#endif
|
||||
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
/* Create the vector shuffle mask for updating the previous block history.
|
||||
Note that the first element of the vector will store cur_loc, so just set
|
||||
@ -288,13 +369,30 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize));
|
||||
|
||||
Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle);
|
||||
|
||||
Constant * PrevCallerShuffleMask = NULL;
|
||||
SmallVector<Constant *, 32> PrevCallerShuffle = {UndefValue::get(Int32Ty)};
|
||||
|
||||
if (ctx_k) {
|
||||
|
||||
for (unsigned I = 0; I < PrevCallerSize - 1; ++I)
|
||||
PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, I));
|
||||
|
||||
for (int I = PrevCallerSize; I < PrevCallerVecSize; ++I)
|
||||
PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, PrevCallerSize));
|
||||
|
||||
PrevCallerShuffleMask = ConstantVector::get(PrevCallerShuffle);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// other constants we need
|
||||
ConstantInt *Zero = ConstantInt::get(Int8Ty, 0);
|
||||
ConstantInt *One = ConstantInt::get(Int8Ty, 1);
|
||||
|
||||
LoadInst *PrevCtx = NULL; // CTX sensitive coverage
|
||||
Value * PrevCtx = NULL; // CTX sensitive coverage
|
||||
LoadInst *PrevCaller = NULL; // K-CTX coverage
|
||||
|
||||
/* Instrument all the things! */
|
||||
|
||||
@ -318,12 +416,30 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
IRBuilder<> IRB(&(*IP));
|
||||
|
||||
// Context sensitive coverage
|
||||
if (ctx_str && &BB == &F.getEntryBlock()) {
|
||||
if (instrument_ctx && &BB == &F.getEntryBlock()) {
|
||||
|
||||
// load the context ID of the previous function and write to to a local
|
||||
// variable on the stack
|
||||
PrevCtx = IRB.CreateLoad(AFLContext);
|
||||
PrevCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ctx_k) {
|
||||
|
||||
PrevCaller = IRB.CreateLoad(AFLPrevCaller);
|
||||
PrevCaller->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
PrevCtx =
|
||||
IRB.CreateZExt(IRB.CreateXorReduce(PrevCaller), IRB.getInt32Ty());
|
||||
|
||||
} else
|
||||
|
||||
#endif
|
||||
{
|
||||
|
||||
// load the context ID of the previous function and write to to a
|
||||
// local variable on the stack
|
||||
LoadInst *PrevCtxLoad = IRB.CreateLoad(AFLContext);
|
||||
PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
PrevCtx = PrevCtxLoad;
|
||||
|
||||
}
|
||||
|
||||
// does the function have calls? and is any of the calls larger than one
|
||||
// basic block?
|
||||
@ -354,10 +470,32 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
// if yes we store a context ID for this function in the global var
|
||||
if (has_calls) {
|
||||
|
||||
ConstantInt *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size));
|
||||
StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext);
|
||||
StoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size));
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ctx_k) {
|
||||
|
||||
Value *ShuffledPrevCaller = IRB.CreateShuffleVector(
|
||||
PrevCaller, UndefValue::get(PrevCallerTy),
|
||||
PrevCallerShuffleMask);
|
||||
Value *UpdatedPrevCaller = IRB.CreateInsertElement(
|
||||
ShuffledPrevCaller, NewCtx, (uint64_t)0);
|
||||
|
||||
StoreInst *Store =
|
||||
IRB.CreateStore(UpdatedPrevCaller, AFLPrevCaller);
|
||||
Store->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
} else
|
||||
|
||||
#endif
|
||||
{
|
||||
|
||||
if (ctx_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx);
|
||||
StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext);
|
||||
StoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -411,13 +549,20 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
|
||||
// in CTX mode we have to restore the original context for the caller -
|
||||
// she might be calling other functions which need the correct CTX
|
||||
if (ctx_str && has_calls) {
|
||||
if (instrument_ctx && has_calls) {
|
||||
|
||||
Instruction *Inst = BB.getTerminator();
|
||||
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
|
||||
|
||||
IRBuilder<> Post_IRB(Inst);
|
||||
StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
|
||||
|
||||
StoreInst *RestoreCtx;
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ctx_k)
|
||||
RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller);
|
||||
else
|
||||
#endif
|
||||
RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
|
||||
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
@ -458,7 +603,7 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
#endif
|
||||
PrevLocTrans = PrevLoc;
|
||||
|
||||
if (ctx_str)
|
||||
if (instrument_ctx)
|
||||
PrevLocTrans =
|
||||
IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty);
|
||||
else
|
||||
@ -545,13 +690,20 @@ bool AFLCoverage::runOnModule(Module &M) {
|
||||
// in CTX mode we have to restore the original context for the caller -
|
||||
// she might be calling other functions which need the correct CTX.
|
||||
// Currently this is only needed for the Ubuntu clang-6.0 bug
|
||||
if (ctx_str && has_calls) {
|
||||
if (instrument_ctx && has_calls) {
|
||||
|
||||
Instruction *Inst = BB.getTerminator();
|
||||
if (isa<ReturnInst>(Inst) || isa<ResumeInst>(Inst)) {
|
||||
|
||||
IRBuilder<> Post_IRB(Inst);
|
||||
StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
|
||||
|
||||
StoreInst *RestoreCtx;
|
||||
#ifdef AFL_HAVE_VECTOR_INTRINSICS
|
||||
if (ctx_k)
|
||||
RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller);
|
||||
else
|
||||
#endif
|
||||
RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext);
|
||||
RestoreCtx->setMetadata(M.getMDKindID("nosanitize"),
|
||||
MDNode::get(C, None));
|
||||
|
||||
|
@ -19,12 +19,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sys/time.h>
|
||||
#include "llvm/Config/llvm-config.h"
|
||||
|
||||
#include "llvm/Config/llvm-config.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
@ -265,13 +266,20 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size;
|
||||
unsigned char do_cast = 0;
|
||||
|
||||
if (!SI->getNumCases() || max_size < 16 || max_size % 8) {
|
||||
if (!SI->getNumCases() || max_size < 16) {
|
||||
|
||||
// if (!be_quiet) errs() << "skip trivial switch..\n";
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (max_size % 8) {
|
||||
|
||||
max_size = (((max_size / 8) + 1) * 8);
|
||||
do_cast = 1;
|
||||
|
||||
}
|
||||
|
||||
IRBuilder<> IRB(SI->getParent());
|
||||
IRB.SetInsertPoint(SI);
|
||||
|
||||
@ -310,36 +318,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
|
||||
if (do_cast) {
|
||||
|
||||
ConstantInt *cint = dyn_cast<ConstantInt>(Val);
|
||||
if (cint) {
|
||||
|
||||
uint64_t val = cint->getZExtValue();
|
||||
// fprintf(stderr, "ConstantInt: %lu\n", val);
|
||||
switch (cast_size) {
|
||||
|
||||
case 8:
|
||||
CompareTo = ConstantInt::get(Int8Ty, val);
|
||||
break;
|
||||
case 16:
|
||||
CompareTo = ConstantInt::get(Int16Ty, val);
|
||||
break;
|
||||
case 32:
|
||||
CompareTo = ConstantInt::get(Int32Ty, val);
|
||||
break;
|
||||
case 64:
|
||||
CompareTo = ConstantInt::get(Int64Ty, val);
|
||||
break;
|
||||
case 128:
|
||||
CompareTo = ConstantInt::get(Int128Ty, val);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
CompareTo = IRB.CreateBitCast(Val, IntegerType::get(C, cast_size));
|
||||
|
||||
}
|
||||
CompareTo =
|
||||
IRB.CreateIntCast(CompareTo, IntegerType::get(C, cast_size), false);
|
||||
|
||||
}
|
||||
|
||||
@ -361,27 +341,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
|
||||
if (do_cast) {
|
||||
|
||||
uint64_t val = cint->getZExtValue();
|
||||
// fprintf(stderr, "ConstantInt: %lu\n", val);
|
||||
switch (cast_size) {
|
||||
|
||||
case 8:
|
||||
new_param = ConstantInt::get(Int8Ty, val);
|
||||
break;
|
||||
case 16:
|
||||
new_param = ConstantInt::get(Int16Ty, val);
|
||||
break;
|
||||
case 32:
|
||||
new_param = ConstantInt::get(Int32Ty, val);
|
||||
break;
|
||||
case 64:
|
||||
new_param = ConstantInt::get(Int64Ty, val);
|
||||
break;
|
||||
case 128:
|
||||
new_param = ConstantInt::get(Int128Ty, val);
|
||||
break;
|
||||
|
||||
}
|
||||
new_param =
|
||||
IRB.CreateIntCast(cint, IntegerType::get(C, cast_size), false);
|
||||
|
||||
}
|
||||
|
||||
@ -457,7 +418,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
IntegerType * intTyOp0 = NULL;
|
||||
IntegerType * intTyOp1 = NULL;
|
||||
unsigned max_size = 0, cast_size = 0;
|
||||
unsigned char attr = 0, do_cast = 0;
|
||||
unsigned char attr = 0;
|
||||
std::vector<Value *> args;
|
||||
|
||||
CmpInst *cmpInst = dyn_cast<CmpInst>(selectcmpInst);
|
||||
@ -523,7 +484,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
max_size = 128;
|
||||
|
||||
attr += 8;
|
||||
do_cast = 1;
|
||||
|
||||
} else {
|
||||
|
||||
@ -540,7 +500,9 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
|
||||
}
|
||||
|
||||
if (!max_size || max_size % 8 || max_size < 16) { continue; }
|
||||
if (!max_size || max_size < 16) { continue; }
|
||||
|
||||
if (max_size % 8) { max_size = (((max_size / 8) + 1) * 8); }
|
||||
|
||||
if (max_size > 128) {
|
||||
|
||||
@ -553,7 +515,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
}
|
||||
|
||||
max_size = 128;
|
||||
do_cast = 1;
|
||||
|
||||
}
|
||||
|
||||
@ -569,92 +530,30 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
break;
|
||||
default:
|
||||
cast_size = 128;
|
||||
do_cast = 1;
|
||||
|
||||
}
|
||||
|
||||
if (do_cast) {
|
||||
// errs() << "[CMPLOG] cmp " << *cmpInst << "(in function " <<
|
||||
// cmpInst->getFunction()->getName() << ")\n";
|
||||
|
||||
// F*cking LLVM optimized out any kind of bitcasts of ConstantInt values
|
||||
// creating illegal calls. WTF. So we have to work around this.
|
||||
// first bitcast to integer type of the same bitsize as the original
|
||||
// type (this is a nop, if already integer)
|
||||
Value *op0_i = IRB.CreateBitCast(
|
||||
op0, IntegerType::get(C, op0->getType()->getPrimitiveSizeInBits()));
|
||||
// then create a int cast, which does zext, trunc or bitcast. In our case
|
||||
// usually zext to the next larger supported type (this is a nop if
|
||||
// already the right type)
|
||||
Value *V0 =
|
||||
IRB.CreateIntCast(op0_i, IntegerType::get(C, cast_size), false);
|
||||
args.push_back(V0);
|
||||
Value *op1_i = IRB.CreateBitCast(
|
||||
op1, IntegerType::get(C, op1->getType()->getPrimitiveSizeInBits()));
|
||||
Value *V1 =
|
||||
IRB.CreateIntCast(op1_i, IntegerType::get(C, cast_size), false);
|
||||
args.push_back(V1);
|
||||
|
||||
ConstantInt *cint = dyn_cast<ConstantInt>(op0);
|
||||
if (cint) {
|
||||
|
||||
uint64_t val = cint->getZExtValue();
|
||||
// fprintf(stderr, "ConstantInt: %lu\n", val);
|
||||
ConstantInt *new_param = NULL;
|
||||
switch (cast_size) {
|
||||
|
||||
case 8:
|
||||
new_param = ConstantInt::get(Int8Ty, val);
|
||||
break;
|
||||
case 16:
|
||||
new_param = ConstantInt::get(Int16Ty, val);
|
||||
break;
|
||||
case 32:
|
||||
new_param = ConstantInt::get(Int32Ty, val);
|
||||
break;
|
||||
case 64:
|
||||
new_param = ConstantInt::get(Int64Ty, val);
|
||||
break;
|
||||
case 128:
|
||||
new_param = ConstantInt::get(Int128Ty, val);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (!new_param) { continue; }
|
||||
args.push_back(new_param);
|
||||
|
||||
} else {
|
||||
|
||||
Value *V0 = IRB.CreateBitCast(op0, IntegerType::get(C, cast_size));
|
||||
args.push_back(V0);
|
||||
|
||||
}
|
||||
|
||||
cint = dyn_cast<ConstantInt>(op1);
|
||||
if (cint) {
|
||||
|
||||
uint64_t val = cint->getZExtValue();
|
||||
ConstantInt *new_param = NULL;
|
||||
switch (cast_size) {
|
||||
|
||||
case 8:
|
||||
new_param = ConstantInt::get(Int8Ty, val);
|
||||
break;
|
||||
case 16:
|
||||
new_param = ConstantInt::get(Int16Ty, val);
|
||||
break;
|
||||
case 32:
|
||||
new_param = ConstantInt::get(Int32Ty, val);
|
||||
break;
|
||||
case 64:
|
||||
new_param = ConstantInt::get(Int64Ty, val);
|
||||
break;
|
||||
case 128:
|
||||
new_param = ConstantInt::get(Int128Ty, val);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (!new_param) { continue; }
|
||||
args.push_back(new_param);
|
||||
|
||||
} else {
|
||||
|
||||
Value *V1 = IRB.CreateBitCast(op1, IntegerType::get(C, cast_size));
|
||||
args.push_back(V1);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
args.push_back(op0);
|
||||
args.push_back(op1);
|
||||
|
||||
}
|
||||
// errs() << "[CMPLOG] casted parameters:\n0: " << *V0 << "\n1: " << *V1
|
||||
// << "\n";
|
||||
|
||||
ConstantInt *attribute = ConstantInt::get(Int8Ty, attr);
|
||||
args.push_back(attribute);
|
||||
@ -667,7 +566,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) {
|
||||
}
|
||||
|
||||
// fprintf(stderr, "_ExtInt(%u) castTo %u with attr %u didcast %u\n",
|
||||
// max_size, cast_size, attr, do_cast);
|
||||
// max_size, cast_size, attr);
|
||||
|
||||
switch (cast_size) {
|
||||
|
||||
|
@ -229,9 +229,9 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
dyn_cast<ConstantDataArray>(Var->getInitializer())) {
|
||||
|
||||
HasStr2 = true;
|
||||
Str2 = Array->getAsString();
|
||||
Str2 = Array->getRawDataValues();
|
||||
valueMap[Str2P] = new std::string(Str2.str());
|
||||
fprintf(stderr, "glo2 %s\n", Str2.str().c_str());
|
||||
// fprintf(stderr, "glo2 %s\n", Str2.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
@ -254,7 +254,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
Var->getInitializer())) {
|
||||
|
||||
HasStr1 = true;
|
||||
Str1 = Array->getAsString();
|
||||
Str1 = Array->getRawDataValues();
|
||||
valueMap[Str1P] = new std::string(Str1.str());
|
||||
// fprintf(stderr, "glo1 %s\n", Str1.str().c_str());
|
||||
|
||||
@ -316,7 +316,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
uint64_t len = ilen->getZExtValue();
|
||||
// if len is zero this is a pointless call but allow real
|
||||
// implementation to worry about that
|
||||
if (!len) continue;
|
||||
if (len < 2) continue;
|
||||
|
||||
if (isMemcmp) {
|
||||
|
||||
@ -362,19 +362,22 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
bool HasStr1 = getConstantStringInfo(Str1P, Str1);
|
||||
bool HasStr2 = getConstantStringInfo(Str2P, Str2);
|
||||
uint64_t constStrLen, unrollLen, constSizedLen = 0;
|
||||
bool isMemcmp =
|
||||
!callInst->getCalledFunction()->getName().compare(StringRef("memcmp"));
|
||||
bool isSizedcmp = isMemcmp ||
|
||||
!callInst->getCalledFunction()->getName().compare(
|
||||
StringRef("strncmp")) ||
|
||||
!callInst->getCalledFunction()->getName().compare(
|
||||
StringRef("strncasecmp"));
|
||||
bool isMemcmp = false;
|
||||
bool isSizedcmp = false;
|
||||
bool isCaseInsensitive = false;
|
||||
Function * Callee = callInst->getCalledFunction();
|
||||
if (Callee) {
|
||||
|
||||
isMemcmp = Callee->getName().compare("memcmp") == 0;
|
||||
isSizedcmp = isMemcmp || Callee->getName().compare("strncmp") == 0 ||
|
||||
Callee->getName().compare("strncasecmp") == 0;
|
||||
isCaseInsensitive = Callee->getName().compare("strcasecmp") == 0 ||
|
||||
Callee->getName().compare("strncasecmp") == 0;
|
||||
|
||||
}
|
||||
|
||||
Value *sizedValue = isSizedcmp ? callInst->getArgOperand(2) : NULL;
|
||||
bool isConstSized = sizedValue && isa<ConstantInt>(sizedValue);
|
||||
bool isCaseInsensitive = !callInst->getCalledFunction()->getName().compare(
|
||||
StringRef("strcasecmp")) ||
|
||||
!callInst->getCalledFunction()->getName().compare(
|
||||
StringRef("strncasecmp"));
|
||||
|
||||
if (!(HasStr1 || HasStr2)) {
|
||||
|
||||
@ -417,15 +420,29 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
|
||||
}
|
||||
|
||||
if (TmpConstStr.length() < 2 ||
|
||||
(TmpConstStr.length() == 2 && !TmpConstStr[1])) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
// add null termination character implicit in c strings
|
||||
TmpConstStr.append("\0", 1);
|
||||
if (!isMemcmp && TmpConstStr[TmpConstStr.length() - 1]) {
|
||||
|
||||
TmpConstStr.append("\0", 1);
|
||||
|
||||
}
|
||||
|
||||
// in the unusual case the const str has embedded null
|
||||
// characters, the string comparison functions should terminate
|
||||
// at the first null
|
||||
if (!isMemcmp)
|
||||
if (!isMemcmp) {
|
||||
|
||||
TmpConstStr.assign(TmpConstStr, 0, TmpConstStr.find('\0') + 1);
|
||||
|
||||
}
|
||||
|
||||
constStrLen = TmpConstStr.length();
|
||||
// prefer use of StringRef (in comparison to std::string a StringRef has
|
||||
// built-in runtime bounds checking, which makes debugging easier)
|
||||
@ -436,15 +453,6 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
|
||||
else
|
||||
unrollLen = constStrLen;
|
||||
|
||||
/*
|
||||
if (!be_quiet)
|
||||
errs() << callInst->getCalledFunction()->getName() << ": unroll len "
|
||||
<< unrollLen
|
||||
<< ((isSizedcmp && !isConstSized) ? ", variable n" : "") << ":
|
||||
"
|
||||
<< ConstStr << "\n";
|
||||
*/
|
||||
|
||||
/* split before the call instruction */
|
||||
BasicBlock *bb = callInst->getParent();
|
||||
BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(callInst));
|
||||
|
@ -14,5 +14,8 @@ typedef u64 PREV_LOC_T;
|
||||
/* Maximum ngram size */
|
||||
#define NGRAM_SIZE_MAX 16U
|
||||
|
||||
/* Maximum K for top-K context sensitivity */
|
||||
#define CTX_MAX_K 32U
|
||||
|
||||
#endif
|
||||
|
@ -149,8 +149,11 @@ bool SplitComparesTransform::simplifyFPCompares(Module &M) {
|
||||
auto op1 = FcmpInst->getOperand(1);
|
||||
|
||||
/* find out what the new predicate is going to be */
|
||||
auto pred = dyn_cast<CmpInst>(FcmpInst)->getPredicate();
|
||||
auto cmp_inst = dyn_cast<CmpInst>(FcmpInst);
|
||||
if (!cmp_inst) { continue; }
|
||||
auto pred = cmp_inst->getPredicate();
|
||||
CmpInst::Predicate new_pred;
|
||||
|
||||
switch (pred) {
|
||||
|
||||
case CmpInst::FCMP_UGE:
|
||||
@ -276,8 +279,11 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
|
||||
auto op1 = IcmpInst->getOperand(1);
|
||||
|
||||
/* find out what the new predicate is going to be */
|
||||
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
||||
auto cmp_inst = dyn_cast<CmpInst>(IcmpInst);
|
||||
if (!cmp_inst) { continue; }
|
||||
auto pred = cmp_inst->getPredicate();
|
||||
CmpInst::Predicate new_pred;
|
||||
|
||||
switch (pred) {
|
||||
|
||||
case CmpInst::ICMP_UGE:
|
||||
@ -412,8 +418,11 @@ bool SplitComparesTransform::simplifyIntSignedness(Module &M) {
|
||||
IntegerType *IntType = IntegerType::get(C, bitw);
|
||||
|
||||
/* get the new predicate */
|
||||
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
||||
auto cmp_inst = dyn_cast<CmpInst>(IcmpInst);
|
||||
if (!cmp_inst) { continue; }
|
||||
auto pred = cmp_inst->getPredicate();
|
||||
CmpInst::Predicate new_pred;
|
||||
|
||||
if (pred == CmpInst::ICMP_SGT) {
|
||||
|
||||
new_pred = CmpInst::ICMP_UGT;
|
||||
@ -603,6 +612,10 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
|
||||
if (op_size != op1->getType()->getPrimitiveSizeInBits()) { continue; }
|
||||
|
||||
const unsigned int sizeInBits = op0->getType()->getPrimitiveSizeInBits();
|
||||
|
||||
// BUG FIXME TODO: u64 does not work for > 64 bit ... e.g. 80 and 128 bit
|
||||
if (sizeInBits > 64) { continue; }
|
||||
|
||||
const unsigned int precision = sizeInBits == 32 ? 24
|
||||
: sizeInBits == 64 ? 53
|
||||
: sizeInBits == 128 ? 113
|
||||
@ -610,8 +623,7 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) {
|
||||
: sizeInBits == 80 ? 65
|
||||
: sizeInBits - 8;
|
||||
|
||||
const unsigned shiftR_exponent = precision - 1;
|
||||
// BUG FIXME TODO: u64 does not work for > 64 bit ... e.g. 80 and 128 bit
|
||||
const unsigned shiftR_exponent = precision - 1;
|
||||
const unsigned long long mask_fraction =
|
||||
(1ULL << (shiftR_exponent - 1)) | ((1ULL << (shiftR_exponent - 1)) - 1);
|
||||
const unsigned long long mask_exponent =
|
||||
@ -1113,7 +1125,9 @@ size_t SplitComparesTransform::splitIntCompares(Module &M, unsigned bitw) {
|
||||
auto op0 = IcmpInst->getOperand(0);
|
||||
auto op1 = IcmpInst->getOperand(1);
|
||||
|
||||
auto pred = dyn_cast<CmpInst>(IcmpInst)->getPredicate();
|
||||
auto cmp_inst = dyn_cast<CmpInst>(IcmpInst);
|
||||
if (!cmp_inst) { continue; }
|
||||
auto pred = cmp_inst->getPredicate();
|
||||
|
||||
BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst));
|
||||
|
||||
|
@ -1 +1 @@
|
||||
6ab6bf28de
|
||||
d1ca56b84e
|
||||
|
@ -17,7 +17,7 @@ The idea and much of the initial implementation comes from Andrew Griffiths.
|
||||
The actual implementation on current QEMU (shipped as qemuafl) is from
|
||||
Andrea Fioraldi. Special thanks to abiondo that re-enabled TCG chaining.
|
||||
|
||||
## 2) How to use
|
||||
## 2) How to use qemu_mode
|
||||
|
||||
The feature is implemented with a patched QEMU. The simplest way
|
||||
to build it is to run ./build_qemu_support.sh. The script will download,
|
||||
@ -99,6 +99,13 @@ Just set AFL_QEMU_INST_RANGES=A,B,C...
|
||||
The format of the items in the list is either a range of addresses like 0x123-0x321
|
||||
or a module name like module.so (that is matched in the mapped object filename).
|
||||
|
||||
Alternatively you can tell QEMU to ignore part of an address space for instrumentation.
|
||||
|
||||
Just set AFL_QEMU_EXCLUDE_RANGES=A,B,C...
|
||||
|
||||
The format of the items on the list is the same as for AFL_QEMU_INST_RANGES, and excluding ranges
|
||||
takes priority over any included ranges or AFL_INST_LIBS.
|
||||
|
||||
## 7) CompareCoverage
|
||||
|
||||
CompareCoverage is a sub-instrumentation with effects similar to laf-intel.
|
||||
@ -176,7 +183,12 @@ Comparative measurements of execution speed or instrumentation coverage will be
|
||||
fairly meaningless if the optimization levels or instrumentation scopes don't
|
||||
match.
|
||||
|
||||
## 12) Gotchas, feedback, bugs
|
||||
## 12) Other features
|
||||
|
||||
With `AFL_QEMU_FORCE_DFL` you force QEMU to ignore the registered signal
|
||||
handlers of the target.
|
||||
|
||||
## 13) Gotchas, feedback, bugs
|
||||
|
||||
If you need to fix up checksums or do other cleanup on mutated test cases, see
|
||||
utils/custom_mutators/ for a viable solution.
|
||||
@ -197,19 +209,12 @@ with -march=core2, can help.
|
||||
Beyond that, this is an early-stage mechanism, so fields reports are welcome.
|
||||
You can send them to <afl-users@googlegroups.com>.
|
||||
|
||||
## 13) Alternatives: static rewriting
|
||||
## 14) Alternatives: static rewriting
|
||||
|
||||
Statically rewriting binaries just once, instead of attempting to translate
|
||||
them at run time, can be a faster alternative. That said, static rewriting is
|
||||
fraught with peril, because it depends on being able to properly and fully model
|
||||
program control flow without actually executing each and every code path.
|
||||
|
||||
The best implementation is this one:
|
||||
|
||||
https://github.com/vanhauser-thc/afl-dyninst
|
||||
|
||||
The issue however is Dyninst which is not rewriting the binaries so that
|
||||
they run stable. A lot of crashes happen, especially in C++ programs that
|
||||
use throw/catch. Try it first, and if it works for you be happy as it is
|
||||
2-3x as fast as qemu_mode, however usually not as fast as QEMU persistent mode.
|
||||
|
||||
Checkout the "Fuzzing binary-only targets" section in our main README.md and
|
||||
the docs/binaryonly_fuzzing.md document for more information and hints.
|
||||
|
@ -59,51 +59,11 @@ if [ ! -f "../afl-showmap" ]; then
|
||||
|
||||
fi
|
||||
|
||||
PREREQ_NOTFOUND=
|
||||
for i in git wget sha384sum bison flex iconv patch pkg-config; do
|
||||
|
||||
T=`command -v "$i" 2>/dev/null`
|
||||
|
||||
if [ "$T" = "" ]; then
|
||||
|
||||
echo "[-] Error: '$i' not found, please install first."
|
||||
PREREQ_NOTFOUND=1
|
||||
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
PYTHONBIN=`command -v python3 || command -v python || command -v python2`
|
||||
|
||||
if [ "$PYTHONBIN" = "" ]; then
|
||||
echo "[-] Error: 'python' not found, please install using 'sudo apt install python3'."
|
||||
PREREQ_NOTFOUND=1
|
||||
fi
|
||||
|
||||
|
||||
if [ ! -d "/usr/include/glib-2.0/" -a ! -d "/usr/local/include/glib-2.0/" ]; then
|
||||
|
||||
echo "[-] Error: devel version of 'glib2' not found, please install first."
|
||||
PREREQ_NOTFOUND=1
|
||||
|
||||
fi
|
||||
|
||||
if [ ! -d "/usr/include/pixman-1/" -a ! -d "/usr/local/include/pixman-1/" ]; then
|
||||
|
||||
echo "[-] Error: devel version of 'pixman-1' not found, please install first."
|
||||
PREREQ_NOTFOUND=1
|
||||
|
||||
fi
|
||||
|
||||
if echo "$CC" | grep -qF /afl-; then
|
||||
|
||||
echo "[-] Error: do not use afl-gcc or afl-clang to compile this tool."
|
||||
PREREQ_NOTFOUND=1
|
||||
|
||||
fi
|
||||
|
||||
if [ "$PREREQ_NOTFOUND" = "1" ]; then
|
||||
exit 1
|
||||
|
||||
fi
|
||||
|
||||
echo "[+] All checks passed!"
|
||||
@ -131,9 +91,13 @@ test -d qemuafl || { echo "[-] Not checked out, please install git or check your
|
||||
echo "[+] Got qemuafl."
|
||||
|
||||
cd "qemuafl" || exit 1
|
||||
echo "[*] Checking out $QEMUAFL_VERSION"
|
||||
sh -c 'git stash && git stash drop' 1>/dev/null 2>/dev/null
|
||||
git checkout "$QEMUAFL_VERSION" || echo Warning: could not check out to commit $QEMUAFL_VERSION
|
||||
if [ -n "$NO_CHECKOUT" ]; then
|
||||
echo "[*] Skipping checkout to $QEMUAFL_VERSION"
|
||||
else
|
||||
echo "[*] Checking out $QEMUAFL_VERSION"
|
||||
sh -c 'git stash' 1>/dev/null 2>/dev/null
|
||||
git checkout "$QEMUAFL_VERSION" || echo Warning: could not check out to commit $QEMUAFL_VERSION
|
||||
fi
|
||||
|
||||
echo "[*] Making sure imported headers matches"
|
||||
cp "../../include/config.h" "./qemuafl/imported/" || exit 1
|
||||
@ -233,15 +197,13 @@ QEMU_CONF_FLAGS=" \
|
||||
--disable-xen \
|
||||
--disable-xen-pci-passthrough \
|
||||
--disable-xfsctl \
|
||||
--enable-pie \
|
||||
--python=${PYTHONBIN} \
|
||||
--target-list="${CPU_TARGET}-linux-user" \
|
||||
--without-default-devices \
|
||||
"
|
||||
|
||||
if [ -n "${CROSS_PREFIX}" ]; then
|
||||
|
||||
QEMU_CONF_FLAGS="${QEMU_CONF_FLAGS} --cross-prefix=${CROSS_PREFIX}"
|
||||
QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS --cross-prefix=$CROSS_PREFIX"
|
||||
|
||||
fi
|
||||
|
||||
@ -249,10 +211,15 @@ if [ "$STATIC" = "1" ]; then
|
||||
|
||||
echo Building STATIC binary
|
||||
|
||||
QEMU_CONF_FLAGS="${QEMU_CONF_FLAGS} \
|
||||
QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \
|
||||
--static \
|
||||
--extra-cflags=-DAFL_QEMU_STATIC_BUILD=1 \
|
||||
"
|
||||
|
||||
else
|
||||
|
||||
QEMU_CONF_FLAGS="${QEMU_CONF_FLAGS} --enable-pie "
|
||||
|
||||
fi
|
||||
|
||||
if [ "$DEBUG" = "1" ]; then
|
||||
@ -262,7 +229,7 @@ if [ "$DEBUG" = "1" ]; then
|
||||
# --enable-gcov might go here but incurs a mesonbuild error on meson
|
||||
# versions prior to 0.56:
|
||||
# https://github.com/qemu/meson/commit/903d5dd8a7dc1d6f8bef79e66d6ebc07c
|
||||
QEMU_CONF_FLAGS="${QEMU_CONF_FLAGS} \
|
||||
QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \
|
||||
--disable-strip \
|
||||
--enable-debug \
|
||||
--enable-debug-info \
|
||||
@ -275,7 +242,7 @@ if [ "$DEBUG" = "1" ]; then
|
||||
|
||||
else
|
||||
|
||||
QEMU_CONF_FLAGS="${QEMU_CONF_FLAGS} \
|
||||
QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \
|
||||
--disable-debug-info \
|
||||
--disable-debug-mutex \
|
||||
--disable-debug-tcg \
|
||||
@ -290,7 +257,7 @@ if [ "$PROFILING" = "1" ]; then
|
||||
|
||||
echo Building PROFILED binary
|
||||
|
||||
QEMU_CONF_FLAGS="${QEMU_CONF_FLAGS} \
|
||||
QEMU_CONF_FLAGS="$QEMU_CONF_FLAGS \
|
||||
--enable-gprof \
|
||||
--enable-profiler \
|
||||
"
|
||||
@ -298,7 +265,7 @@ if [ "$PROFILING" = "1" ]; then
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
./configure ${QEMU_CONF_FLAGS} || exit 1
|
||||
./configure $QEMU_CONF_FLAGS || exit 1
|
||||
|
||||
echo "[+] Configuration complete."
|
||||
|
||||
@ -368,18 +335,42 @@ if [ "$ORIG_CROSS" = "" ]; then
|
||||
then # works on Arch Linux
|
||||
CROSS=$CPU_TARGET-pc-linux-gnu-gcc
|
||||
fi
|
||||
if ! command -v "$CROSS" > /dev/null && [ "$CPU_TARGET" = "i386" ]
|
||||
then
|
||||
CROSS=i686-linux-gnu-gcc
|
||||
if ! command -v "$CROSS" > /dev/null
|
||||
then # works on Arch Linux
|
||||
CROSS=i686-pc-linux-gnu-gcc
|
||||
fi
|
||||
if ! command -v "$CROSS" > /dev/null && [ "`uname -m`" = "x86_64" ]
|
||||
then # set -m32
|
||||
test "$CC" = "" && CC="gcc"
|
||||
CROSS="$CC"
|
||||
CROSS_FLAGS=-m32
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! command -v "$CROSS" > /dev/null
|
||||
then
|
||||
if ! command -v "$CROSS" > /dev/null ; then
|
||||
if [ "$CPU_TARGET" = "$(uname -m)" ] ; then
|
||||
echo "[+] Building afl++ qemu support libraries with CC=$CC"
|
||||
echo "[+] Building libcompcov ..."
|
||||
make -C libcompcov && echo "[+] libcompcov ready"
|
||||
echo "[+] Building unsigaction ..."
|
||||
make -C unsigaction && echo "[+] unsigaction ready"
|
||||
echo "[+] Building libqasan ..."
|
||||
make -C libqasan && echo "[+] unsigaction ready"
|
||||
else
|
||||
echo "[!] Cross compiler $CROSS could not be found, cannot compile libcompcov libqasan and unsigaction"
|
||||
fi
|
||||
else
|
||||
echo "[+] Building afl++ qemu support libraries with CC=\"$CROSS $CROSS_FLAGS\""
|
||||
echo "[+] Building libcompcov ..."
|
||||
make -C libcompcov CC=$CROSS && echo "[+] libcompcov ready"
|
||||
make -C libcompcov CC="$CROSS $CROSS_FLAGS" && echo "[+] libcompcov ready"
|
||||
echo "[+] Building unsigaction ..."
|
||||
make -C unsigaction CC=$CROSS && echo "[+] unsigaction ready"
|
||||
make -C unsigaction CC="$CROSS $CROSS_FLAGS" && echo "[+] unsigaction ready"
|
||||
echo "[+] Building libqasan ..."
|
||||
make -C libqasan CC=$CROSS && echo "[+] unsigaction ready"
|
||||
make -C libqasan CC="$CROSS $CROSS_FLAGS" && echo "[+] unsigaction ready"
|
||||
fi
|
||||
|
||||
echo "[+] All done for qemu_mode, enjoy!"
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/shm.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
@ -159,14 +161,15 @@ static void __compcov_load(void) {
|
||||
|
||||
}
|
||||
|
||||
static void __compcov_trace(u64 cur_loc, const u8 *v0, const u8 *v1, size_t n) {
|
||||
static void __compcov_trace(uintptr_t cur_loc, const u8 *v0, const u8 *v1,
|
||||
size_t n) {
|
||||
|
||||
size_t i;
|
||||
|
||||
if (debug_fd != 1) {
|
||||
|
||||
char debugbuf[4096];
|
||||
snprintf(debugbuf, sizeof(debugbuf), "0x%llx %s %s %zu\n", cur_loc,
|
||||
snprintf(debugbuf, sizeof(debugbuf), "0x%" PRIxPTR " %s %s %zu\n", cur_loc,
|
||||
v0 == NULL ? "(null)" : (char *)v0,
|
||||
v1 == NULL ? "(null)" : (char *)v1, n);
|
||||
write(debug_fd, debugbuf, strlen(debugbuf));
|
||||
@ -206,7 +209,7 @@ int strcmp(const char *str1, const char *str2) {
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
uintptr_t cur_loc = (uintptr_t)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
@ -235,7 +238,7 @@ int strncmp(const char *str1, const char *str2, size_t len) {
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
uintptr_t cur_loc = (uintptr_t)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
@ -265,7 +268,7 @@ int strcasecmp(const char *str1, const char *str2) {
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
uintptr_t cur_loc = (uintptr_t)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
@ -296,7 +299,7 @@ int strncasecmp(const char *str1, const char *str2, size_t len) {
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
uintptr_t cur_loc = (uintptr_t)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
@ -324,7 +327,7 @@ int memcmp(const void *mem1, const void *mem2, size_t len) {
|
||||
|
||||
if (n <= MAX_CMP_LENGTH) {
|
||||
|
||||
u64 cur_loc = (u64)retaddr;
|
||||
uintptr_t cur_loc = (uintptr_t)retaddr;
|
||||
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||
cur_loc &= MAP_SIZE - 1;
|
||||
|
||||
|
@ -4,16 +4,25 @@ This library is the injected runtime used by QEMU AddressSanitizer (QASan).
|
||||
|
||||
The original repository is [here](https://github.com/andreafioraldi/qasan).
|
||||
|
||||
The version embedded in qemuafl is an updated version of just the usermode part and this runtime in injected via LD_PRELOAD (so works just for dynamically linked binaries).
|
||||
The version embedded in qemuafl is an updated version of just the usermode part
|
||||
and this runtime is injected via LD_PRELOAD (so works just for dynamically
|
||||
linked binaries).
|
||||
|
||||
The usage is super simple, just set the env var `AFL_USE_QASAN=1` when fuzzing in qemu mode (-Q). afl-fuzz will automatically set AFL_PRELOAD to load this library and enable the QASan instrumentation in afl-qemu-trace.
|
||||
The usage is super simple, just set the env var `AFL_USE_QASAN=1` when fuzzing
|
||||
in qemu mode (-Q). afl-fuzz will automatically set AFL_PRELOAD to load this
|
||||
library and enable the QASan instrumentation in afl-qemu-trace.
|
||||
|
||||
For debugging purposes, we still suggest to run the original QASan as the stacktrace support for ARM (just a debug feature, it does not affect the bug finding capabilities during fuzzing) is WIP.
|
||||
For debugging purposes, we still suggest to run the original QASan as the
|
||||
stacktrace support for ARM (just a debug feature, it does not affect the bug
|
||||
finding capabilities during fuzzing) is WIP.
|
||||
|
||||
### When I should use QASan?
|
||||
### When should I use QASan?
|
||||
|
||||
If your target binary is PIC x86_64, you should before give a try to [retrowrite](https://github.com/HexHive/retrowrite) for static rewriting.
|
||||
If your target binary is PIC x86_64, you should also give a try to
|
||||
[retrowrite](https://github.com/HexHive/retrowrite) for static rewriting.
|
||||
|
||||
If it fails, or if your binary is for another architecture, or you want to use persistent and snapshot mdoe, AFL++ QASan mode is what you want/have to use.
|
||||
If it fails, or if your binary is for another architecture, or you want to use
|
||||
persistent and snapshot mode, AFL++ QASan mode is what you want/have to use.
|
||||
|
||||
Note that the overhead of libdislocator when combined with QEMU mode is much lower but it can catch less bugs. This is a short blanket, take your choice.
|
||||
Note that the overhead of libdislocator when combined with QEMU mode is much
|
||||
lower but it can catch less bugs. This is a short blanket, take your choice.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -26,6 +26,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "libqasan.h"
|
||||
#include "map_macro.h"
|
||||
|
||||
ssize_t (*__lq_libc_write)(int, const void *, size_t);
|
||||
ssize_t (*__lq_libc_read)(int, void *, size_t);
|
||||
char *(*__lq_libc_fgets)(char *, int, FILE *);
|
||||
int (*__lq_libc_atoi)(const char *);
|
||||
long (*__lq_libc_atol)(const char *);
|
||||
@ -35,6 +37,8 @@ void __libqasan_init_hooks(void) {
|
||||
|
||||
__libqasan_init_malloc();
|
||||
|
||||
__lq_libc_write = ASSERT_DLSYM(write);
|
||||
__lq_libc_read = ASSERT_DLSYM(read);
|
||||
__lq_libc_fgets = ASSERT_DLSYM(fgets);
|
||||
__lq_libc_atoi = ASSERT_DLSYM(atoi);
|
||||
__lq_libc_atol = ASSERT_DLSYM(atol);
|
||||
@ -42,6 +46,30 @@ void __libqasan_init_hooks(void) {
|
||||
|
||||
}
|
||||
|
||||
ssize_t write(int fd, const void *buf, size_t count) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: write(%d, %p, %zu)\n", rtv, fd, buf, count);
|
||||
ssize_t r = __lq_libc_write(fd, buf, count);
|
||||
QASAN_DEBUG("\t\t = %zd\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
ssize_t read(int fd, void *buf, size_t count) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: read(%d, %p, %zu)\n", rtv, fd, buf, count);
|
||||
ssize_t r = __lq_libc_read(fd, buf, count);
|
||||
QASAN_DEBUG("\t\t = %zd\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
size_t malloc_usable_size(const void *ptr) {
|
||||
|
||||
@ -54,7 +82,7 @@ size_t malloc_usable_size(void *ptr) {
|
||||
|
||||
QASAN_DEBUG("%14p: malloc_usable_size(%p)\n", rtv, ptr);
|
||||
size_t r = __libqasan_malloc_usable_size((void *)ptr);
|
||||
QASAN_DEBUG("\t\t = %ld\n", r);
|
||||
QASAN_DEBUG("\t\t = %zu\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
@ -64,7 +92,7 @@ void *malloc(size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: malloc(%ld)\n", rtv, size);
|
||||
QASAN_DEBUG("%14p: malloc(%zu)\n", rtv, size);
|
||||
void *r = __libqasan_malloc(size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -76,7 +104,7 @@ void *calloc(size_t nmemb, size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: calloc(%ld, %ld)\n", rtv, nmemb, size);
|
||||
QASAN_DEBUG("%14p: calloc(%zu, %zu)\n", rtv, nmemb, size);
|
||||
void *r = __libqasan_calloc(nmemb, size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -88,7 +116,7 @@ void *realloc(void *ptr, size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: realloc(%p, %ld)\n", rtv, ptr, size);
|
||||
QASAN_DEBUG("%14p: realloc(%p, %zu)\n", rtv, ptr, size);
|
||||
void *r = __libqasan_realloc(ptr, size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -100,7 +128,7 @@ int posix_memalign(void **memptr, size_t alignment, size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: posix_memalign(%p, %ld, %ld)\n", rtv, memptr, alignment,
|
||||
QASAN_DEBUG("%14p: posix_memalign(%p, %zu, %zu)\n", rtv, memptr, alignment,
|
||||
size);
|
||||
int r = __libqasan_posix_memalign(memptr, alignment, size);
|
||||
QASAN_DEBUG("\t\t = %d [*memptr = %p]\n", r, *memptr);
|
||||
@ -113,7 +141,7 @@ void *memalign(size_t alignment, size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memalign(%ld, %ld)\n", rtv, alignment, size);
|
||||
QASAN_DEBUG("%14p: memalign(%zu, %zu)\n", rtv, alignment, size);
|
||||
void *r = __libqasan_memalign(alignment, size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -125,7 +153,7 @@ void *aligned_alloc(size_t alignment, size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: aligned_alloc(%ld, %ld)\n", rtv, alignment, size);
|
||||
QASAN_DEBUG("%14p: aligned_alloc(%zu, %zu)\n", rtv, alignment, size);
|
||||
void *r = __libqasan_aligned_alloc(alignment, size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -137,7 +165,7 @@ void *valloc(size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: valloc(%ld)\n", rtv, size);
|
||||
QASAN_DEBUG("%14p: valloc(%zu)\n", rtv, size);
|
||||
void *r = __libqasan_memalign(sysconf(_SC_PAGESIZE), size);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -149,7 +177,7 @@ void *pvalloc(size_t size) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: pvalloc(%ld)\n", rtv, size);
|
||||
QASAN_DEBUG("%14p: pvalloc(%zu)\n", rtv, size);
|
||||
size_t page_size = sysconf(_SC_PAGESIZE);
|
||||
size = (size & (page_size - 1)) + page_size;
|
||||
void *r = __libqasan_memalign(page_size, size);
|
||||
@ -174,7 +202,9 @@ char *fgets(char *s, int size, FILE *stream) {
|
||||
|
||||
QASAN_DEBUG("%14p: fgets(%p, %d, %p)\n", rtv, s, size, stream);
|
||||
QASAN_STORE(s, size);
|
||||
#ifndef __ANDROID__
|
||||
QASAN_LOAD(stream, sizeof(FILE));
|
||||
#endif
|
||||
char *r = __lq_libc_fgets(s, size, stream);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
|
||||
@ -186,7 +216,7 @@ int memcmp(const void *s1, const void *s2, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memcmp(%p, %p, %ld)\n", rtv, s1, s2, n);
|
||||
QASAN_DEBUG("%14p: memcmp(%p, %p, %zu)\n", rtv, s1, s2, n);
|
||||
QASAN_LOAD(s1, n);
|
||||
QASAN_LOAD(s2, n);
|
||||
int r = __libqasan_memcmp(s1, s2, n);
|
||||
@ -200,7 +230,7 @@ void *memcpy(void *dest, const void *src, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memcpy(%p, %p, %ld)\n", rtv, dest, src, n);
|
||||
QASAN_DEBUG("%14p: memcpy(%p, %p, %zu)\n", rtv, dest, src, n);
|
||||
QASAN_LOAD(src, n);
|
||||
QASAN_STORE(dest, n);
|
||||
void *r = __libqasan_memcpy(dest, src, n);
|
||||
@ -214,7 +244,7 @@ void *mempcpy(void *dest, const void *src, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: mempcpy(%p, %p, %ld)\n", rtv, dest, src, n);
|
||||
QASAN_DEBUG("%14p: mempcpy(%p, %p, %zu)\n", rtv, dest, src, n);
|
||||
QASAN_LOAD(src, n);
|
||||
QASAN_STORE(dest, n);
|
||||
void *r = (uint8_t *)__libqasan_memcpy(dest, src, n) + n;
|
||||
@ -228,7 +258,7 @@ void *memmove(void *dest, const void *src, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memmove(%p, %p, %ld)\n", rtv, dest, src, n);
|
||||
QASAN_DEBUG("%14p: memmove(%p, %p, %zu)\n", rtv, dest, src, n);
|
||||
QASAN_LOAD(src, n);
|
||||
QASAN_STORE(dest, n);
|
||||
void *r = __libqasan_memmove(dest, src, n);
|
||||
@ -242,7 +272,7 @@ void *memset(void *s, int c, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memset(%p, %d, %ld)\n", rtv, s, c, n);
|
||||
QASAN_DEBUG("%14p: memset(%p, %d, %zu)\n", rtv, s, c, n);
|
||||
QASAN_STORE(s, n);
|
||||
void *r = __libqasan_memset(s, c, n);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
@ -255,7 +285,7 @@ void *memchr(const void *s, int c, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memchr(%p, %d, %ld)\n", rtv, s, c, n);
|
||||
QASAN_DEBUG("%14p: memchr(%p, %d, %zu)\n", rtv, s, c, n);
|
||||
void *r = __libqasan_memchr(s, c, n);
|
||||
if (r == NULL)
|
||||
QASAN_LOAD(s, n);
|
||||
@ -271,7 +301,7 @@ void *memrchr(const void *s, int c, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memrchr(%p, %d, %ld)\n", rtv, s, c, n);
|
||||
QASAN_DEBUG("%14p: memrchr(%p, %d, %zu)\n", rtv, s, c, n);
|
||||
QASAN_LOAD(s, n);
|
||||
void *r = __libqasan_memrchr(s, c, n);
|
||||
QASAN_DEBUG("\t\t = %p\n", r);
|
||||
@ -285,7 +315,7 @@ void *memmem(const void *haystack, size_t haystacklen, const void *needle,
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: memmem(%p, %ld, %p, %ld)\n", rtv, haystack, haystacklen,
|
||||
QASAN_DEBUG("%14p: memmem(%p, %zu, %p, %zu)\n", rtv, haystack, haystacklen,
|
||||
needle, needlelen);
|
||||
QASAN_LOAD(haystack, haystacklen);
|
||||
QASAN_LOAD(needle, needlelen);
|
||||
@ -301,7 +331,7 @@ void bzero(void *s, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: bzero(%p, %ld)\n", rtv, s, n);
|
||||
QASAN_DEBUG("%14p: bzero(%p, %zu)\n", rtv, s, n);
|
||||
QASAN_STORE(s, n);
|
||||
__libqasan_memset(s, 0, n);
|
||||
|
||||
@ -313,7 +343,7 @@ void explicit_bzero(void *s, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: bzero(%p, %ld)\n", rtv, s, n);
|
||||
QASAN_DEBUG("%14p: bzero(%p, %zu)\n", rtv, s, n);
|
||||
QASAN_STORE(s, n);
|
||||
__libqasan_memset(s, 0, n);
|
||||
|
||||
@ -323,7 +353,7 @@ int bcmp(const void *s1, const void *s2, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: bcmp(%p, %p, %ld)\n", rtv, s1, s2, n);
|
||||
QASAN_DEBUG("%14p: bcmp(%p, %p, %zu)\n", rtv, s1, s2, n);
|
||||
QASAN_LOAD(s1, n);
|
||||
QASAN_LOAD(s2, n);
|
||||
int r = __libqasan_bcmp(s1, s2, n);
|
||||
@ -381,7 +411,7 @@ int strncasecmp(const char *s1, const char *s2, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: strncasecmp(%p, %p, %ld)\n", rtv, s1, s2, n);
|
||||
QASAN_DEBUG("%14p: strncasecmp(%p, %p, %zu)\n", rtv, s1, s2, n);
|
||||
size_t l1 = __libqasan_strnlen(s1, n);
|
||||
QASAN_LOAD(s1, l1);
|
||||
size_t l2 = __libqasan_strnlen(s2, n);
|
||||
@ -431,7 +461,7 @@ int strncmp(const char *s1, const char *s2, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: strncmp(%p, %p, %ld)\n", rtv, s1, s2, n);
|
||||
QASAN_DEBUG("%14p: strncmp(%p, %p, %zu)\n", rtv, s1, s2, n);
|
||||
size_t l1 = __libqasan_strnlen(s1, n);
|
||||
QASAN_LOAD(s1, l1);
|
||||
size_t l2 = __libqasan_strnlen(s2, n);
|
||||
@ -462,7 +492,7 @@ char *strncpy(char *dest, const char *src, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: strncpy(%p, %p, %ld)\n", rtv, dest, src, n);
|
||||
QASAN_DEBUG("%14p: strncpy(%p, %p, %zu)\n", rtv, dest, src, n);
|
||||
size_t l = __libqasan_strnlen(src, n);
|
||||
QASAN_STORE(dest, n);
|
||||
void *r;
|
||||
@ -521,7 +551,7 @@ size_t strlen(const char *s) {
|
||||
QASAN_DEBUG("%14p: strlen(%p)\n", rtv, s);
|
||||
size_t r = __libqasan_strlen(s);
|
||||
QASAN_LOAD(s, r + 1);
|
||||
QASAN_DEBUG("\t\t = %ld\n", r);
|
||||
QASAN_DEBUG("\t\t = %zu\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
@ -531,10 +561,10 @@ size_t strnlen(const char *s, size_t n) {
|
||||
|
||||
void *rtv = __builtin_return_address(0);
|
||||
|
||||
QASAN_DEBUG("%14p: strnlen(%p, %ld)\n", rtv, s, n);
|
||||
QASAN_DEBUG("%14p: strnlen(%p, %zu)\n", rtv, s, n);
|
||||
size_t r = __libqasan_strnlen(s, n);
|
||||
QASAN_LOAD(s, r);
|
||||
QASAN_DEBUG("\t\t = %ld\n", r);
|
||||
QASAN_DEBUG("\t\t = %zu\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
@ -621,7 +651,7 @@ size_t wcslen(const wchar_t *s) {
|
||||
QASAN_DEBUG("%14p: wcslen(%p)\n", rtv, s);
|
||||
size_t r = __libqasan_wcslen(s);
|
||||
QASAN_LOAD(s, sizeof(wchar_t) * (r + 1));
|
||||
QASAN_DEBUG("\t\t = %ld\n", r);
|
||||
QASAN_DEBUG("\t\t = %zu\n", r);
|
||||
|
||||
return r;
|
||||
|
||||
|
@ -72,7 +72,7 @@ void __libqasan_print_maps(void) {
|
||||
|
||||
QASAN_LOG("QEMU-AddressSanitizer (v%s)\n", QASAN_VERSTR);
|
||||
QASAN_LOG(
|
||||
"Copyright (C) 2019-2020 Andrea Fioraldi <andreafioraldi@gmail.com>\n");
|
||||
"Copyright (C) 2019-2021 Andrea Fioraldi <andreafioraldi@gmail.com>\n");
|
||||
QASAN_LOG("\n");
|
||||
|
||||
if (__qasan_log) __libqasan_print_maps();
|
||||
|
@ -24,6 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "libqasan.h"
|
||||
#include <features.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
@ -65,9 +66,26 @@ struct chunk_struct {
|
||||
|
||||
};
|
||||
|
||||
#ifdef __GLIBC__
|
||||
|
||||
void *(*__lq_libc_malloc)(size_t);
|
||||
void (*__lq_libc_free)(void *);
|
||||
#define backend_malloc __lq_libc_malloc
|
||||
#define backend_free __lq_libc_free
|
||||
|
||||
#define TMP_ZONE_SIZE 4096
|
||||
static int __tmp_alloc_zone_idx;
|
||||
static unsigned char __tmp_alloc_zone[TMP_ZONE_SIZE];
|
||||
|
||||
#else
|
||||
|
||||
// From dlmalloc.c
|
||||
void *dlmalloc(size_t);
|
||||
void dlfree(void *);
|
||||
void * dlmalloc(size_t);
|
||||
void dlfree(void *);
|
||||
#define backend_malloc dlmalloc
|
||||
#define backend_free dlfree
|
||||
|
||||
#endif
|
||||
|
||||
int __libqasan_malloc_initialized;
|
||||
|
||||
@ -102,9 +120,9 @@ static int quanratine_push(struct chunk_begin *ck) {
|
||||
quarantine_bytes -= tmp->requested_size;
|
||||
|
||||
if (tmp->aligned_orig)
|
||||
dlfree(tmp->aligned_orig);
|
||||
backend_free(tmp->aligned_orig);
|
||||
else
|
||||
dlfree(tmp);
|
||||
backend_free(tmp);
|
||||
|
||||
}
|
||||
|
||||
@ -122,6 +140,11 @@ void __libqasan_init_malloc(void) {
|
||||
|
||||
if (__libqasan_malloc_initialized) return;
|
||||
|
||||
#ifdef __GLIBC__
|
||||
__lq_libc_malloc = dlsym(RTLD_NEXT, "malloc");
|
||||
__lq_libc_free = dlsym(RTLD_NEXT, "free");
|
||||
#endif
|
||||
|
||||
LOCK_INIT(&quarantine_lock, PTHREAD_PROCESS_PRIVATE);
|
||||
|
||||
__libqasan_malloc_initialized = 1;
|
||||
@ -142,13 +165,27 @@ size_t __libqasan_malloc_usable_size(void *ptr) {
|
||||
|
||||
void *__libqasan_malloc(size_t size) {
|
||||
|
||||
if (!__libqasan_malloc_initialized) { __libqasan_init_malloc(); }
|
||||
if (!__libqasan_malloc_initialized) {
|
||||
|
||||
if (!__libqasan_malloc_initialized) __libqasan_init_malloc();
|
||||
__libqasan_init_malloc();
|
||||
|
||||
#ifdef __GLIBC__
|
||||
void *r = &__tmp_alloc_zone[__tmp_alloc_zone_idx];
|
||||
|
||||
if (size & (ALLOC_ALIGN_SIZE - 1))
|
||||
__tmp_alloc_zone_idx +=
|
||||
(size & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE;
|
||||
else
|
||||
__tmp_alloc_zone_idx += size;
|
||||
|
||||
return r;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int state = QASAN_SWAP(QASAN_DISABLED); // disable qasan for this thread
|
||||
|
||||
struct chunk_begin *p = dlmalloc(sizeof(struct chunk_struct) + size);
|
||||
struct chunk_begin *p = backend_malloc(sizeof(struct chunk_struct) + size);
|
||||
|
||||
QASAN_SWAP(state);
|
||||
|
||||
@ -179,6 +216,12 @@ void __libqasan_free(void *ptr) {
|
||||
|
||||
if (!ptr) return;
|
||||
|
||||
#ifdef __GLIBC__
|
||||
if (ptr >= (void *)__tmp_alloc_zone &&
|
||||
ptr < ((void *)__tmp_alloc_zone + TMP_ZONE_SIZE))
|
||||
return;
|
||||
#endif
|
||||
|
||||
struct chunk_begin *p = ptr;
|
||||
p -= 1;
|
||||
|
||||
@ -190,9 +233,9 @@ void __libqasan_free(void *ptr) {
|
||||
if (!quanratine_push(p)) {
|
||||
|
||||
if (p->aligned_orig)
|
||||
dlfree(p->aligned_orig);
|
||||
backend_free(p->aligned_orig);
|
||||
else
|
||||
dlfree(p);
|
||||
backend_free(p);
|
||||
|
||||
}
|
||||
|
||||
@ -210,6 +253,17 @@ void *__libqasan_calloc(size_t nmemb, size_t size) {
|
||||
|
||||
size *= nmemb;
|
||||
|
||||
#ifdef __GLIBC__
|
||||
if (!__libqasan_malloc_initialized) {
|
||||
|
||||
void *r = &__tmp_alloc_zone[__tmp_alloc_zone_idx];
|
||||
__tmp_alloc_zone_idx += size;
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
char *p = __libqasan_malloc(size);
|
||||
if (!p) return NULL;
|
||||
|
||||
@ -252,7 +306,7 @@ int __libqasan_posix_memalign(void **ptr, size_t align, size_t len) {
|
||||
|
||||
int state = QASAN_SWAP(QASAN_DISABLED); // disable qasan for this thread
|
||||
|
||||
char *orig = dlmalloc(sizeof(struct chunk_struct) + size);
|
||||
char *orig = backend_malloc(sizeof(struct chunk_struct) + size);
|
||||
|
||||
QASAN_SWAP(state);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
|
||||
This code is DEPRECATED!
|
||||
I'm keeping it here cause maybe the unistrumentation of a function is needed
|
||||
I'm keeping it here cause maybe the uninstrumentation of a function is needed
|
||||
for some strange reason.
|
||||
|
||||
*/
|
||||
|
Submodule qemu_mode/qemuafl updated: 6ab6bf28de...d1ca56b84e
@ -785,6 +785,7 @@ static void set_up_environment(void) {
|
||||
"abort_on_error=1:"
|
||||
"detect_leaks=0:"
|
||||
"allocator_may_return_null=1:"
|
||||
"detect_odr_violation=0:"
|
||||
"symbolize=0:"
|
||||
"handle_segv=0:"
|
||||
"handle_sigbus=0:"
|
||||
|
292
src/afl-cc.c
292
src/afl-cc.c
@ -22,7 +22,7 @@
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "alloc-inl.h"
|
||||
#include "llvm-ngram-coverage.h"
|
||||
#include "llvm-alternative-coverage.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
@ -31,8 +31,6 @@
|
||||
#include <strings.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#if (LLVM_MAJOR - 0 == 0)
|
||||
#undef LLVM_MAJOR
|
||||
@ -52,14 +50,13 @@ static u8 **cc_params; /* Parameters passed to the real CC */
|
||||
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
|
||||
static u8 clang_mode; /* Invoked as afl-clang*? */
|
||||
static u8 llvm_fullpath[PATH_MAX];
|
||||
static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode;
|
||||
static u8 instrument_mode, instrument_opt_mode, ngram_size, ctx_k, lto_mode;
|
||||
static u8 compiler_mode, plusplus_mode, have_instr_env = 0;
|
||||
static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto, have_instr_list = 0;
|
||||
static u8 * lto_flag = AFL_CLANG_FLTO, *argvnull;
|
||||
static u8 debug;
|
||||
static u8 cwd[4096];
|
||||
static u8 cmplog_mode;
|
||||
static u8 * ll_file;
|
||||
u8 use_stdin; /* dummy */
|
||||
// static u8 *march_opt = CFLAGS_OPT;
|
||||
|
||||
@ -76,7 +73,9 @@ enum {
|
||||
INSTRUMENT_GCC = 6,
|
||||
INSTRUMENT_CLANG = 7,
|
||||
INSTRUMENT_OPT_CTX = 8,
|
||||
INSTRUMENT_OPT_NGRAM = 16
|
||||
INSTRUMENT_OPT_NGRAM = 16,
|
||||
INSTRUMENT_OPT_CALLER = 32,
|
||||
INSTRUMENT_OPT_CTX_K = 64,
|
||||
|
||||
};
|
||||
|
||||
@ -91,7 +90,7 @@ char instrument_mode_string[18][18] = {
|
||||
"GCC",
|
||||
"CLANG",
|
||||
"CTX",
|
||||
"",
|
||||
"CALLER",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
@ -318,7 +317,6 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0,
|
||||
preprocessor_only = 0, have_unroll = 0, have_o = 0, have_pic = 0,
|
||||
have_c = 0;
|
||||
u32 out_idx = 0;
|
||||
|
||||
cc_params = ck_alloc((argc + 128) * sizeof(u8 *));
|
||||
|
||||
@ -558,6 +556,11 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
#if LLVM_MAJOR >= 13
|
||||
// fuck you llvm 13
|
||||
cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager";
|
||||
#endif
|
||||
|
||||
if (lto_mode && !have_c) {
|
||||
|
||||
u8 *ld_path = strdup(AFL_REAL_LD);
|
||||
@ -745,8 +748,6 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
cc_params[cc_par_cnt++] = cur;
|
||||
|
||||
if (!strcmp(cur, "-o")) { out_idx = cc_par_cnt; }
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_HARDEN")) {
|
||||
@ -807,7 +808,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (!getenv("AFL_DONT_OPTIMIZE") && !getenv("AFL_LLVM_LTO_CALLGRAPH")) {
|
||||
if (!getenv("AFL_DONT_OPTIMIZE")) {
|
||||
|
||||
cc_params[cc_par_cnt++] = "-g";
|
||||
if (!have_o) cc_params[cc_par_cnt++] = "-O3";
|
||||
@ -939,7 +940,10 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (preprocessor_only) {
|
||||
// prevent unnecessary build errors
|
||||
cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument";
|
||||
|
||||
if (preprocessor_only || have_c) {
|
||||
|
||||
/* In the preprocessor_only case (-E), we are not actually compiling at
|
||||
all but requesting the compiler to output preprocessed sources only.
|
||||
@ -953,7 +957,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
#ifndef __ANDROID__
|
||||
|
||||
if (compiler_mode != GCC && compiler_mode != CLANG && !getenv("AFL_LLVM_LTO_CALLGRAPH")) {
|
||||
if (compiler_mode != GCC && compiler_mode != CLANG) {
|
||||
|
||||
switch (bit_mode) {
|
||||
|
||||
@ -1000,39 +1004,19 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
}
|
||||
|
||||
#if !defined(__APPLE__) && !defined(__sun)
|
||||
if (!shared_linking && !have_c)
|
||||
if (!shared_linking)
|
||||
cc_params[cc_par_cnt++] =
|
||||
alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path);
|
||||
#endif
|
||||
|
||||
#if defined(USEMMAP) && !defined(__HAIKU__)
|
||||
if (!have_c) cc_params[cc_par_cnt++] = "-lrt";
|
||||
cc_params[cc_par_cnt++] = "-lrt";
|
||||
#endif
|
||||
|
||||
// prevent unnecessary build errors
|
||||
cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument";
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (getenv("AFL_LLVM_LTO_CALLGRAPH")) {
|
||||
|
||||
if (!lto_mode) { FATAL("AFL_LLVM_LTO_CALLGRAPH requires LTO mode"); }
|
||||
cc_params[cc_par_cnt++] = "-O0";
|
||||
cc_params[cc_par_cnt++] = "-w";
|
||||
|
||||
if (!have_c) {
|
||||
|
||||
ll_file = alloc_printf("%s.ll", cc_params[out_idx]);
|
||||
cc_params[out_idx] = ll_file;
|
||||
cc_params[cc_par_cnt++] = "-S";
|
||||
cc_params[cc_par_cnt++] = "-emit-llvm";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cc_params[cc_par_cnt] = NULL;
|
||||
|
||||
}
|
||||
@ -1041,7 +1025,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
|
||||
|
||||
int main(int argc, char **argv, char **envp) {
|
||||
|
||||
int i;
|
||||
int i, passthrough = 0;
|
||||
char *callname = argv[0], *ptr = NULL;
|
||||
|
||||
if (getenv("AFL_DEBUG")) {
|
||||
@ -1061,6 +1045,13 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) {
|
||||
|
||||
passthrough = 1;
|
||||
if (!debug) { be_quiet = 1; }
|
||||
|
||||
}
|
||||
|
||||
if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1;
|
||||
argvnull = (u8 *)argv[0];
|
||||
check_environment_vars(envp);
|
||||
@ -1291,6 +1282,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
}
|
||||
|
||||
if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX;
|
||||
if (getenv("AFL_LLVM_CALLER")) instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
|
||||
|
||||
if (getenv("AFL_LLVM_NGRAM_SIZE")) {
|
||||
|
||||
@ -1304,6 +1296,26 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_LLVM_CTX_K")) {
|
||||
|
||||
ctx_k = atoi(getenv("AFL_LLVM_CTX_K"));
|
||||
if (ctx_k < 1 || ctx_k > CTX_MAX_K)
|
||||
FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)",
|
||||
CTX_MAX_K);
|
||||
if (ctx_k == 1) {
|
||||
|
||||
setenv("AFL_LLVM_CALLER", "1", 1);
|
||||
unsetenv("AFL_LLVM_CTX_K");
|
||||
instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
|
||||
|
||||
} else {
|
||||
|
||||
instrument_opt_mode |= INSTRUMENT_OPT_CTX_K;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (getenv("AFL_LLVM_INSTRUMENT")) {
|
||||
|
||||
u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;");
|
||||
@ -1399,6 +1411,44 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0) {
|
||||
|
||||
u8 *ptr3 = ptr2 + strlen("ctx-");
|
||||
while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9'))
|
||||
ptr3++;
|
||||
|
||||
if (!*ptr3) {
|
||||
|
||||
if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL)
|
||||
FATAL(
|
||||
"you must set the K-CTX K with (e.g. for value 2) "
|
||||
"AFL_LLVM_INSTRUMENT=ctx-2");
|
||||
|
||||
}
|
||||
|
||||
ctx_k = atoi(ptr3);
|
||||
if (ctx_k < 1 || ctx_k > CTX_MAX_K)
|
||||
FATAL(
|
||||
"K-CTX instrumentation option must be between 1 and CTX_MAX_K "
|
||||
"(%u)",
|
||||
CTX_MAX_K);
|
||||
|
||||
if (ctx_k == 1) {
|
||||
|
||||
instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
|
||||
setenv("AFL_LLVM_CALLER", "1", 1);
|
||||
unsetenv("AFL_LLVM_CTX_K");
|
||||
|
||||
} else {
|
||||
|
||||
instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K);
|
||||
u8 *ptr4 = alloc_printf("%u", ctx_k);
|
||||
setenv("AFL_LLVM_CTX_K", ptr4, 1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr2, "ctx", strlen("ctx")) == 0) {
|
||||
|
||||
instrument_opt_mode |= INSTRUMENT_OPT_CTX;
|
||||
@ -1406,6 +1456,13 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) {
|
||||
|
||||
instrument_opt_mode |= INSTRUMENT_OPT_CALLER;
|
||||
setenv("AFL_LLVM_CALLER", "1", 1);
|
||||
|
||||
}
|
||||
|
||||
if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) {
|
||||
|
||||
u8 *ptr3 = ptr2 + strlen("ngram");
|
||||
@ -1439,6 +1496,35 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
|
||||
(instrument_opt_mode & INSTRUMENT_OPT_CALLER)) {
|
||||
|
||||
FATAL("you cannot set CTX and CALLER together");
|
||||
|
||||
}
|
||||
|
||||
if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) &&
|
||||
(instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) {
|
||||
|
||||
FATAL("you cannot set CTX and K-CTX together");
|
||||
|
||||
}
|
||||
|
||||
if ((instrument_opt_mode & INSTRUMENT_OPT_CALLER) &&
|
||||
(instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) {
|
||||
|
||||
FATAL("you cannot set CALLER and K-CTX together");
|
||||
|
||||
}
|
||||
|
||||
if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT &&
|
||||
(compiler_mode == LLVM || compiler_mode == UNSET)) {
|
||||
|
||||
instrument_mode = INSTRUMENT_CLASSIC;
|
||||
compiler_mode = LLVM;
|
||||
|
||||
}
|
||||
|
||||
if (!compiler_mode) {
|
||||
|
||||
// lto is not a default because outside of afl-cc RANLIB and AR have to
|
||||
@ -1508,12 +1594,13 @@ int main(int argc, char **argv, char **envp) {
|
||||
" CLASSIC %s no yes module yes yes "
|
||||
"yes\n"
|
||||
" - NORMAL\n"
|
||||
" - CALLER\n"
|
||||
" - CTX\n"
|
||||
" - NGRAM-{2-16}\n"
|
||||
" INSTRIM no yes module yes yes "
|
||||
" yes\n"
|
||||
" - NORMAL\n"
|
||||
" - CTX\n"
|
||||
" - CALLER\n"
|
||||
" - NGRAM-{2-16}\n"
|
||||
" [GCC_PLUGIN] gcc plugin: %s%s\n"
|
||||
" CLASSIC DEFAULT no yes no no no "
|
||||
@ -1560,7 +1647,10 @@ int main(int argc, char **argv, char **envp) {
|
||||
NATIVE_MSG
|
||||
|
||||
" CLASSIC: decision target instrumentation (README.llvm.md)\n"
|
||||
" CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n"
|
||||
" CALLER: CLASSIC + single callee context "
|
||||
"(instrumentation/README.ctx.md)\n"
|
||||
" CTX: CLASSIC + full callee context "
|
||||
"(instrumentation/README.ctx.md)\n"
|
||||
" NGRAM-x: CLASSIC + previous path "
|
||||
"((instrumentation/README.ngram.md)\n"
|
||||
" INSTRIM: Dominator tree (for LLVM <= 6.0) "
|
||||
@ -1603,8 +1693,11 @@ int main(int argc, char **argv, char **envp) {
|
||||
" AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n"
|
||||
" AFL_NO_BUILTIN: no builtins for string compare functions (for "
|
||||
"libtokencap.so)\n"
|
||||
" AFL_NOOP: behave like a normal compiler (to pass configure "
|
||||
"tests)\n"
|
||||
" AFL_PATH: path to instrumenting pass and runtime "
|
||||
"(afl-compiler-rt.*o)\n"
|
||||
" AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n"
|
||||
" AFL_INST_RATIO: percentage of branches to instrument\n"
|
||||
" AFL_QUIET: suppress verbose output\n"
|
||||
" AFL_HARDEN: adds code hardening to catch memory bugs\n"
|
||||
@ -1653,15 +1746,17 @@ int main(int argc, char **argv, char **envp) {
|
||||
" AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen "
|
||||
"mutator)\n"
|
||||
" AFL_LLVM_INSTRUMENT: set instrumentation mode:\n"
|
||||
" CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CTX, NGRAM-2 ... "
|
||||
"NGRAM-16\n"
|
||||
" CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CALLER, CTX, "
|
||||
"NGRAM-2 ..-16\n"
|
||||
" You can also use the old environment variables instead:\n"
|
||||
" AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n"
|
||||
" AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n"
|
||||
" AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed "
|
||||
"(option to INSTRIM)\n"
|
||||
" AFL_LLVM_CTX: use context sensitive coverage (for CLASSIC and "
|
||||
"INSTRIM)\n"
|
||||
" AFL_LLVM_CALLER: use single context sensitive coverage (for "
|
||||
"CLASSIC)\n"
|
||||
" AFL_LLVM_CTX: use full context sensitive coverage (for "
|
||||
"CLASSIC)\n"
|
||||
" AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for "
|
||||
"CLASSIC & INSTRIM)\n");
|
||||
|
||||
@ -1678,9 +1773,8 @@ int main(int argc, char **argv, char **envp) {
|
||||
" AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a "
|
||||
"global var\n"
|
||||
" AFL_LLVM_LTO_STARTID: from which ID to start counting from for "
|
||||
"a bb\n"
|
||||
" AFL_LLVM_LTO_CALLGRAPH: will not generate a binary but a "
|
||||
"callgraph instead\n"
|
||||
"a "
|
||||
"bb\n"
|
||||
" AFL_REAL_LD: use this lld linker instead of the compiled in "
|
||||
"path\n"
|
||||
"If anything fails - be sure to read README.lto.md!\n");
|
||||
@ -1717,7 +1811,10 @@ int main(int argc, char **argv, char **envp) {
|
||||
"Do not be overwhelmed :) afl-cc uses good defaults if no options are "
|
||||
"selected.\n"
|
||||
"Read the documentation for FEATURES though, all are good but few are "
|
||||
"defaults.\n\n");
|
||||
"defaults.\n"
|
||||
"Recommended is afl-clang-lto with AFL_LLVM_CMPLOG or afl-clang-fast "
|
||||
"with\n"
|
||||
"AFL_LLVM_CMPLOG and AFL_LLVM_DICT2FILE.\n\n");
|
||||
|
||||
exit(1);
|
||||
|
||||
@ -1777,7 +1874,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
}
|
||||
|
||||
if (instrument_opt_mode && compiler_mode != LLVM)
|
||||
FATAL("CTX and NGRAM can only be used in LLVM mode");
|
||||
FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode");
|
||||
|
||||
if (!instrument_opt_mode) {
|
||||
|
||||
@ -1787,15 +1884,18 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
} else {
|
||||
|
||||
if (instrument_opt_mode == INSTRUMENT_OPT_CTX)
|
||||
char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size);
|
||||
char *ptr3 = alloc_printf(" + K-CTX-%u", ctx_k);
|
||||
|
||||
ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]);
|
||||
else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM)
|
||||
ptr = alloc_printf("%s + NGRAM-%u",
|
||||
instrument_mode_string[instrument_mode], ngram_size);
|
||||
else
|
||||
ptr = alloc_printf("%s + CTX + NGRAM-%u",
|
||||
instrument_mode_string[instrument_mode], ngram_size);
|
||||
ptr = alloc_printf(
|
||||
"%s%s%s%s%s", instrument_mode_string[instrument_mode],
|
||||
(instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "",
|
||||
(instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "",
|
||||
(instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "",
|
||||
(instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : "");
|
||||
|
||||
ck_free(ptr2);
|
||||
ck_free(ptr3);
|
||||
|
||||
}
|
||||
|
||||
@ -1806,11 +1906,14 @@ int main(int argc, char **argv, char **envp) {
|
||||
"(requires LLVM 11 or higher)");
|
||||
#endif
|
||||
|
||||
if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC &&
|
||||
instrument_mode != INSTRUMENT_CFG)
|
||||
if (instrument_opt_mode && instrument_mode == INSTRUMENT_CFG &&
|
||||
instrument_opt_mode & INSTRUMENT_OPT_CTX)
|
||||
FATAL("CFG instrumentation mode supports NGRAM and CALLER, but not CTX.");
|
||||
else if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC)
|
||||
// we will drop CFG/INSTRIM in the future so do not advertise
|
||||
FATAL(
|
||||
"CTX and NGRAM instrumentation options can only be used with CFG "
|
||||
"(recommended) and CLASSIC instrumentation modes!");
|
||||
"CALLER, CTX and NGRAM instrumentation options can only be used with "
|
||||
"the LLVM CLASSIC instrumentation mode.");
|
||||
|
||||
if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO"))
|
||||
FATAL(
|
||||
@ -1904,79 +2007,18 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
s32 pid = 0;
|
||||
if (ll_file) { pid = fork(); } // CG run
|
||||
if (passthrough) {
|
||||
|
||||
if (!pid) {
|
||||
|
||||
execvp(cc_params[0], (char **)cc_params);
|
||||
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
|
||||
|
||||
}
|
||||
|
||||
// only for AFL_LLVM_LTO_CALLGRAPH
|
||||
s32 status;
|
||||
if (waitpid(pid, &status, 0) <= 0) { PFATAL("waitpid() failed"); }
|
||||
|
||||
u8 *cg_file = (u8 *)ck_strdup(ll_file);
|
||||
cg_file[strlen(cg_file) - 2] = 0;
|
||||
strcat(cg_file, "cg");
|
||||
if (!be_quiet) SAYF("Generating callgraph into %s ...\n", cg_file);
|
||||
u8 *opt_cmd = (u8 *)ck_alloc(strlen(cc_params[0]) + 16);
|
||||
strcpy(opt_cmd, cc_params[0]);
|
||||
if ((ptr = (u8 *)strrchr(opt_cmd, '/'))) {
|
||||
|
||||
++ptr;
|
||||
argv[0] = cc_params[0];
|
||||
execvp(cc_params[0], (char **)argv);
|
||||
|
||||
} else {
|
||||
|
||||
ptr = opt_cmd;
|
||||
execvp(cc_params[0], (char **)cc_params);
|
||||
|
||||
}
|
||||
|
||||
u8 *dash = (u8 *)strchr(opt_cmd, '-');
|
||||
strcpy((char *)opt_cmd, "opt");
|
||||
if (dash) {
|
||||
|
||||
dash = (u8 *)strrchr(cc_params[0], '/');
|
||||
dash = (u8 *)strchr(dash, '-');
|
||||
strcat((char *)opt_cmd, (char *)dash);
|
||||
|
||||
}
|
||||
|
||||
u8 *opt_params[8];
|
||||
u32 opt_par_cnt = 0;
|
||||
opt_params[opt_par_cnt++] = opt_cmd;
|
||||
opt_params[opt_par_cnt++] = "-O0";
|
||||
opt_params[opt_par_cnt++] = "--analyze";
|
||||
opt_params[opt_par_cnt++] = "--dot-callgraph";
|
||||
opt_params[opt_par_cnt++] = "-o";
|
||||
opt_params[opt_par_cnt++] = cg_file;
|
||||
opt_params[opt_par_cnt++] = ll_file;
|
||||
opt_params[opt_par_cnt] = NULL;
|
||||
|
||||
if (debug) {
|
||||
|
||||
DEBUGF("cd '%s';", getthecwd());
|
||||
for (i = 0; i < (s32)opt_par_cnt; i++)
|
||||
SAYF(" '%s'", opt_params[i]);
|
||||
SAYF("\n");
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (!pid) {
|
||||
|
||||
execvp(opt_params[0], (char **)opt_params);
|
||||
FATAL("Oops, failed to execute '%s' - check your PATH", opt_params[0]);
|
||||
|
||||
}
|
||||
|
||||
if (waitpid(pid, &status, 0) <= 0) { PFATAL("waitpid() failed"); }
|
||||
if (!be_quiet) SAYF("Done, delete '%s' now.\n", ll_file);
|
||||
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
|
||||
|
||||
return 0;
|
||||
|
||||
|
156
src/afl-common.c
156
src/afl-common.c
@ -47,6 +47,10 @@ u8 be_quiet = 0;
|
||||
u8 *doc_path = "";
|
||||
u8 last_intr = 0;
|
||||
|
||||
#ifndef AFL_PATH
|
||||
#define AFL_PATH "/usr/local/lib/afl/"
|
||||
#endif
|
||||
|
||||
void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin) {
|
||||
|
||||
u32 i = 0;
|
||||
@ -372,11 +376,11 @@ u8 *get_libqasan_path(u8 *own_loc) {
|
||||
|
||||
}
|
||||
|
||||
if (!access(BIN_PATH "/libqasan.so", X_OK)) {
|
||||
if (!access(AFL_PATH "/libqasan.so", X_OK)) {
|
||||
|
||||
if (cp) { ck_free(cp); }
|
||||
|
||||
return ck_strdup(BIN_PATH "/libqasan.so");
|
||||
return ck_strdup(AFL_PATH "/libqasan.so");
|
||||
|
||||
}
|
||||
|
||||
@ -518,12 +522,147 @@ int parse_afl_kill_signal_env(u8 *afl_kill_signal_env, int default_signal) {
|
||||
|
||||
}
|
||||
|
||||
static inline unsigned int helper_min3(unsigned int a, unsigned int b,
|
||||
unsigned int c) {
|
||||
|
||||
return a < b ? (a < c ? a : c) : (b < c ? b : c);
|
||||
|
||||
}
|
||||
|
||||
// from
|
||||
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C
|
||||
static int string_distance_levenshtein(char *s1, char *s2) {
|
||||
|
||||
unsigned int s1len, s2len, x, y, lastdiag, olddiag;
|
||||
s1len = strlen(s1);
|
||||
s2len = strlen(s2);
|
||||
unsigned int column[s1len + 1];
|
||||
column[s1len] = 1;
|
||||
|
||||
for (y = 1; y <= s1len; y++)
|
||||
column[y] = y;
|
||||
for (x = 1; x <= s2len; x++) {
|
||||
|
||||
column[0] = x;
|
||||
for (y = 1, lastdiag = x - 1; y <= s1len; y++) {
|
||||
|
||||
olddiag = column[y];
|
||||
column[y] = helper_min3(column[y] + 1, column[y - 1] + 1,
|
||||
lastdiag + (s1[y - 1] == s2[x - 1] ? 0 : 1));
|
||||
lastdiag = olddiag;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return column[s1len];
|
||||
|
||||
}
|
||||
|
||||
#define ENV_SIMILARITY_TRESHOLD 3
|
||||
|
||||
void print_suggested_envs(char *mispelled_env) {
|
||||
|
||||
size_t env_name_len =
|
||||
strcspn(mispelled_env, "=") - 4; // remove the AFL_prefix
|
||||
char *env_name = ck_alloc(env_name_len + 1);
|
||||
memcpy(env_name, mispelled_env + 4, env_name_len);
|
||||
|
||||
char *seen = ck_alloc(sizeof(afl_environment_variables) / sizeof(char *));
|
||||
int found = 0;
|
||||
|
||||
int j;
|
||||
for (j = 0; afl_environment_variables[j] != NULL; ++j) {
|
||||
|
||||
char *afl_env = afl_environment_variables[j] + 4;
|
||||
int distance = string_distance_levenshtein(afl_env, env_name);
|
||||
if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) {
|
||||
|
||||
SAYF("Did you mean %s?\n", afl_environment_variables[j]);
|
||||
seen[j] = 1;
|
||||
found = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (found) goto cleanup;
|
||||
|
||||
for (j = 0; afl_environment_variables[j] != NULL; ++j) {
|
||||
|
||||
char * afl_env = afl_environment_variables[j] + 4;
|
||||
size_t afl_env_len = strlen(afl_env);
|
||||
char * reduced = ck_alloc(afl_env_len + 1);
|
||||
|
||||
size_t start = 0;
|
||||
while (start < afl_env_len) {
|
||||
|
||||
size_t end = start + strcspn(afl_env + start, "_") + 1;
|
||||
memcpy(reduced, afl_env, start);
|
||||
if (end < afl_env_len)
|
||||
memcpy(reduced + start, afl_env + end, afl_env_len - end);
|
||||
reduced[afl_env_len - end + start] = 0;
|
||||
|
||||
int distance = string_distance_levenshtein(reduced, env_name);
|
||||
if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) {
|
||||
|
||||
SAYF("Did you mean %s?\n", afl_environment_variables[j]);
|
||||
seen[j] = 1;
|
||||
found = 1;
|
||||
|
||||
}
|
||||
|
||||
start = end;
|
||||
|
||||
};
|
||||
|
||||
ck_free(reduced);
|
||||
|
||||
}
|
||||
|
||||
if (found) goto cleanup;
|
||||
|
||||
char * reduced = ck_alloc(env_name_len + 1);
|
||||
size_t start = 0;
|
||||
while (start < env_name_len) {
|
||||
|
||||
size_t end = start + strcspn(env_name + start, "_") + 1;
|
||||
memcpy(reduced, env_name, start);
|
||||
if (end < env_name_len)
|
||||
memcpy(reduced + start, env_name + end, env_name_len - end);
|
||||
reduced[env_name_len - end + start] = 0;
|
||||
|
||||
for (j = 0; afl_environment_variables[j] != NULL; ++j) {
|
||||
|
||||
int distance = string_distance_levenshtein(
|
||||
afl_environment_variables[j] + 4, reduced);
|
||||
if (distance < ENV_SIMILARITY_TRESHOLD && seen[j] == 0) {
|
||||
|
||||
SAYF("Did you mean %s?\n", afl_environment_variables[j]);
|
||||
seen[j] = 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
start = end;
|
||||
|
||||
};
|
||||
|
||||
ck_free(reduced);
|
||||
|
||||
cleanup:
|
||||
ck_free(env_name);
|
||||
ck_free(seen);
|
||||
|
||||
}
|
||||
|
||||
void check_environment_vars(char **envp) {
|
||||
|
||||
if (be_quiet) { return; }
|
||||
|
||||
int index = 0, issue_detected = 0;
|
||||
char *env, *val;
|
||||
char *env, *val, *ignore = getenv("AFL_IGNORE_UNKNOWN_ENVS");
|
||||
while ((env = envp[index++]) != NULL) {
|
||||
|
||||
if (strncmp(env, "ALF_", 4) == 0 || strncmp(env, "_ALF", 4) == 0 ||
|
||||
@ -543,6 +682,7 @@ void check_environment_vars(char **envp) {
|
||||
env[strlen(afl_environment_variables[i])] == '=') {
|
||||
|
||||
match = 1;
|
||||
|
||||
if ((val = getenv(afl_environment_variables[i])) && !*val) {
|
||||
|
||||
WARNF(
|
||||
@ -582,11 +722,13 @@ void check_environment_vars(char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (match == 0) {
|
||||
if (match == 0 && !ignore) {
|
||||
|
||||
WARNF("Mistyped AFL environment variable: %s", env);
|
||||
issue_detected = 1;
|
||||
|
||||
print_suggested_envs(env);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -981,7 +1123,7 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms) {
|
||||
/* Reads the map size from ENV */
|
||||
u32 get_map_size(void) {
|
||||
|
||||
uint32_t map_size = (MAP_SIZE << 2); // needed for target ctors :(
|
||||
uint32_t map_size = 8000000; // a very large default map
|
||||
char * ptr;
|
||||
|
||||
if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) {
|
||||
@ -989,12 +1131,12 @@ u32 get_map_size(void) {
|
||||
map_size = atoi(ptr);
|
||||
if (!map_size || map_size > (1 << 29)) {
|
||||
|
||||
FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 32U,
|
||||
FATAL("illegal AFL_MAP_SIZE %u, must be between %u and %u", map_size, 64U,
|
||||
1U << 29);
|
||||
|
||||
}
|
||||
|
||||
if (map_size % 32) { map_size = (((map_size >> 5) + 1) << 5); }
|
||||
if (map_size % 64) { map_size = (((map_size >> 6) + 1) << 6); }
|
||||
|
||||
}
|
||||
|
||||
|
@ -481,27 +481,28 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
/* 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); }
|
||||
if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 1); }
|
||||
|
||||
/* Set sane defaults for ASAN if nothing else specified. */
|
||||
|
||||
if (fsrv->debug == true && !getenv("ASAN_OPTIONS"))
|
||||
if (!getenv("ASAN_OPTIONS"))
|
||||
setenv("ASAN_OPTIONS",
|
||||
"abort_on_error=1:"
|
||||
"detect_leaks=0:"
|
||||
"malloc_context_size=0:"
|
||||
"symbolize=0:"
|
||||
"allocator_may_return_null=1:"
|
||||
"detect_odr_violation=0:"
|
||||
"handle_segv=0:"
|
||||
"handle_sigbus=0:"
|
||||
"handle_abort=0:"
|
||||
"handle_sigfpe=0:"
|
||||
"handle_sigill=0",
|
||||
0);
|
||||
1);
|
||||
|
||||
/* Set sane defaults for UBSAN if nothing else specified. */
|
||||
|
||||
if (fsrv->debug == true && !getenv("UBSAN_OPTIONS"))
|
||||
if (!getenv("UBSAN_OPTIONS"))
|
||||
setenv("UBSAN_OPTIONS",
|
||||
"halt_on_error=1:"
|
||||
"abort_on_error=1:"
|
||||
@ -513,7 +514,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
"handle_abort=0:"
|
||||
"handle_sigfpe=0:"
|
||||
"handle_sigill=0",
|
||||
0);
|
||||
1);
|
||||
|
||||
/* Envs for QASan */
|
||||
setenv("QASAN_MAX_CALL_STACK", "0", 0);
|
||||
@ -522,7 +523,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
/* MSAN is tricky, because it doesn't support abort_on_error=1 at this
|
||||
point. So, we do this in a very hacky way. */
|
||||
|
||||
if (fsrv->debug == true && !getenv("MSAN_OPTIONS"))
|
||||
if (!getenv("MSAN_OPTIONS"))
|
||||
setenv("MSAN_OPTIONS",
|
||||
"exit_code=" STRINGIFY(MSAN_ERROR) ":"
|
||||
"symbolize=0:"
|
||||
@ -535,7 +536,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
"handle_abort=0:"
|
||||
"handle_sigfpe=0:"
|
||||
"handle_sigill=0",
|
||||
0);
|
||||
1);
|
||||
|
||||
fsrv->init_child_func(fsrv, argv);
|
||||
|
||||
@ -656,11 +657,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; }
|
||||
|
||||
if (unlikely(tmp_map_size % 32)) {
|
||||
if (unlikely(tmp_map_size % 64)) {
|
||||
|
||||
// should not happen
|
||||
WARNF("Target reported non-aligned map size of %u", tmp_map_size);
|
||||
tmp_map_size = (((tmp_map_size + 31) >> 5) << 5);
|
||||
tmp_map_size = (((tmp_map_size + 63) >> 6) << 6);
|
||||
|
||||
}
|
||||
|
||||
@ -820,7 +821,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
" - The target binary requires a large map and crashes before "
|
||||
"reporting.\n"
|
||||
" Set a high value (e.g. AFL_MAP_SIZE=1024000) or use "
|
||||
" Set a high value (e.g. AFL_MAP_SIZE=8000000) or use "
|
||||
"AFL_DEBUG=1 to see the\n"
|
||||
" message from the target binary\n\n"
|
||||
|
||||
@ -847,7 +848,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
|
||||
" - The target binary requires a large map and crashes before "
|
||||
"reporting.\n"
|
||||
" Set a high value (e.g. AFL_MAP_SIZE=1024000) or use "
|
||||
" Set a high value (e.g. AFL_MAP_SIZE=8000000) or use "
|
||||
"AFL_DEBUG=1 to see the\n"
|
||||
" message from the target binary\n\n"
|
||||
|
||||
@ -908,10 +909,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
} else if (!fsrv->mem_limit) {
|
||||
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
"Hmm, looks like the target binary terminated before we could"
|
||||
" complete a handshake with the injected code.\n"
|
||||
"If the target was compiled with afl-clang-lto and AFL_LLVM_MAP_ADDR"
|
||||
" then recompiling without this parameter.\n"
|
||||
"Hmm, looks like the target binary terminated before we could complete"
|
||||
" a\n"
|
||||
"handshake with the injected code.\n"
|
||||
"Most likely the target has a huge coverage map, retry with setting"
|
||||
" the\n"
|
||||
"environment variable AFL_MAP_SIZE=8000000\n"
|
||||
"Otherwise there is a horrible bug in the fuzzer.\n"
|
||||
"Poke <afl-users@googlegroups.com> for troubleshooting tips.\n");
|
||||
|
||||
@ -927,6 +930,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
|
||||
"explanations:\n\n"
|
||||
|
||||
"%s"
|
||||
|
||||
" - Most likely the target has a huge coverage map, retry with "
|
||||
"setting the\n"
|
||||
" environment variable AFL_MAP_SIZE=8000000\n\n"
|
||||
|
||||
" - The current memory limit (%s) is too restrictive, causing an "
|
||||
"OOM\n"
|
||||
" fault in the dynamic linker. This can be fixed with the -m "
|
||||
|
@ -387,6 +387,130 @@ static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) {
|
||||
|
||||
}
|
||||
|
||||
/* add an extra/dict/token - no checks performed, no sorting */
|
||||
|
||||
static void add_extra_nocheck(afl_state_t *afl, u8 *mem, u32 len) {
|
||||
|
||||
afl->extras = afl_realloc((void **)&afl->extras,
|
||||
(afl->extras_cnt + 1) * sizeof(struct extra_data));
|
||||
|
||||
if (unlikely(!afl->extras)) { PFATAL("alloc"); }
|
||||
|
||||
afl->extras[afl->extras_cnt].data = ck_alloc(len);
|
||||
afl->extras[afl->extras_cnt].len = len;
|
||||
memcpy(afl->extras[afl->extras_cnt].data, mem, len);
|
||||
afl->extras_cnt++;
|
||||
|
||||
/* We only want to print this once */
|
||||
|
||||
if (afl->extras_cnt == afl->max_det_extras + 1) {
|
||||
|
||||
WARNF("More than %u tokens - will use them probabilistically.",
|
||||
afl->max_det_extras);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Sometimes strings in input is transformed to unicode internally, so for
|
||||
fuzzing we should attempt to de-unicode if it looks like simple unicode */
|
||||
|
||||
void deunicode_extras(afl_state_t *afl) {
|
||||
|
||||
if (!afl->extras_cnt) return;
|
||||
|
||||
u32 i, j, orig_cnt = afl->extras_cnt;
|
||||
u8 buf[64];
|
||||
|
||||
for (i = 0; i < orig_cnt; ++i) {
|
||||
|
||||
if (afl->extras[i].len < 6 || afl->extras[i].len > 64 ||
|
||||
afl->extras[i].len % 2) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
u32 k = 0, z1 = 0, z2 = 0, z3 = 0, z4 = 0, half = afl->extras[i].len >> 1;
|
||||
u32 quarter = half >> 1;
|
||||
|
||||
for (j = 0; j < afl->extras[i].len; ++j) {
|
||||
|
||||
switch (j % 4) {
|
||||
|
||||
case 2:
|
||||
if (!afl->extras[i].data[j]) { ++z3; }
|
||||
// fall through
|
||||
case 0:
|
||||
if (!afl->extras[i].data[j]) { ++z1; }
|
||||
break;
|
||||
case 3:
|
||||
if (!afl->extras[i].data[j]) { ++z4; }
|
||||
// fall through
|
||||
case 1:
|
||||
if (!afl->extras[i].data[j]) { ++z2; }
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ((z1 < half && z2 < half) || z1 + z2 == afl->extras[i].len) { continue; }
|
||||
|
||||
// also maybe 32 bit unicode?
|
||||
if (afl->extras[i].len % 4 == 0 && afl->extras[i].len >= 12 &&
|
||||
(z3 == quarter || z4 == quarter) && z1 + z2 == quarter * 3) {
|
||||
|
||||
for (j = 0; j < afl->extras[i].len; ++j) {
|
||||
|
||||
if (z4 < quarter) {
|
||||
|
||||
if (j % 4 == 3) { buf[k++] = afl->extras[i].data[j]; }
|
||||
|
||||
} else if (z3 < quarter) {
|
||||
|
||||
if (j % 4 == 2) { buf[k++] = afl->extras[i].data[j]; }
|
||||
|
||||
} else if (z2 < half) {
|
||||
|
||||
if (j % 4 == 1) { buf[k++] = afl->extras[i].data[j]; }
|
||||
|
||||
} else {
|
||||
|
||||
if (j % 4 == 0) { buf[k++] = afl->extras[i].data[j]; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
add_extra_nocheck(afl, buf, k);
|
||||
k = 0;
|
||||
|
||||
}
|
||||
|
||||
for (j = 0; j < afl->extras[i].len; ++j) {
|
||||
|
||||
if (z1 < half) {
|
||||
|
||||
if (j % 2 == 0) { buf[k++] = afl->extras[i].data[j]; }
|
||||
|
||||
} else {
|
||||
|
||||
if (j % 2 == 1) { buf[k++] = afl->extras[i].data[j]; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
add_extra_nocheck(afl, buf, k);
|
||||
|
||||
}
|
||||
|
||||
qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data),
|
||||
compare_extras_len);
|
||||
|
||||
}
|
||||
|
||||
/* Removes duplicates from the loaded extras. This can happen if multiple files
|
||||
are loaded */
|
||||
|
||||
@ -396,9 +520,9 @@ void dedup_extras(afl_state_t *afl) {
|
||||
|
||||
u32 i, j, orig_cnt = afl->extras_cnt;
|
||||
|
||||
for (i = 0; i < afl->extras_cnt - 1; i++) {
|
||||
for (i = 0; i < afl->extras_cnt - 1; ++i) {
|
||||
|
||||
for (j = i + 1; j < afl->extras_cnt; j++) {
|
||||
for (j = i + 1; j < afl->extras_cnt; ++j) {
|
||||
|
||||
restart_dedup:
|
||||
|
||||
@ -462,30 +586,11 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) {
|
||||
|
||||
}
|
||||
|
||||
afl->extras = afl_realloc((void **)&afl->extras,
|
||||
(afl->extras_cnt + 1) * sizeof(struct extra_data));
|
||||
|
||||
if (unlikely(!afl->extras)) { PFATAL("alloc"); }
|
||||
|
||||
afl->extras[afl->extras_cnt].data = ck_alloc(len);
|
||||
afl->extras[afl->extras_cnt].len = len;
|
||||
|
||||
memcpy(afl->extras[afl->extras_cnt].data, mem, len);
|
||||
|
||||
afl->extras_cnt++;
|
||||
add_extra_nocheck(afl, mem, len);
|
||||
|
||||
qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data),
|
||||
compare_extras_len);
|
||||
|
||||
/* We only want to print this once */
|
||||
|
||||
if (afl->extras_cnt == afl->max_det_extras + 1) {
|
||||
|
||||
WARNF("More than %u tokens - will use them probabilistically.",
|
||||
afl->max_det_extras);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Maybe add automatic extra. */
|
||||
|
@ -828,7 +828,7 @@ void perform_dry_run(afl_state_t *afl) {
|
||||
for (idx = 0; idx < afl->queued_paths; idx++) {
|
||||
|
||||
q = afl->queue_buf[idx];
|
||||
if (unlikely(q->disabled)) { continue; }
|
||||
if (unlikely(!q || q->disabled)) { continue; }
|
||||
|
||||
u8 res;
|
||||
s32 fd;
|
||||
@ -882,32 +882,23 @@ void perform_dry_run(afl_state_t *afl) {
|
||||
|
||||
if (afl->timeout_given) {
|
||||
|
||||
/* The -t nn+ syntax in the command line sets afl->timeout_given to
|
||||
'2' and instructs afl-fuzz to tolerate but skip queue entries that
|
||||
time out. */
|
||||
/* if we have a timeout but a timeout value was given then always
|
||||
skip. The '+' meaning has been changed! */
|
||||
WARNF("Test case results in a timeout (skipping)");
|
||||
++cal_failures;
|
||||
q->cal_failed = CAL_CHANCES;
|
||||
q->disabled = 1;
|
||||
q->perf_score = 0;
|
||||
|
||||
if (afl->timeout_given > 1) {
|
||||
if (!q->was_fuzzed) {
|
||||
|
||||
WARNF("Test case results in a timeout (skipping)");
|
||||
q->cal_failed = CAL_CHANCES;
|
||||
++cal_failures;
|
||||
break;
|
||||
q->was_fuzzed = 1;
|
||||
--afl->pending_not_fuzzed;
|
||||
--afl->active_paths;
|
||||
|
||||
}
|
||||
|
||||
SAYF("\n" cLRD "[-] " cRST
|
||||
"The program took more than %u ms to process one of the initial "
|
||||
"test cases.\n"
|
||||
" Usually, the right thing to do is to relax the -t option - "
|
||||
"or to delete it\n"
|
||||
" altogether and allow the fuzzer to auto-calibrate. That "
|
||||
"said, if you know\n"
|
||||
" what you are doing and want to simply skip the unruly test "
|
||||
"cases, append\n"
|
||||
" '+' at the end of the value passed to -t ('-t %u+').\n",
|
||||
afl->fsrv.exec_tmout, afl->fsrv.exec_tmout);
|
||||
|
||||
FATAL("Test case '%s' results in a timeout", fn);
|
||||
break;
|
||||
|
||||
} else {
|
||||
|
||||
@ -1060,16 +1051,25 @@ void perform_dry_run(afl_state_t *afl) {
|
||||
p->perf_score = 0;
|
||||
|
||||
u32 i = 0;
|
||||
while (unlikely(afl->queue_buf[i]->disabled)) {
|
||||
while (unlikely(i < afl->queued_paths && afl->queue_buf[i] &&
|
||||
afl->queue_buf[i]->disabled)) {
|
||||
|
||||
++i;
|
||||
|
||||
}
|
||||
|
||||
afl->queue = afl->queue_buf[i];
|
||||
if (i < afl->queued_paths && afl->queue_buf[i]) {
|
||||
|
||||
afl->queue = afl->queue_buf[i];
|
||||
|
||||
} else {
|
||||
|
||||
afl->queue = afl->queue_buf[0];
|
||||
|
||||
}
|
||||
|
||||
afl->max_depth = 0;
|
||||
for (i = 0; i < afl->queued_paths; i++) {
|
||||
for (i = 0; i < afl->queued_paths && likely(afl->queue_buf[i]); i++) {
|
||||
|
||||
if (!afl->queue_buf[i]->disabled &&
|
||||
afl->queue_buf[i]->depth > afl->max_depth)
|
||||
@ -1136,10 +1136,11 @@ void perform_dry_run(afl_state_t *afl) {
|
||||
for (idx = 0; idx < afl->queued_paths; idx++) {
|
||||
|
||||
q = afl->queue_buf[idx];
|
||||
if (q->disabled || q->cal_failed || !q->exec_cksum) { continue; }
|
||||
if (!q || q->disabled || q->cal_failed || !q->exec_cksum) { continue; }
|
||||
|
||||
u32 done = 0;
|
||||
for (i = idx + 1; i < afl->queued_paths && !done; i++) {
|
||||
for (i = idx + 1;
|
||||
i < afl->queued_paths && !done && likely(afl->queue_buf[i]); i++) {
|
||||
|
||||
struct queue_entry *p = afl->queue_buf[i];
|
||||
if (p->disabled || p->cal_failed || !p->exec_cksum) { continue; }
|
||||
@ -1191,7 +1192,7 @@ void perform_dry_run(afl_state_t *afl) {
|
||||
|
||||
for (idx = 0; idx < afl->queued_paths; idx++) {
|
||||
|
||||
if (!afl->queue_buf[idx]->disabled &&
|
||||
if (afl->queue_buf[idx] && !afl->queue_buf[idx]->disabled &&
|
||||
afl->queue_buf[idx]->depth > afl->max_depth)
|
||||
afl->max_depth = afl->queue_buf[idx]->depth;
|
||||
|
||||
@ -1247,7 +1248,7 @@ void pivot_inputs(afl_state_t *afl) {
|
||||
|
||||
ACTF("Creating hard links for all input files...");
|
||||
|
||||
for (i = 0; i < afl->queued_paths; i++) {
|
||||
for (i = 0; i < afl->queued_paths && likely(afl->queue_buf[i]); i++) {
|
||||
|
||||
q = afl->queue_buf[i];
|
||||
|
||||
@ -2017,7 +2018,7 @@ void setup_dirs_fds(afl_state_t *afl) {
|
||||
fprintf(afl->fsrv.plot_file,
|
||||
"# unix_time, cycles_done, cur_path, paths_total, "
|
||||
"pending_total, pending_favs, map_size, unique_crashes, "
|
||||
"unique_hangs, max_depth, execs_per_sec\n");
|
||||
"unique_hangs, max_depth, execs_per_sec, total_execs, edges_found\n");
|
||||
fflush(afl->fsrv.plot_file);
|
||||
|
||||
/* ignore errors */
|
||||
@ -2457,7 +2458,7 @@ void check_asan_opts(afl_state_t *afl) {
|
||||
|
||||
}
|
||||
|
||||
if (!strstr(x, "symbolize=0")) {
|
||||
if (!afl->debug && !strstr(x, "symbolize=0")) {
|
||||
|
||||
FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");
|
||||
|
||||
|
@ -198,34 +198,35 @@ void create_alias_table(afl_state_t *afl) {
|
||||
while (nS)
|
||||
afl->alias_probability[S[--nS]] = 1;
|
||||
|
||||
#ifdef INTROSPECTION
|
||||
u8 fn[PATH_MAX];
|
||||
snprintf(fn, PATH_MAX, "%s/introspection_corpus.txt", afl->out_dir);
|
||||
FILE *f = fopen(fn, "a");
|
||||
if (f) {
|
||||
/*
|
||||
#ifdef INTROSPECTION
|
||||
u8 fn[PATH_MAX];
|
||||
snprintf(fn, PATH_MAX, "%s/introspection_corpus.txt", afl->out_dir);
|
||||
FILE *f = fopen(fn, "a");
|
||||
if (f) {
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
for (i = 0; i < n; i++) {
|
||||
|
||||
struct queue_entry *q = afl->queue_buf[i];
|
||||
fprintf(
|
||||
f,
|
||||
"entry=%u name=%s favored=%s variable=%s disabled=%s len=%u "
|
||||
"exec_us=%u "
|
||||
"bitmap_size=%u bitsmap_size=%u tops=%u weight=%f perf_score=%f\n",
|
||||
i, q->fname, q->favored ? "true" : "false",
|
||||
q->var_behavior ? "true" : "false", q->disabled ? "true" : "false",
|
||||
q->len, (u32)q->exec_us, q->bitmap_size, q->bitsmap_size, q->tc_ref,
|
||||
q->weight, q->perf_score);
|
||||
struct queue_entry *q = afl->queue_buf[i];
|
||||
fprintf(
|
||||
f,
|
||||
"entry=%u name=%s favored=%s variable=%s disabled=%s len=%u "
|
||||
"exec_us=%u "
|
||||
"bitmap_size=%u bitsmap_size=%u tops=%u weight=%f perf_score=%f\n",
|
||||
i, q->fname, q->favored ? "true" : "false",
|
||||
q->var_behavior ? "true" : "false", q->disabled ? "true" : "false",
|
||||
q->len, (u32)q->exec_us, q->bitmap_size, q->bitsmap_size, q->tc_ref,
|
||||
q->weight, q->perf_score);
|
||||
|
||||
}
|
||||
|
||||
fprintf(f, "\n");
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
|
||||
fprintf(f, "\n");
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
*/
|
||||
/*
|
||||
fprintf(stderr, " entry alias probability perf_score weight
|
||||
filename\n"); for (u32 i = 0; i < n; ++i) fprintf(stderr, " %5u %5u %11u
|
||||
@ -324,7 +325,7 @@ static u8 check_if_text(afl_state_t *afl, struct queue_entry *q) {
|
||||
|
||||
if (len >= MAX_FILE) len = MAX_FILE - 1;
|
||||
if ((fd = open(q->fname, O_RDONLY)) < 0) return 0;
|
||||
buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len);
|
||||
buf = afl_realloc(AFL_BUF_PARAM(in_scratch), len + 1);
|
||||
comp = read(fd, buf, len);
|
||||
close(fd);
|
||||
if (comp != (ssize_t)len) return 0;
|
||||
|
@ -30,8 +30,6 @@
|
||||
|
||||
//#define _DEBUG
|
||||
//#define CMPLOG_INTROSPECTION
|
||||
#define COMBINE
|
||||
#define ARITHMETIC_LESSER_GREATER
|
||||
|
||||
// CMP attribute enum
|
||||
enum {
|
||||
@ -206,14 +204,31 @@ static void type_replace(afl_state_t *afl, u8 *buf, u32 len) {
|
||||
case '\t':
|
||||
c = ' ';
|
||||
break;
|
||||
/*
|
||||
case '\r':
|
||||
case '\n':
|
||||
// nothing ...
|
||||
break;
|
||||
*/
|
||||
case '\r':
|
||||
c = '\n';
|
||||
break;
|
||||
case '\n':
|
||||
c = '\r';
|
||||
break;
|
||||
case 0:
|
||||
c = 1;
|
||||
break;
|
||||
case 1:
|
||||
c = 0;
|
||||
break;
|
||||
case 0xff:
|
||||
c = 0;
|
||||
break;
|
||||
default:
|
||||
c = (buf[i] ^ 0xff);
|
||||
if (buf[i] < 32) {
|
||||
|
||||
c = (buf[i] ^ 0x1f);
|
||||
|
||||
} else {
|
||||
|
||||
c = (buf[i] ^ 0x7f); // we keep the highest bit
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -383,6 +398,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len,
|
||||
rng = ranges;
|
||||
ranges = rng->next;
|
||||
ck_free(rng);
|
||||
rng = NULL;
|
||||
|
||||
}
|
||||
|
||||
@ -421,8 +437,9 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len,
|
||||
|
||||
if (taint) {
|
||||
|
||||
if (len / positions == 1 && positions > CMPLOG_POSITIONS_MAX &&
|
||||
afl->active_paths / afl->colorize_success > CMPLOG_CORPUS_PERCENT) {
|
||||
if (afl->colorize_success &&
|
||||
(len / positions == 1 && positions > CMPLOG_POSITIONS_MAX &&
|
||||
afl->active_paths / afl->colorize_success > CMPLOG_CORPUS_PERCENT)) {
|
||||
|
||||
#ifdef _DEBUG
|
||||
fprintf(stderr, "Colorization unsatisfactory\n");
|
||||
@ -456,6 +473,15 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len,
|
||||
return 0;
|
||||
|
||||
checksum_fail:
|
||||
while (ranges) {
|
||||
|
||||
rng = ranges;
|
||||
ranges = rng->next;
|
||||
ck_free(rng);
|
||||
rng = NULL;
|
||||
|
||||
}
|
||||
|
||||
ck_free(backup);
|
||||
ck_free(changed);
|
||||
|
||||
@ -496,7 +522,7 @@ static u8 its_fuzz(afl_state_t *afl, u8 *buf, u32 len, u8 *status) {
|
||||
|
||||
}
|
||||
|
||||
#ifdef CMPLOG_TRANSFORM
|
||||
//#ifdef CMPLOG_SOLVE_TRANSFORM
|
||||
static int strntoll(const char *str, size_t sz, char **end, int base,
|
||||
long long *out) {
|
||||
|
||||
@ -504,6 +530,8 @@ static int strntoll(const char *str, size_t sz, char **end, int base,
|
||||
long long ret;
|
||||
const char *beg = str;
|
||||
|
||||
if (!str || !sz) { return 1; }
|
||||
|
||||
for (; beg && sz && *beg == ' '; beg++, sz--) {};
|
||||
|
||||
if (!sz) return 1;
|
||||
@ -527,6 +555,8 @@ static int strntoull(const char *str, size_t sz, char **end, int base,
|
||||
unsigned long long ret;
|
||||
const char * beg = str;
|
||||
|
||||
if (!str || !sz) { return 1; }
|
||||
|
||||
for (; beg && sz && *beg == ' '; beg++, sz--)
|
||||
;
|
||||
|
||||
@ -577,7 +607,7 @@ static int is_hex(const char *str) {
|
||||
|
||||
}
|
||||
|
||||
#ifdef CMPLOG_TRANSFORM_BASE64
|
||||
#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
|
||||
// tests 4 bytes at location
|
||||
static int is_base64(const char *str) {
|
||||
|
||||
@ -690,10 +720,10 @@ static void to_base64(u8 *src, u8 *dst, u32 dst_len) {
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//#endif
|
||||
|
||||
static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
u64 pattern, u64 repl, u64 o_pattern,
|
||||
u64 changed_val, u8 attr, u32 idx, u32 taint_len,
|
||||
@ -717,9 +747,9 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
// o_pattern, pattern, repl, changed_val, idx, taint_len,
|
||||
// h->shape + 1, attr);
|
||||
|
||||
#ifdef CMPLOG_TRANSFORM
|
||||
//#ifdef CMPLOG_SOLVE_TRANSFORM
|
||||
// reverse atoi()/strnu?toll() is expensive, so we only to it in lvl 3
|
||||
if (lvl & LVL3) {
|
||||
if (afl->cmplog_enable_transform && (lvl & LVL3)) {
|
||||
|
||||
u8 * endptr;
|
||||
u8 use_num = 0, use_unum = 0;
|
||||
@ -740,11 +770,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
#ifdef _DEBUG
|
||||
if (idx == 0)
|
||||
fprintf(stderr, "ASCII is=%u use_num=%u use_unum=%u idx=%u %llx==%llx\n",
|
||||
afl->queue_cur->is_ascii, use_num, use_unum, idx, num, pattern);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// num is likely not pattern as atoi("AAA") will be zero...
|
||||
if (use_num && ((u64)num == pattern || !num)) {
|
||||
@ -794,37 +824,82 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
// Try to identify transform magic
|
||||
if (pattern != o_pattern && repl == changed_val && attr <= IS_EQUAL) {
|
||||
|
||||
u64 *ptr = (u64 *)&buf[idx];
|
||||
u64 *o_ptr = (u64 *)&orig_buf[idx];
|
||||
u64 b_val, o_b_val, mask;
|
||||
u64 b_val, o_b_val, mask;
|
||||
u8 bytes;
|
||||
|
||||
switch (SHAPE_BYTES(h->shape)) {
|
||||
|
||||
case 0:
|
||||
case 1:
|
||||
b_val = (u64)(*ptr % 0x100);
|
||||
bytes = 1;
|
||||
break;
|
||||
case 2:
|
||||
bytes = 2;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
bytes = 4;
|
||||
break;
|
||||
default:
|
||||
bytes = 8;
|
||||
|
||||
}
|
||||
|
||||
// necessary for preventing heap access overflow
|
||||
bytes = MIN(bytes, len - idx);
|
||||
|
||||
switch (bytes) {
|
||||
|
||||
case 0: // cannot happen
|
||||
b_val = o_b_val = mask = 0; // keep the linters happy
|
||||
break;
|
||||
case 1: {
|
||||
|
||||
u8 *ptr = (u8 *)&buf[idx];
|
||||
u8 *o_ptr = (u8 *)&orig_buf[idx];
|
||||
b_val = (u64)(*ptr);
|
||||
o_b_val = (u64)(*o_ptr % 0x100);
|
||||
mask = 0xff;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
b_val = (u64)(*ptr % 0x10000);
|
||||
o_b_val = (u64)(*o_ptr % 0x10000);
|
||||
case 3: {
|
||||
|
||||
u16 *ptr = (u16 *)&buf[idx];
|
||||
u16 *o_ptr = (u16 *)&orig_buf[idx];
|
||||
b_val = (u64)(*ptr);
|
||||
o_b_val = (u64)(*o_ptr);
|
||||
mask = 0xffff;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
b_val = (u64)(*ptr % 0x100000000);
|
||||
o_b_val = (u64)(*o_ptr % 0x100000000);
|
||||
case 7: {
|
||||
|
||||
u32 *ptr = (u32 *)&buf[idx];
|
||||
u32 *o_ptr = (u32 *)&orig_buf[idx];
|
||||
b_val = (u64)(*ptr);
|
||||
o_b_val = (u64)(*o_ptr);
|
||||
mask = 0xffffffff;
|
||||
break;
|
||||
default:
|
||||
b_val = *ptr;
|
||||
o_b_val = *o_ptr;
|
||||
|
||||
}
|
||||
|
||||
default: {
|
||||
|
||||
u64 *ptr = (u64 *)&buf[idx];
|
||||
u64 *o_ptr = (u64 *)&orig_buf[idx];
|
||||
b_val = (u64)(*ptr);
|
||||
o_b_val = (u64)(*o_ptr);
|
||||
mask = 0xffffffffffffffff;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// test for arithmetic, eg. "if ((user_val - 0x1111) == 0x1234) ..."
|
||||
@ -984,7 +1059,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
// we only allow this for ascii2integer (above) so leave if this is the case
|
||||
if (unlikely(pattern == o_pattern)) { return 0; }
|
||||
@ -1009,7 +1084,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
u64 tmp_64 = *buf_64;
|
||||
*buf_64 = repl;
|
||||
if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
|
||||
#ifdef COMBINE
|
||||
#ifdef CMPLOG_COMBINE
|
||||
if (*status == 1) { memcpy(cbuf + idx, buf_64, 8); }
|
||||
#endif
|
||||
*buf_64 = tmp_64;
|
||||
@ -1050,7 +1125,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
u32 tmp_32 = *buf_32;
|
||||
*buf_32 = (u32)repl;
|
||||
if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
|
||||
#ifdef COMBINE
|
||||
#ifdef CMPLOG_COMBINE
|
||||
if (*status == 1) { memcpy(cbuf + idx, buf_32, 4); }
|
||||
#endif
|
||||
*buf_32 = tmp_32;
|
||||
@ -1084,7 +1159,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
u16 tmp_16 = *buf_16;
|
||||
*buf_16 = (u16)repl;
|
||||
if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
|
||||
#ifdef COMBINE
|
||||
#ifdef CMPLOG_COMBINE
|
||||
if (*status == 1) { memcpy(cbuf + idx, buf_16, 2); }
|
||||
#endif
|
||||
*buf_16 = tmp_16;
|
||||
@ -1122,7 +1197,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
u8 tmp_8 = *buf_8;
|
||||
*buf_8 = (u8)repl;
|
||||
if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
|
||||
#ifdef COMBINE
|
||||
#ifdef CMPLOG_COMBINE
|
||||
if (*status == 1) { cbuf[idx] = *buf_8; }
|
||||
#endif
|
||||
*buf_8 = tmp_8;
|
||||
@ -1139,8 +1214,12 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
// 16 = modified float, 32 = modified integer (modified = wont match
|
||||
// in original buffer)
|
||||
|
||||
#ifdef ARITHMETIC_LESSER_GREATER
|
||||
if (lvl < LVL3 || attr == IS_TRANSFORM) { return 0; }
|
||||
//#ifdef CMPLOG_SOLVE_ARITHMETIC
|
||||
if (!afl->cmplog_enable_arith || lvl < LVL3 || attr == IS_TRANSFORM) {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
if (!(attr & (IS_GREATER | IS_LESSER)) || SHAPE_BYTES(h->shape) < 4) {
|
||||
|
||||
@ -1245,11 +1324,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
double *f = (double *)&repl;
|
||||
float g = (float)*f;
|
||||
repl_new = 0;
|
||||
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
memcpy((char *)&repl_new, (char *)&g, 4);
|
||||
#else
|
||||
#else
|
||||
memcpy(((char *)&repl_new) + 4, (char *)&g, 4);
|
||||
#endif
|
||||
#endif
|
||||
changed_val = repl_new;
|
||||
h->shape = 3; // modify shape
|
||||
|
||||
@ -1304,7 +1383,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h,
|
||||
|
||||
}
|
||||
|
||||
#endif /* ARITHMETIC_LESSER_GREATER */
|
||||
//#endif /* CMPLOG_SOLVE_ARITHMETIC
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1366,7 +1445,7 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h,
|
||||
|
||||
if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
|
||||
|
||||
#ifdef COMBINE
|
||||
#ifdef CMPLOG_COMBINE
|
||||
if (*status == 1) { memcpy(cbuf + idx, r, shape); }
|
||||
#endif
|
||||
|
||||
@ -1463,7 +1542,7 @@ static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) {
|
||||
for (k = 0; k < size; ++k) {
|
||||
|
||||
#else
|
||||
u32 off = 16 - size;
|
||||
u32 off = 16 - size;
|
||||
for (k = 16 - size; k < 16; ++k) {
|
||||
|
||||
#endif
|
||||
@ -1499,11 +1578,12 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
|
||||
struct cmp_header *h = &afl->shm.cmp_map->headers[key];
|
||||
struct tainted * t;
|
||||
u32 i, j, idx, taint_len, loggeds;
|
||||
u32 have_taint = 1, is_n = 0;
|
||||
u32 have_taint = 1;
|
||||
u8 status = 0, found_one = 0;
|
||||
|
||||
/* loop cmps are useless, detect and ignore them */
|
||||
#ifdef WORD_SIZE_64
|
||||
u32 is_n = 0;
|
||||
u128 s128_v0 = 0, s128_v1 = 0, orig_s128_v0 = 0, orig_s128_v1 = 0;
|
||||
#endif
|
||||
u64 s_v0, s_v1;
|
||||
@ -1521,6 +1601,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
|
||||
|
||||
}
|
||||
|
||||
#ifdef WORD_SIZE_64
|
||||
switch (SHAPE_BYTES(h->shape)) {
|
||||
|
||||
case 1:
|
||||
@ -1533,6 +1614,8 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
for (i = 0; i < loggeds; ++i) {
|
||||
|
||||
struct cmp_operands *o = &afl->shm.cmp_map->log[key][i];
|
||||
@ -1770,20 +1853,20 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
|
||||
}
|
||||
|
||||
static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
|
||||
u8 *o_pattern, u8 *changed_val, u32 idx,
|
||||
u8 *o_pattern, u8 *changed_val, u8 plen, u32 idx,
|
||||
u32 taint_len, u8 *orig_buf, u8 *buf, u8 *cbuf,
|
||||
u32 len, u8 lvl, u8 *status) {
|
||||
|
||||
#ifndef COMBINE
|
||||
#ifndef CMPLOG_COMBINE
|
||||
(void)(cbuf);
|
||||
#endif
|
||||
#ifndef CMPLOG_TRANSFORM
|
||||
(void)(changed_val);
|
||||
#endif
|
||||
//#ifndef CMPLOG_SOLVE_TRANSFORM
|
||||
// (void)(changed_val);
|
||||
//#endif
|
||||
|
||||
u8 save[40];
|
||||
u32 saved_idx = idx, pre, from = 0, to = 0, i, j;
|
||||
u32 its_len = MIN((u32)32, len - idx);
|
||||
u32 its_len = MIN((u32)plen, len - idx);
|
||||
its_len = MIN(its_len, taint_len);
|
||||
u32 saved_its_len = its_len;
|
||||
|
||||
@ -1847,7 +1930,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
|
||||
|
||||
if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; }
|
||||
|
||||
#ifdef COMBINE
|
||||
#ifdef CMPLOG_COMBINE
|
||||
if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); }
|
||||
#endif
|
||||
|
||||
@ -1859,16 +1942,16 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
|
||||
|
||||
}
|
||||
|
||||
#ifdef CMPLOG_TRANSFORM
|
||||
//#ifdef CMPLOG_SOLVE_TRANSFORM
|
||||
|
||||
if (*status == 1) return 0;
|
||||
|
||||
if (lvl & LVL3) {
|
||||
if (afl->cmplog_enable_transform && (lvl & LVL3)) {
|
||||
|
||||
u32 toupper = 0, tolower = 0, xor = 0, arith = 0, tohex = 0, fromhex = 0;
|
||||
#ifdef CMPLOG_TRANSFORM_BASE64
|
||||
#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
|
||||
u32 tob64 = 0, fromb64 = 0;
|
||||
#endif
|
||||
#endif
|
||||
u32 from_0 = 0, from_x = 0, from_X = 0, from_slash = 0, from_up = 0;
|
||||
u32 to_0 = 0, to_x = 0, to_slash = 0, to_up = 0;
|
||||
u8 xor_val[32], arith_val[32], tmp[48];
|
||||
@ -1964,7 +2047,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
|
||||
|
||||
}
|
||||
|
||||
#ifdef CMPLOG_TRANSFORM_BASE64
|
||||
#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
|
||||
if (i % 3 == 2 && i < 24) {
|
||||
|
||||
if (is_base64(repl + ((i / 3) << 2))) tob64 += 3;
|
||||
@ -1977,7 +2060,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if ((o_pattern[i] ^ orig_buf[idx + i]) == xor_val[i] && xor_val[i]) {
|
||||
|
||||
@ -2005,20 +2088,20 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
|
||||
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
#ifdef _DEBUG
|
||||
fprintf(stderr,
|
||||
"RTN idx=%u loop=%u xor=%u arith=%u tolower=%u toupper=%u "
|
||||
"tohex=%u fromhex=%u to_0=%u to_slash=%u to_x=%u "
|
||||
"from_0=%u from_slash=%u from_x=%u\n",
|
||||
idx, i, xor, arith, tolower, toupper, tohex, fromhex, to_0,
|
||||
to_slash, to_x, from_0, from_slash, from_x);
|
||||
#ifdef CMPLOG_TRANSFORM_BASE64
|
||||
#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
|
||||
fprintf(stderr, "RTN idx=%u loop=%u tob64=%u from64=%u\n", tob64,
|
||||
fromb64);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CMPLOG_TRANSFORM_BASE64
|
||||
#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
|
||||
// input is base64 and converted to binary? convert repl to base64!
|
||||
if ((i % 4) == 3 && i < 24 && fromb64 > i) {
|
||||
|
||||
@ -2041,7 +2124,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// input is converted to hex? convert repl to binary!
|
||||
if (i < 16 && tohex > i) {
|
||||
@ -2170,16 +2253,16 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
|
||||
|
||||
}
|
||||
|
||||
#ifdef COMBINE
|
||||
#ifdef CMPLOG_COMBINE
|
||||
if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i + 1); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if ((i >= 7 &&
|
||||
(i >= xor&&i >= arith &&i >= tolower &&i >= toupper &&i > tohex &&i >
|
||||
(fromhex + from_0 + from_x + from_slash + 1)
|
||||
#ifdef CMPLOG_TRANSFORM_BASE64
|
||||
#ifdef CMPLOG_SOLVE_TRANSFORM_BASE64
|
||||
&& i > tob64 + 3 && i > fromb64 + 4
|
||||
#endif
|
||||
#endif
|
||||
)) ||
|
||||
repl[i] != changed_val[i] || *status == 1) {
|
||||
|
||||
@ -2193,7 +2276,7 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl,
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
return 0;
|
||||
|
||||
@ -2282,9 +2365,9 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
|
||||
|
||||
status = 0;
|
||||
|
||||
if (unlikely(rtn_extend_encoding(afl, o->v0, o->v1, orig_o->v0,
|
||||
orig_o->v1, idx, taint_len, orig_buf,
|
||||
buf, cbuf, len, lvl, &status))) {
|
||||
if (unlikely(rtn_extend_encoding(
|
||||
afl, o->v0, o->v1, orig_o->v0, orig_o->v1, SHAPE_BYTES(h->shape),
|
||||
idx, taint_len, orig_buf, buf, cbuf, len, lvl, &status))) {
|
||||
|
||||
return 1;
|
||||
|
||||
@ -2299,9 +2382,9 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf,
|
||||
|
||||
status = 0;
|
||||
|
||||
if (unlikely(rtn_extend_encoding(afl, o->v1, o->v0, orig_o->v1,
|
||||
orig_o->v0, idx, taint_len, orig_buf,
|
||||
buf, cbuf, len, lvl, &status))) {
|
||||
if (unlikely(rtn_extend_encoding(
|
||||
afl, o->v1, o->v0, orig_o->v1, orig_o->v0, SHAPE_BYTES(h->shape),
|
||||
idx, taint_len, orig_buf, buf, cbuf, len, lvl, &status))) {
|
||||
|
||||
return 1;
|
||||
|
||||
@ -2469,7 +2552,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) {
|
||||
u32 lvl = (afl->queue_cur->colorized ? 0 : LVL1) +
|
||||
(afl->cmplog_lvl == CMPLOG_LVL_MAX ? LVL3 : 0);
|
||||
|
||||
#ifdef COMBINE
|
||||
#ifdef CMPLOG_COMBINE
|
||||
u8 *cbuf = afl_realloc((void **)&afl->in_scratch_buf, len + 128);
|
||||
memcpy(cbuf, orig_buf, len);
|
||||
u8 *virgin_backup = afl_realloc((void **)&afl->ex_buf, afl->shm.map_size);
|
||||
@ -2526,9 +2609,9 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) {
|
||||
|
||||
} else if ((lvl & LVL1)
|
||||
|
||||
#ifdef CMPLOG_TRANSFORM
|
||||
|| (lvl & LVL3)
|
||||
#endif
|
||||
//#ifdef CMPLOG_SOLVE_TRANSFORM
|
||||
|| ((lvl & LVL3) && afl->cmplog_enable_transform)
|
||||
//#endif
|
||||
) {
|
||||
|
||||
if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, lvl, taint))) {
|
||||
@ -2583,7 +2666,7 @@ exit_its:
|
||||
|
||||
}
|
||||
|
||||
#ifdef COMBINE
|
||||
#ifdef CMPLOG_COMBINE
|
||||
if (afl->queued_paths + afl->unique_crashes > orig_hit_cnt + 1) {
|
||||
|
||||
// copy the current virgin bits so we can recover the information
|
||||
@ -2607,9 +2690,9 @@ exit_its:
|
||||
}
|
||||
|
||||
#else
|
||||
u32 *v = (u64 *)afl->virgin_bits;
|
||||
u32 *s = (u64 *)virgin_save;
|
||||
u32 i;
|
||||
u32 *v = (u32 *)afl->virgin_bits;
|
||||
u32 *s = (u32 *)virgin_save;
|
||||
u32 i;
|
||||
for (i = 0; i < (afl->shm.map_size >> 2); i++) {
|
||||
|
||||
v[i] &= s[i];
|
||||
@ -2622,7 +2705,7 @@ exit_its:
|
||||
dump("COMB", cbuf, len);
|
||||
if (status == 1) {
|
||||
|
||||
fprintf(stderr, "NEW COMBINED\n");
|
||||
fprintf(stderr, "NEW CMPLOG_COMBINED\n");
|
||||
|
||||
} else {
|
||||
|
||||
@ -2672,7 +2755,3 @@ exit_its:
|
||||
|
||||
}
|
||||
|
||||
#ifdef COMBINE
|
||||
#undef COMBINE
|
||||
#endif
|
||||
|
||||
|
@ -707,6 +707,8 @@ void sync_fuzzers(afl_state_t *afl) {
|
||||
|
||||
if (afl->foreign_sync_cnt) read_foreign_testcases(afl, 0);
|
||||
|
||||
afl->last_sync_time = get_cur_time();
|
||||
|
||||
}
|
||||
|
||||
/* Trim all new test cases to save cycles when doing deterministic checks. The
|
||||
|
@ -486,6 +486,8 @@ void read_afl_environment(afl_state_t *afl, char **envp) {
|
||||
WARNF("Mistyped AFL environment variable: %s", env);
|
||||
issue_detected = 1;
|
||||
|
||||
print_suggested_envs(env);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -185,15 +185,14 @@ void load_stats_file(afl_state_t *afl) {
|
||||
|
||||
/* Update stats file for unattended monitoring. */
|
||||
|
||||
void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability,
|
||||
double eps) {
|
||||
void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
|
||||
double stability, double eps) {
|
||||
|
||||
#ifndef __HAIKU__
|
||||
struct rusage rus;
|
||||
#endif
|
||||
|
||||
u64 cur_time = get_cur_time();
|
||||
u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits);
|
||||
u8 fn[PATH_MAX];
|
||||
FILE *f;
|
||||
|
||||
@ -353,9 +352,11 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability,
|
||||
|
||||
/* Update the plot file if there is a reason to. */
|
||||
|
||||
void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) {
|
||||
void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg,
|
||||
double eps) {
|
||||
|
||||
if (unlikely(afl->plot_prev_qp == afl->queued_paths &&
|
||||
if (unlikely(afl->stop_soon) ||
|
||||
unlikely(afl->plot_prev_qp == afl->queued_paths &&
|
||||
afl->plot_prev_pf == afl->pending_favored &&
|
||||
afl->plot_prev_pnf == afl->pending_not_fuzzed &&
|
||||
afl->plot_prev_ce == afl->current_entry &&
|
||||
@ -384,16 +385,16 @@ void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) {
|
||||
/* Fields in the file:
|
||||
|
||||
unix_time, afl->cycles_done, cur_path, paths_total, paths_not_fuzzed,
|
||||
favored_not_fuzzed, afl->unique_crashes, afl->unique_hangs, afl->max_depth,
|
||||
execs_per_sec */
|
||||
favored_not_fuzzed, unique_crashes, unique_hangs, max_depth,
|
||||
execs_per_sec, edges_found */
|
||||
|
||||
fprintf(
|
||||
afl->fsrv.plot_file,
|
||||
"%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu\n",
|
||||
get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry,
|
||||
afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored,
|
||||
bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, eps,
|
||||
afl->plot_prev_ed); /* ignore errors */
|
||||
fprintf(afl->fsrv.plot_file,
|
||||
"%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu, "
|
||||
"%u\n",
|
||||
get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry,
|
||||
afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored,
|
||||
bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth,
|
||||
eps, afl->plot_prev_ed, t_bytes); /* ignore errors */
|
||||
|
||||
fflush(afl->fsrv.plot_file);
|
||||
|
||||
@ -532,7 +533,8 @@ void show_stats(afl_state_t *afl) {
|
||||
if (cur_ms - afl->stats_last_stats_ms > STATS_UPDATE_SEC * 1000) {
|
||||
|
||||
afl->stats_last_stats_ms = cur_ms;
|
||||
write_stats_file(afl, t_byte_ratio, stab_ratio, afl->stats_avg_exec);
|
||||
write_stats_file(afl, t_bytes, t_byte_ratio, stab_ratio,
|
||||
afl->stats_avg_exec);
|
||||
save_auto(afl);
|
||||
write_bitmap(afl);
|
||||
|
||||
@ -555,7 +557,7 @@ void show_stats(afl_state_t *afl) {
|
||||
if (cur_ms - afl->stats_last_plot_ms > PLOT_UPDATE_SEC * 1000) {
|
||||
|
||||
afl->stats_last_plot_ms = cur_ms;
|
||||
maybe_update_plot_file(afl, t_byte_ratio, afl->stats_avg_exec);
|
||||
maybe_update_plot_file(afl, t_bytes, t_byte_ratio, afl->stats_avg_exec);
|
||||
|
||||
}
|
||||
|
||||
@ -643,6 +645,13 @@ void show_stats(afl_state_t *afl) {
|
||||
#define SP10 SP5 SP5
|
||||
#define SP20 SP10 SP10
|
||||
|
||||
/* Since `total_crashes` does not get reloaded from disk on restart,
|
||||
it indicates if we found crashes this round already -> paint red.
|
||||
If it's 0, but `unique_crashes` is set from a past run, paint in yellow. */
|
||||
char *crash_color = afl->total_crashes ? cLRD
|
||||
: afl->unique_crashes ? cYEL
|
||||
: cRST;
|
||||
|
||||
/* Lord, forgive me this. */
|
||||
|
||||
SAYF(SET_G1 bSTG bLT bH bSTOP cCYA
|
||||
@ -730,7 +739,7 @@ void show_stats(afl_state_t *afl) {
|
||||
u_stringify_time_diff(time_tmp, cur_ms, afl->last_crash_time);
|
||||
SAYF(bV bSTOP " last uniq crash : " cRST "%-33s " bSTG bV bSTOP
|
||||
" uniq crashes : %s%-6s" bSTG bV "\n",
|
||||
time_tmp, afl->unique_crashes ? cLRD : cRST, tmp);
|
||||
time_tmp, crash_color, tmp);
|
||||
|
||||
sprintf(tmp, "%s%s", u_stringify_int(IB(0), afl->unique_hangs),
|
||||
(afl->unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : "");
|
||||
@ -813,15 +822,13 @@ void show_stats(afl_state_t *afl) {
|
||||
|
||||
SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP
|
||||
" new crashes : %s%-22s" bSTG bV "\n",
|
||||
u_stringify_int(IB(0), afl->fsrv.total_execs),
|
||||
afl->unique_crashes ? cLRD : cRST, tmp);
|
||||
u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp);
|
||||
|
||||
} else {
|
||||
|
||||
SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP
|
||||
" total crashes : %s%-22s" bSTG bV "\n",
|
||||
u_stringify_int(IB(0), afl->fsrv.total_execs),
|
||||
afl->unique_crashes ? cLRD : cRST, tmp);
|
||||
u_stringify_int(IB(0), afl->fsrv.total_execs), crash_color, tmp);
|
||||
|
||||
}
|
||||
|
||||
@ -1217,7 +1224,7 @@ void show_init_stats(afl_state_t *afl) {
|
||||
stringify_int(IB(0), min_us), stringify_int(IB(1), max_us),
|
||||
stringify_int(IB(2), avg_us));
|
||||
|
||||
if (!afl->timeout_given) {
|
||||
if (afl->timeout_given != 1) {
|
||||
|
||||
/* 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.
|
||||
|
312
src/afl-fuzz.c
312
src/afl-fuzz.c
@ -103,7 +103,10 @@ static void usage(u8 *argv0, int more_help) {
|
||||
" quad -- see docs/power_schedules.md\n"
|
||||
" -f file - location read by the fuzzed program (default: stdin "
|
||||
"or @@)\n"
|
||||
" -t msec - timeout for each run (auto-scaled, 50-%u ms)\n"
|
||||
" -t msec - timeout for each run (auto-scaled, default %u ms). "
|
||||
"Add a '+'\n"
|
||||
" to auto-calculate the timeout, the value being the "
|
||||
"maximum.\n"
|
||||
" -m megs - memory limit for child process (%u MB, 0 = no limit "
|
||||
"[default])\n"
|
||||
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||
@ -122,10 +125,10 @@ static void usage(u8 *argv0, int more_help) {
|
||||
" -c program - enable CmpLog by specifying a binary compiled for "
|
||||
"it.\n"
|
||||
" if using QEMU, just use -c 0.\n"
|
||||
" -l cmplog_level - set the complexity/intensivity of CmpLog.\n"
|
||||
" Values: 1 (basic), 2 (larger files) and 3 "
|
||||
"(transform)\n\n"
|
||||
|
||||
" -l cmplog_opts - CmpLog configuration values (e.g. \"2AT\"):\n"
|
||||
" 1=small files (default), 2=larger files, 3=all "
|
||||
"files,\n"
|
||||
" A=arithmetic solving, T=transformational solving.\n\n"
|
||||
"Fuzzing behavior settings:\n"
|
||||
" -Z - sequential queue selection instead of weighted "
|
||||
"random\n"
|
||||
@ -137,8 +140,8 @@ static void usage(u8 *argv0, int more_help) {
|
||||
|
||||
"Testing settings:\n"
|
||||
" -s seed - use a fixed seed for the RNG\n"
|
||||
" -V seconds - fuzz for a specific time then terminate\n"
|
||||
" -E execs - fuzz for a approx. no of total executions then "
|
||||
" -V seconds - fuzz for a specified time then terminate\n"
|
||||
" -E execs - fuzz for an approx. no. of total executions then "
|
||||
"terminate\n"
|
||||
" Note: not precise and can have several more "
|
||||
"executions.\n\n"
|
||||
@ -198,6 +201,7 @@ static void usage(u8 *argv0, int more_help) {
|
||||
"AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n"
|
||||
"AFL_HANG_TMOUT: override timeout value (in milliseconds)\n"
|
||||
"AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n"
|
||||
"AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n"
|
||||
"AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n"
|
||||
"AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n"
|
||||
"AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n"
|
||||
@ -347,7 +351,7 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
exit_1 = !!afl->afl_env.afl_bench_just_one;
|
||||
|
||||
SAYF(cCYA "afl-fuzz" VERSION cRST
|
||||
" based on afl by Michal Zalewski and a big online community\n");
|
||||
" based on afl by Michal Zalewski and a large online community\n");
|
||||
|
||||
doc_path = access(DOC_PATH, F_OK) != 0 ? (u8 *)"docs" : (u8 *)DOC_PATH;
|
||||
|
||||
@ -552,13 +556,22 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
case 'F': /* foreign sync dir */
|
||||
|
||||
if (!afl->is_main_node)
|
||||
if (!optarg) { FATAL("Missing path for -F"); }
|
||||
if (!afl->is_main_node) {
|
||||
|
||||
FATAL(
|
||||
"Option -F can only be specified after the -M option for the "
|
||||
"main fuzzer of a fuzzing campaign");
|
||||
if (afl->foreign_sync_cnt >= FOREIGN_SYNCS_MAX)
|
||||
|
||||
}
|
||||
|
||||
if (afl->foreign_sync_cnt >= FOREIGN_SYNCS_MAX) {
|
||||
|
||||
FATAL("Maximum %u entried of -F option can be specified",
|
||||
FOREIGN_SYNCS_MAX);
|
||||
|
||||
}
|
||||
|
||||
afl->foreign_syncs[afl->foreign_sync_cnt].dir = optarg;
|
||||
while (afl->foreign_syncs[afl->foreign_sync_cnt]
|
||||
.dir[strlen(afl->foreign_syncs[afl->foreign_sync_cnt].dir) -
|
||||
@ -802,13 +815,36 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
case 'l': {
|
||||
|
||||
afl->cmplog_lvl = atoi(optarg);
|
||||
if (afl->cmplog_lvl < 1 || afl->cmplog_lvl > CMPLOG_LVL_MAX) {
|
||||
if (!optarg) { FATAL("missing parameter for 'l'"); }
|
||||
char *c = optarg;
|
||||
while (*c) {
|
||||
|
||||
FATAL(
|
||||
"Bad complog level value, accepted values are 1 (default), 2 and "
|
||||
"%u.",
|
||||
CMPLOG_LVL_MAX);
|
||||
switch (*c) {
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
afl->cmplog_lvl = 1;
|
||||
break;
|
||||
case '2':
|
||||
afl->cmplog_lvl = 2;
|
||||
break;
|
||||
case '3':
|
||||
afl->cmplog_lvl = 3;
|
||||
break;
|
||||
case 'a':
|
||||
case 'A':
|
||||
afl->cmplog_enable_arith = 1;
|
||||
break;
|
||||
case 't':
|
||||
case 'T':
|
||||
afl->cmplog_enable_transform = 1;
|
||||
break;
|
||||
default:
|
||||
FATAL("Unknown option value '%c' in -l %s", *c, optarg);
|
||||
|
||||
}
|
||||
|
||||
++c;
|
||||
|
||||
}
|
||||
|
||||
@ -1367,6 +1403,15 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY);
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0) != 0) {
|
||||
|
||||
WARNF("general thread priority settings failed");
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
init_count_class16();
|
||||
|
||||
if (afl->is_main_node && check_main_node_exists(afl) == 1) {
|
||||
@ -1401,24 +1446,9 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
// read_foreign_testcases(afl, 1); for the moment dont do this
|
||||
OKF("Loaded a total of %u seeds.", afl->queued_paths);
|
||||
|
||||
load_auto(afl);
|
||||
|
||||
pivot_inputs(afl);
|
||||
|
||||
if (extras_dir_cnt) {
|
||||
|
||||
for (i = 0; i < extras_dir_cnt; i++) {
|
||||
|
||||
load_extras(afl, extras_dir[i]);
|
||||
|
||||
}
|
||||
|
||||
dedup_extras(afl);
|
||||
OKF("Loaded a total of %u extras.", afl->extras_cnt);
|
||||
|
||||
}
|
||||
|
||||
if (!afl->timeout_given) { find_timeout(afl); }
|
||||
if (!afl->timeout_given) { find_timeout(afl); } // only for resumes!
|
||||
|
||||
if ((afl->tmp_dir = afl->afl_env.afl_tmpdir) != NULL &&
|
||||
!afl->in_place_resume) {
|
||||
@ -1532,6 +1562,21 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
if (afl->non_instrumented_mode || afl->fsrv.qemu_mode || afl->unicorn_mode) {
|
||||
|
||||
map_size = afl->fsrv.map_size = MAP_SIZE;
|
||||
afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size);
|
||||
afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size);
|
||||
afl->virgin_crash = ck_realloc(afl->virgin_crash, map_size);
|
||||
afl->var_bytes = ck_realloc(afl->var_bytes, map_size);
|
||||
afl->top_rated = ck_realloc(afl->top_rated, map_size * sizeof(void *));
|
||||
afl->clean_trace = ck_realloc(afl->clean_trace, map_size);
|
||||
afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, map_size);
|
||||
afl->first_trace = ck_realloc(afl->first_trace, map_size);
|
||||
afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size);
|
||||
|
||||
}
|
||||
|
||||
afl->argv = use_argv;
|
||||
afl->fsrv.trace_bits =
|
||||
afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode);
|
||||
@ -1539,42 +1584,44 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode &&
|
||||
!afl->unicorn_mode) {
|
||||
|
||||
afl->fsrv.map_size = 4194304; // dummy temporary value
|
||||
setenv("AFL_MAP_SIZE", "4194304", 1);
|
||||
if (map_size <= 8000000 && !afl->non_instrumented_mode &&
|
||||
!afl->fsrv.qemu_mode && !afl->unicorn_mode) {
|
||||
|
||||
afl->fsrv.map_size = 8000000; // dummy temporary value
|
||||
setenv("AFL_MAP_SIZE", "8000000", 1);
|
||||
|
||||
}
|
||||
|
||||
u32 new_map_size = afl_fsrv_get_mapsize(
|
||||
&afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child);
|
||||
|
||||
if (new_map_size && new_map_size != 4194304) {
|
||||
// only reinitialize when it makes sense
|
||||
if ((map_size < new_map_size ||
|
||||
(new_map_size != MAP_SIZE && new_map_size < map_size &&
|
||||
map_size - new_map_size > MAP_SIZE))) {
|
||||
|
||||
// only reinitialize when it makes sense
|
||||
if (map_size < new_map_size ||
|
||||
(new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) {
|
||||
OKF("Re-initializing maps to %u bytes", new_map_size);
|
||||
|
||||
OKF("Re-initializing maps to %u bytes", new_map_size);
|
||||
afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size);
|
||||
afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size);
|
||||
afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size);
|
||||
afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size);
|
||||
afl->top_rated =
|
||||
ck_realloc(afl->top_rated, new_map_size * sizeof(void *));
|
||||
afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size);
|
||||
afl->clean_trace_custom =
|
||||
ck_realloc(afl->clean_trace_custom, new_map_size);
|
||||
afl->first_trace = ck_realloc(afl->first_trace, new_map_size);
|
||||
afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size);
|
||||
|
||||
afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size);
|
||||
afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size);
|
||||
afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size);
|
||||
afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size);
|
||||
afl->top_rated =
|
||||
ck_realloc(afl->top_rated, new_map_size * sizeof(void *));
|
||||
afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size);
|
||||
afl->clean_trace_custom =
|
||||
ck_realloc(afl->clean_trace_custom, new_map_size);
|
||||
afl->first_trace = ck_realloc(afl->first_trace, new_map_size);
|
||||
afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size);
|
||||
|
||||
afl_fsrv_kill(&afl->fsrv);
|
||||
afl_shm_deinit(&afl->shm);
|
||||
afl->fsrv.map_size = new_map_size;
|
||||
afl->fsrv.trace_bits =
|
||||
afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode);
|
||||
setenv("AFL_NO_AUTODICT", "1", 1); // loaded already
|
||||
afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon,
|
||||
afl->afl_env.afl_debug_child);
|
||||
|
||||
}
|
||||
afl_fsrv_kill(&afl->fsrv);
|
||||
afl_shm_deinit(&afl->shm);
|
||||
afl->fsrv.map_size = new_map_size;
|
||||
afl->fsrv.trace_bits =
|
||||
afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode);
|
||||
setenv("AFL_NO_AUTODICT", "1", 1); // loaded already
|
||||
afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon,
|
||||
afl->afl_env.afl_debug_child);
|
||||
|
||||
map_size = new_map_size;
|
||||
|
||||
@ -1594,57 +1641,77 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary;
|
||||
afl->cmplog_fsrv.init_child_func = cmplog_exec_child;
|
||||
|
||||
afl->cmplog_fsrv.map_size = 4194304;
|
||||
if (map_size <= 8000000 && !afl->non_instrumented_mode &&
|
||||
!afl->fsrv.qemu_mode && !afl->unicorn_mode) {
|
||||
|
||||
afl->cmplog_fsrv.map_size = 8000000; // dummy temporary value
|
||||
setenv("AFL_MAP_SIZE", "8000000", 1);
|
||||
|
||||
}
|
||||
|
||||
u32 new_map_size =
|
||||
afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon,
|
||||
afl->afl_env.afl_debug_child);
|
||||
|
||||
if (new_map_size && new_map_size != 4194304) {
|
||||
// only reinitialize when it needs to be larger
|
||||
if (map_size < new_map_size) {
|
||||
|
||||
// only reinitialize when it needs to be larger
|
||||
if (map_size < new_map_size) {
|
||||
OKF("Re-initializing maps to %u bytes due cmplog", new_map_size);
|
||||
|
||||
OKF("Re-initializing maps to %u bytes due cmplog", new_map_size);
|
||||
afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size);
|
||||
afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size);
|
||||
afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size);
|
||||
afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size);
|
||||
afl->top_rated =
|
||||
ck_realloc(afl->top_rated, new_map_size * sizeof(void *));
|
||||
afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size);
|
||||
afl->clean_trace_custom =
|
||||
ck_realloc(afl->clean_trace_custom, new_map_size);
|
||||
afl->first_trace = ck_realloc(afl->first_trace, new_map_size);
|
||||
afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size);
|
||||
|
||||
afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size);
|
||||
afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size);
|
||||
afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size);
|
||||
afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size);
|
||||
afl->top_rated =
|
||||
ck_realloc(afl->top_rated, new_map_size * sizeof(void *));
|
||||
afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size);
|
||||
afl->clean_trace_custom =
|
||||
ck_realloc(afl->clean_trace_custom, new_map_size);
|
||||
afl->first_trace = ck_realloc(afl->first_trace, new_map_size);
|
||||
afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size);
|
||||
afl_fsrv_kill(&afl->fsrv);
|
||||
afl_fsrv_kill(&afl->cmplog_fsrv);
|
||||
afl_shm_deinit(&afl->shm);
|
||||
|
||||
afl_fsrv_kill(&afl->fsrv);
|
||||
afl_fsrv_kill(&afl->cmplog_fsrv);
|
||||
afl_shm_deinit(&afl->shm);
|
||||
afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same
|
||||
afl->cmplog_fsrv.map_size = new_map_size; // non-cmplog stays the same
|
||||
map_size = new_map_size;
|
||||
|
||||
afl->fsrv.trace_bits =
|
||||
afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode);
|
||||
setenv("AFL_NO_AUTODICT", "1", 1); // loaded already
|
||||
afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon,
|
||||
afl->afl_env.afl_debug_child);
|
||||
setenv("AFL_NO_AUTODICT", "1", 1); // loaded already
|
||||
afl->fsrv.trace_bits =
|
||||
afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode);
|
||||
afl->cmplog_fsrv.trace_bits = afl->fsrv.trace_bits;
|
||||
afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon,
|
||||
afl->afl_env.afl_debug_child);
|
||||
afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon,
|
||||
afl->afl_env.afl_debug_child);
|
||||
|
||||
afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon,
|
||||
afl->afl_env.afl_debug_child);
|
||||
} else {
|
||||
|
||||
map_size = new_map_size;
|
||||
|
||||
}
|
||||
afl->cmplog_fsrv.map_size = new_map_size;
|
||||
|
||||
}
|
||||
|
||||
afl->cmplog_fsrv.map_size = map_size;
|
||||
|
||||
OKF("Cmplog forkserver successfully started");
|
||||
|
||||
}
|
||||
|
||||
load_auto(afl);
|
||||
|
||||
if (extras_dir_cnt) {
|
||||
|
||||
for (i = 0; i < extras_dir_cnt; i++) {
|
||||
|
||||
load_extras(afl, extras_dir[i]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
deunicode_extras(afl);
|
||||
dedup_extras(afl);
|
||||
if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); }
|
||||
|
||||
// after we have the correct bitmap size we can read the bitmap -B option
|
||||
// and set the virgin maps
|
||||
if (afl->in_bitmap) {
|
||||
@ -1672,20 +1739,49 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
cull_queue(afl);
|
||||
|
||||
if (!afl->pending_not_fuzzed) {
|
||||
// ensure we have at least one seed that is not disabled.
|
||||
u32 entry, valid_seeds = 0;
|
||||
for (entry = 0; entry < afl->queued_paths; ++entry)
|
||||
if (!afl->queue_buf[entry]->disabled) { ++valid_seeds; }
|
||||
|
||||
if (!afl->pending_not_fuzzed || !valid_seeds) {
|
||||
|
||||
FATAL("We need at least one valid input seed that does not crash!");
|
||||
|
||||
}
|
||||
|
||||
if (afl->timeout_given == 2) { // -t ...+ option
|
||||
|
||||
if (valid_seeds == 1) {
|
||||
|
||||
WARNF(
|
||||
"Only one valid seed is present, auto-calculating the timeout is "
|
||||
"disabled!");
|
||||
afl->timeout_given = 1;
|
||||
|
||||
} else {
|
||||
|
||||
u64 max_ms = 0;
|
||||
|
||||
for (entry = 0; entry < afl->queued_paths; ++entry)
|
||||
if (!afl->queue_buf[entry]->disabled)
|
||||
if (afl->queue_buf[entry]->exec_us > max_ms)
|
||||
max_ms = afl->queue_buf[entry]->exec_us;
|
||||
|
||||
afl->fsrv.exec_tmout = max_ms;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
show_init_stats(afl);
|
||||
|
||||
if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl);
|
||||
|
||||
afl->start_time = get_cur_time();
|
||||
if (afl->in_place_resume || afl->afl_env.afl_autoresume) load_stats_file(afl);
|
||||
write_stats_file(afl, 0, 0, 0);
|
||||
maybe_update_plot_file(afl, 0, 0);
|
||||
write_stats_file(afl, 0, 0, 0, 0);
|
||||
maybe_update_plot_file(afl, 0, 0, 0);
|
||||
save_auto(afl);
|
||||
|
||||
if (afl->stop_soon) { goto stop_fuzzing; }
|
||||
@ -1735,12 +1831,15 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
if (unlikely(afl->old_seed_selection)) {
|
||||
|
||||
afl->current_entry = 0;
|
||||
while (unlikely(afl->queue_buf[afl->current_entry]->disabled)) {
|
||||
while (unlikely(afl->current_entry < afl->queued_paths &&
|
||||
afl->queue_buf[afl->current_entry]->disabled)) {
|
||||
|
||||
++afl->current_entry;
|
||||
|
||||
}
|
||||
|
||||
if (afl->current_entry >= afl->queued_paths) { afl->current_entry = 0; }
|
||||
|
||||
afl->queue_cur = afl->queue_buf[afl->current_entry];
|
||||
|
||||
if (unlikely(seek_to)) {
|
||||
@ -1943,15 +2042,24 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
if (unlikely(afl->is_main_node)) {
|
||||
|
||||
if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) {
|
||||
if (unlikely(get_cur_time() >
|
||||
(SYNC_TIME >> 1) + afl->last_sync_time)) {
|
||||
|
||||
sync_fuzzers(afl);
|
||||
if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) {
|
||||
|
||||
sync_fuzzers(afl);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); }
|
||||
if (unlikely(get_cur_time() > SYNC_TIME + afl->last_sync_time)) {
|
||||
|
||||
if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1966,12 +2074,12 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
}
|
||||
|
||||
write_bitmap(afl);
|
||||
maybe_update_plot_file(afl, 0, 0);
|
||||
maybe_update_plot_file(afl, 0, 0, 0);
|
||||
save_auto(afl);
|
||||
|
||||
stop_fuzzing:
|
||||
|
||||
write_stats_file(afl, 0, 0, 0);
|
||||
write_stats_file(afl, 0, 0, 0, 0);
|
||||
afl->force_ui_update = 1; // ensure the screen is reprinted
|
||||
show_stats(afl); // print the screen one last time
|
||||
|
||||
|
@ -563,6 +563,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
|
||||
"detect_leaks=0:"
|
||||
"allocator_may_return_null=1:"
|
||||
"symbolize=0:"
|
||||
"detect_odr_violation=0:"
|
||||
"handle_segv=0:"
|
||||
"handle_sigbus=0:"
|
||||
"handle_abort=0:"
|
||||
@ -1013,7 +1014,6 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
if (in_dir) {
|
||||
|
||||
if (at_file) { PFATAL("Options -A and -i are mutually exclusive"); }
|
||||
detect_file_args(argv + optind, "", &fsrv->use_stdin);
|
||||
|
||||
} else {
|
||||
@ -1169,8 +1169,9 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
|
||||
}
|
||||
|
||||
stdin_file =
|
||||
alloc_printf("%s/.afl-showmap-temp-%u", use_dir, (u32)getpid());
|
||||
stdin_file = at_file ? strdup(at_file)
|
||||
: (char *)alloc_printf("%s/.afl-showmap-temp-%u",
|
||||
use_dir, (u32)getpid());
|
||||
unlink(stdin_file);
|
||||
atexit(at_exit_handler);
|
||||
fsrv->out_file = stdin_file;
|
||||
|
@ -717,6 +717,7 @@ static void set_up_environment(afl_forkserver_t *fsrv) {
|
||||
"detect_leaks=0:"
|
||||
"allocator_may_return_null=1:"
|
||||
"symbolize=0:"
|
||||
"detect_odr_violation=0:"
|
||||
"handle_segv=0:"
|
||||
"handle_sigbus=0:"
|
||||
"handle_abort=0:"
|
||||
|
@ -7,7 +7,7 @@ AFL_GCC=afl-gcc
|
||||
$ECHO "$BLUE[*] Testing: ${AFL_GCC}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin"
|
||||
test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "i386" && {
|
||||
test -e ../${AFL_GCC} -a -e ../afl-showmap -a -e ../afl-fuzz && {
|
||||
../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1
|
||||
../${AFL_GCC} -o test-instr.plain -O0 ../test-instr.c > /dev/null 2>&1
|
||||
AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1
|
||||
test -e test-instr.plain && {
|
||||
$ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded"
|
||||
@ -39,7 +39,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc
|
||||
$ECHO "$RED[!] ${AFL_GCC} failed"
|
||||
echo CUT------------------------------------------------------------------CUT
|
||||
uname -a
|
||||
../${AFL_GCC} -o test-instr.plain ../test-instr.c
|
||||
../${AFL_GCC} -o test-instr.plain -O0 ../test-instr.c
|
||||
echo CUT------------------------------------------------------------------CUT
|
||||
CODE=1
|
||||
}
|
||||
@ -128,7 +128,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc
|
||||
$ECHO "$BLUE[*] Testing: ${AFL_GCC}, afl-showmap, afl-fuzz, afl-cmin and afl-tmin"
|
||||
SKIP=
|
||||
test -e ../${AFL_GCC} -a -e ../afl-showmap -a -e ../afl-fuzz && {
|
||||
../${AFL_GCC} -o test-instr.plain ../test-instr.c > /dev/null 2>&1
|
||||
../${AFL_GCC} -o test-instr.plain -O0 ../test-instr.c > /dev/null 2>&1
|
||||
AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1
|
||||
test -e test-instr.plain && {
|
||||
$ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded"
|
||||
|
@ -37,9 +37,9 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && {
|
||||
echo "00000" > in/in
|
||||
|
||||
# Run afl-fuzz w/ the C mutator
|
||||
$ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 5 seconds"
|
||||
$ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 10 seconds"
|
||||
{
|
||||
AFL_CUSTOM_MUTATOR_LIBRARY=./libexamplemutator.so AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1
|
||||
AFL_CUSTOM_MUTATOR_LIBRARY=./libexamplemutator.so AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1
|
||||
} >>errors 2>&1
|
||||
|
||||
# Check results
|
||||
@ -57,9 +57,9 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && {
|
||||
rm -rf out errors core.*
|
||||
|
||||
# Run afl-fuzz w/ multiple C mutators
|
||||
$ECHO "$GREY[*] running afl-fuzz with multiple custom C mutators, this will take approx 5 seconds"
|
||||
$ECHO "$GREY[*] running afl-fuzz with multiple custom C mutators, this will take approx 10 seconds"
|
||||
{
|
||||
AFL_CUSTOM_MUTATOR_LIBRARY="./libexamplemutator.so;./libexamplemutator2.so" AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V1 -m ${MEM_LIMIT} -i in -o out -- ./test-multiple-mutators >>errors 2>&1
|
||||
AFL_CUSTOM_MUTATOR_LIBRARY="./libexamplemutator.so;./libexamplemutator2.so" AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-multiple-mutators >>errors 2>&1
|
||||
} >>errors 2>&1
|
||||
|
||||
test -n "$( ls out/default/crashes/id:000000* 2>/dev/null )" && { # TODO: update here
|
||||
@ -76,11 +76,11 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && {
|
||||
rm -rf out errors core.*
|
||||
|
||||
# Run afl-fuzz w/ the Python mutator
|
||||
$ECHO "$GREY[*] running afl-fuzz for the Python mutator, this will take approx 5 seconds"
|
||||
$ECHO "$GREY[*] running afl-fuzz for the Python mutator, this will take approx 10 seconds"
|
||||
{
|
||||
export PYTHONPATH=${CUSTOM_MUTATOR_PATH}
|
||||
export AFL_PYTHON_MODULE=example
|
||||
AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V5 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1
|
||||
AFL_CUSTOM_MUTATOR_ONLY=1 ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1
|
||||
unset PYTHONPATH
|
||||
unset AFL_PYTHON_MODULE
|
||||
} >>errors 2>&1
|
||||
|
@ -162,9 +162,9 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
|
||||
test -e test-floatingpoint && {
|
||||
mkdir -p in
|
||||
echo ZZZZ > in/in
|
||||
$ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 30 seconds"
|
||||
$ECHO "$GREY[*] running afl-fuzz with floating point splitting, this will take max. 45 seconds"
|
||||
{
|
||||
AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -s 1 -V30 -m ${MEM_LIMIT} -i in -o out -D -- ./test-floatingpoint >>errors 2>&1
|
||||
AFL_BENCH_UNTIL_CRASH=1 AFL_NO_UI=1 ../afl-fuzz -Z -s 123 -V50 -m ${MEM_LIMIT} -i in -o out -D -- ./test-floatingpoint >>errors 2>&1
|
||||
} >>errors 2>&1
|
||||
test -n "$( ls out/default/crashes/id:* 2>/dev/null )" && {
|
||||
$ECHO "$GREEN[+] llvm_mode laf-intel floatingpoint splitting feature works correctly"
|
||||
|
@ -39,14 +39,7 @@ test -e ../afl-qemu-trace && {
|
||||
$ECHO "$GREY[*] running afl-fuzz for qemu_mode AFL_ENTRYPOINT, this will take approx 6 seconds"
|
||||
{
|
||||
{
|
||||
if file test-instr | grep -q "32-bit"; then
|
||||
# for 32-bit reduce 8 nibbles to the lower 7 nibbles
|
||||
ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.//'`
|
||||
else
|
||||
# for 64-bit reduce 16 nibbles to the lower 9 nibbles
|
||||
ADDR_LOWER_PART=`nm test-instr | grep "T main" | awk '{print $1}' | sed 's/^.......//'`
|
||||
fi
|
||||
export AFL_ENTRYPOINT=`expr 0x4${ADDR_LOWER_PART}`
|
||||
export AFL_ENTRYPOINT=`printf 1 | AFL_DEBUG=1 ../afl-qemu-trace ./test-instr 2>&1 >/dev/null | awk '/forkserver/{print $4; exit}'`
|
||||
$ECHO AFL_ENTRYPOINT=$AFL_ENTRYPOINT - $(nm test-instr | grep "T main") - $(file ./test-instr)
|
||||
../afl-fuzz -m ${MEM_LIMIT} -V2 -Q -i in -o out -- ./test-instr
|
||||
unset AFL_ENTRYPOINT
|
||||
|
@ -14,7 +14,7 @@ test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/samples/shel
|
||||
EASY_INSTALL_FOUND=0
|
||||
for PYTHON in $PYTHONS ; do
|
||||
|
||||
if $PYTHON -c "help('easy_install');" </dev/null | grep -q module ; then
|
||||
if $PYTHON -c "import setuptools" ; then
|
||||
|
||||
EASY_INSTALL_FOUND=1
|
||||
PY=$PYTHON
|
||||
|
@ -8,19 +8,19 @@ The CompareCoverage and NeverZero counters features are by Andrea Fioraldi <andr
|
||||
|
||||
## 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
|
||||
The code in ./unicorn_mode allows you to build the (Unicorn Engine)[https://github.com/unicorn-engine/unicorn] with afl support.
|
||||
This means, you can run anything that can be emulated in unicorn and 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.
|
||||
with afl-cc or used in QEMU mode.
|
||||
|
||||
There is a significant performance penalty compared to native AFL,
|
||||
but at least we're able to use AFL++ on these binaries, right?
|
||||
|
||||
## 2) How to use
|
||||
|
||||
Requirements: you need an installed python environment.
|
||||
First, you will need a working harness for your target in unicorn, using Python, C, or Rust.
|
||||
For some pointers for more advanced emulation, take a look at [BaseSAFE](https://github.com/fgsect/BaseSAFE) and [Qiling](https://github.com/qilingframework/qiling).
|
||||
|
||||
### Building AFL++'s Unicorn Mode
|
||||
|
||||
@ -34,23 +34,23 @@ cd unicorn_mode
|
||||
```
|
||||
|
||||
NOTE: This script checks out a Unicorn Engine fork as submodule that has been tested
|
||||
and is stable-ish, based on the unicorn engine master.
|
||||
and is stable-ish, based on the unicorn engine `next` branch.
|
||||
|
||||
Building Unicorn will take a little bit (~5-10 minutes). Once it completes
|
||||
it automatically compiles a sample application and verifies that it works.
|
||||
|
||||
### Fuzzing with Unicorn Mode
|
||||
|
||||
To really use unicorn-mode effectively you need to prepare the following:
|
||||
To 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
|
||||
+ Quality/speed of results will depend greatly on the quality of starting
|
||||
samples
|
||||
+ See AFL's guidance on how to create a sample corpus
|
||||
* Unicornafl-based test harness which:
|
||||
* Unicornafl-based test harness in Rust, C, or Python, which:
|
||||
+ Adds memory map regions
|
||||
+ Loads binary code into memory
|
||||
+ Calls uc.afl_fuzz() / uc.afl_start_forkserver
|
||||
@ -59,13 +59,13 @@ To really use unicorn-mode effectively you need to prepare the following:
|
||||
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
|
||||
should be checked in the place_input handler. If a constraint
|
||||
fails, just return false from the handler. 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
|
||||
+ Emulates the interesting code from beginning to end
|
||||
+ If a crash is detected, the test harness must 'crash' by
|
||||
throwing a signal (SIGSEGV, SIGKILL, SIGABORT, etc.)
|
||||
throwing a signal (SIGSEGV, SIGKILL, SIGABORT, etc.), or indicate a crash in the crash validation callback.
|
||||
|
||||
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:
|
||||
@ -79,11 +79,12 @@ 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:
|
||||
post that uses slightly older concepts, but describes the general ideas, at:
|
||||
|
||||
[https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf](https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf)
|
||||
|
||||
The 'helper_scripts' directory also contains several helper scripts that allow you
|
||||
|
||||
The ['helper_scripts'](./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.
|
||||
|
||||
@ -92,10 +93,10 @@ A example use of AFL-Unicorn mode is discussed in the paper Unicorefuzz:
|
||||
|
||||
## 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.
|
||||
As for the QEMU-based instrumentation, unicornafl comes with a sub-instruction based instrumentation similar in purpose to laf-intel.
|
||||
|
||||
The options that enable Unicorn CompareCoverage are the same used for QEMU.
|
||||
This will split up each multi-byte compare to give feedback for each correct byte.
|
||||
AFL_COMPCOV_LEVEL=1 is to instrument comparisons with only immediate values.
|
||||
|
||||
AFL_COMPCOV_LEVEL=2 instruments all comparison instructions.
|
||||
@ -119,6 +120,20 @@ unicornafl.monkeypatch()
|
||||
|
||||
This will replace all unicorn imports with unicornafl inputs.
|
||||
|
||||
Refer to the [samples/arm_example/arm_tester.c](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!
|
||||
5) Examples
|
||||
|
||||
Apart from reading the documentation in `afl.c` and the python bindings of unicornafl, the best documentation are the [samples/](./samples).
|
||||
The following examples exist at the time of writing:
|
||||
|
||||
- c: A simple example how to use the c bindings
|
||||
- compcov_x64: A python example that uses compcov to traverse hard-to-reach blocks
|
||||
- persistent: A c example using persistent mode for maximum speed, and resetting the target state between each iteration
|
||||
- simple: A simple python example
|
||||
- speedtest/c: The c harness for an example target, used to compare c, python, and rust bindings and fix speed issues
|
||||
- speedtest/python: Fuzzing the same target in python
|
||||
- speedtest/rust: Fuzzing the same target using a rust harness
|
||||
|
||||
Usually, the place to look at is the `harness` in each folder. The source code in each harness is pretty well documented.
|
||||
Most harnesses also have the `afl-fuzz` commandline, or even offer a `make fuzz` Makefile target.
|
||||
Targets in these folders, if x86, can usually be made using `make target` in each folder or get shipped pre-built (plus their source).
|
||||
Especially take a look at the [speedtest documentation](./samples/speedtest/README.md) to see how the languages compare.
|
@ -117,19 +117,19 @@ done
|
||||
|
||||
# some python version should be available now
|
||||
PYTHONS="`command -v python3` `command -v python` `command -v python2`"
|
||||
EASY_INSTALL_FOUND=0
|
||||
SETUPTOOLS_FOUND=0
|
||||
for PYTHON in $PYTHONS ; do
|
||||
|
||||
if $PYTHON -c "import setuptools" ; then
|
||||
|
||||
EASY_INSTALL_FOUND=1
|
||||
SETUPTOOLS_FOUND=1
|
||||
PYTHONBIN=$PYTHON
|
||||
break
|
||||
|
||||
fi
|
||||
|
||||
done
|
||||
if [ "0" = $EASY_INSTALL_FOUND ]; then
|
||||
if [ "0" = $SETUPTOOLS_FOUND ]; then
|
||||
|
||||
echo "[-] Error: Python setup-tools not found. Run 'sudo apt-get install python-setuptools', or install python3-setuptools, or run '$PYTHONBIN -m ensurepip', or create a virtualenv, or ..."
|
||||
PREREQ_NOTFOUND=1
|
||||
|
@ -45,30 +45,31 @@ MAX_SEG_SIZE = 128 * 1024 * 1024
|
||||
INDEX_FILE_NAME = "_index.json"
|
||||
|
||||
|
||||
#----------------------
|
||||
#---- Helper Functions
|
||||
# ----------------------
|
||||
# ---- Helper Functions
|
||||
|
||||
|
||||
def map_arch():
|
||||
arch = get_arch() # from GEF
|
||||
if 'x86_64' in arch or 'x86-64' in arch:
|
||||
arch = get_arch() # from GEF
|
||||
if "x86_64" in arch or "x86-64" in arch:
|
||||
return "x64"
|
||||
elif 'x86' in arch or 'i386' in arch:
|
||||
elif "x86" in arch or "i386" in arch:
|
||||
return "x86"
|
||||
elif 'aarch64' in arch or 'arm64' in arch:
|
||||
elif "aarch64" in arch or "arm64" in arch:
|
||||
return "arm64le"
|
||||
elif 'aarch64_be' in arch:
|
||||
elif "aarch64_be" in arch:
|
||||
return "arm64be"
|
||||
elif 'armeb' in arch:
|
||||
elif "armeb" in arch:
|
||||
# check for THUMB mode
|
||||
cpsr = get_register('$cpsr')
|
||||
if (cpsr & (1 << 5)):
|
||||
cpsr = get_register("$cpsr")
|
||||
if cpsr & (1 << 5):
|
||||
return "armbethumb"
|
||||
else:
|
||||
return "armbe"
|
||||
elif 'arm' in arch:
|
||||
elif "arm" in arch:
|
||||
# check for THUMB mode
|
||||
cpsr = get_register('$cpsr')
|
||||
if (cpsr & (1 << 5)):
|
||||
cpsr = get_register("$cpsr")
|
||||
if cpsr & (1 << 5):
|
||||
return "armlethumb"
|
||||
else:
|
||||
return "armle"
|
||||
@ -76,8 +77,9 @@ def map_arch():
|
||||
return ""
|
||||
|
||||
|
||||
#-----------------------
|
||||
#---- Dumping functions
|
||||
# -----------------------
|
||||
# ---- Dumping functions
|
||||
|
||||
|
||||
def dump_arch_info():
|
||||
arch_info = {}
|
||||
@ -89,7 +91,7 @@ def dump_regs():
|
||||
reg_state = {}
|
||||
for reg in current_arch.all_registers:
|
||||
reg_val = get_register(reg)
|
||||
reg_state[reg.strip().strip('$')] = reg_val
|
||||
reg_state[reg.strip().strip("$")] = reg_val
|
||||
|
||||
return reg_state
|
||||
|
||||
@ -108,47 +110,76 @@ def dump_process_memory(output_dir):
|
||||
if entry.page_start == entry.page_end:
|
||||
continue
|
||||
|
||||
seg_info = {'start': entry.page_start, 'end': entry.page_end, 'name': entry.path, 'permissions': {
|
||||
"r": entry.is_readable() > 0,
|
||||
"w": entry.is_writable() > 0,
|
||||
"x": entry.is_executable() > 0
|
||||
}, 'content_file': ''}
|
||||
seg_info = {
|
||||
"start": entry.page_start,
|
||||
"end": entry.page_end,
|
||||
"name": entry.path,
|
||||
"permissions": {
|
||||
"r": entry.is_readable() > 0,
|
||||
"w": entry.is_writable() > 0,
|
||||
"x": entry.is_executable() > 0,
|
||||
},
|
||||
"content_file": "",
|
||||
}
|
||||
|
||||
# "(deleted)" may or may not be valid, but don't push it.
|
||||
if entry.is_readable() and not '(deleted)' in entry.path:
|
||||
if entry.is_readable() and not "(deleted)" in entry.path:
|
||||
try:
|
||||
# Compress and dump the content to a file
|
||||
seg_content = read_memory(entry.page_start, entry.size)
|
||||
if(seg_content == None):
|
||||
print("Segment empty: @0x{0:016x} (size:UNKNOWN) {1}".format(entry.page_start, entry.path))
|
||||
if seg_content == None:
|
||||
print(
|
||||
"Segment empty: @0x{0:016x} (size:UNKNOWN) {1}".format(
|
||||
entry.page_start, entry.path
|
||||
)
|
||||
)
|
||||
else:
|
||||
print("Dumping segment @0x{0:016x} (size:0x{1:x}): {2} [{3}]".format(entry.page_start, len(seg_content), entry.path, repr(seg_info['permissions'])))
|
||||
print(
|
||||
"Dumping segment @0x{0:016x} (size:0x{1:x}): {2} [{3}]".format(
|
||||
entry.page_start,
|
||||
len(seg_content),
|
||||
entry.path,
|
||||
repr(seg_info["permissions"]),
|
||||
)
|
||||
)
|
||||
compressed_seg_content = zlib.compress(seg_content)
|
||||
md5_sum = hashlib.md5(compressed_seg_content).hexdigest() + ".bin"
|
||||
seg_info["content_file"] = md5_sum
|
||||
|
||||
# Write the compressed contents to disk
|
||||
out_file = open(os.path.join(output_dir, md5_sum), 'wb')
|
||||
out_file = open(os.path.join(output_dir, md5_sum), "wb")
|
||||
out_file.write(compressed_seg_content)
|
||||
out_file.close()
|
||||
|
||||
except:
|
||||
print("Exception reading segment ({}): {}".format(entry.path, sys.exc_info()[0]))
|
||||
print(
|
||||
"Exception reading segment ({}): {}".format(
|
||||
entry.path, sys.exc_info()[0]
|
||||
)
|
||||
)
|
||||
else:
|
||||
print("Skipping segment {0}@0x{1:016x}".format(entry.path, entry.page_start))
|
||||
print(
|
||||
"Skipping segment {0}@0x{1:016x}".format(entry.path, entry.page_start)
|
||||
)
|
||||
|
||||
# Add the segment to the list
|
||||
final_segment_list.append(seg_info)
|
||||
|
||||
|
||||
return final_segment_list
|
||||
|
||||
#---------------------------------------------
|
||||
#---- ARM Extention (dump floating point regs)
|
||||
|
||||
# ---------------------------------------------
|
||||
# ---- ARM Extention (dump floating point regs)
|
||||
|
||||
|
||||
def dump_float(rge=32):
|
||||
reg_convert = ""
|
||||
if map_arch() == "armbe" or map_arch() == "armle" or map_arch() == "armbethumb" or map_arch() == "armbethumb":
|
||||
if (
|
||||
map_arch() == "armbe"
|
||||
or map_arch() == "armle"
|
||||
or map_arch() == "armbethumb"
|
||||
or map_arch() == "armbethumb"
|
||||
):
|
||||
reg_state = {}
|
||||
for reg_num in range(32):
|
||||
value = gdb.selected_frame().read_register("d" + str(reg_num))
|
||||
@ -158,8 +189,10 @@ def dump_float(rge=32):
|
||||
|
||||
return reg_state
|
||||
|
||||
#----------
|
||||
#---- Main
|
||||
|
||||
# ----------
|
||||
# ---- Main
|
||||
|
||||
|
||||
def main():
|
||||
print("----- Unicorn Context Dumper -----")
|
||||
@ -175,7 +208,9 @@ def main():
|
||||
try:
|
||||
|
||||
# Create the output directory
|
||||
timestamp = datetime.datetime.fromtimestamp(time.time()).strftime('%Y%m%d_%H%M%S')
|
||||
timestamp = datetime.datetime.fromtimestamp(time.time()).strftime(
|
||||
"%Y%m%d_%H%M%S"
|
||||
)
|
||||
output_path = "UnicornContext_" + timestamp
|
||||
if not os.path.exists(output_path):
|
||||
os.makedirs(output_path)
|
||||
@ -190,7 +225,7 @@ def main():
|
||||
}
|
||||
|
||||
# Write the index file
|
||||
index_file = open(os.path.join(output_path, INDEX_FILE_NAME), 'w')
|
||||
index_file = open(os.path.join(output_path, INDEX_FILE_NAME), "w")
|
||||
index_file.write(json.dumps(context, indent=4))
|
||||
index_file.close()
|
||||
print("Done.")
|
||||
@ -198,5 +233,6 @@ def main():
|
||||
except Exception as e:
|
||||
print("!!! ERROR:\n\t{}".format(repr(e)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user