reintegrate gcc_plugin

This commit is contained in:
hexcoder-
2019-10-18 18:01:33 +02:00
parent f4a74a3405
commit b1822f2602
10 changed files with 1406 additions and 11 deletions

2
.gitignore vendored
View File

@ -23,6 +23,8 @@ afl-clang-fast.8
afl-cmin.8 afl-cmin.8
afl-fuzz.8 afl-fuzz.8
afl-gcc.8 afl-gcc.8
afl-gcc-fast.8
afl-g++-fast.8
afl-gotcpu.8 afl-gotcpu.8
afl-plot.8 afl-plot.8
afl-showmap.8 afl-showmap.8

View File

@ -99,6 +99,7 @@ all: test_x86 test_shm test_python27 ready $(PROGS) afl-as test_build all_done
man: $(MANPAGES) man: $(MANPAGES)
-$(MAKE) -C llvm_mode -$(MAKE) -C llvm_mode
-$(MAKE) -C gcc_plugin
tests: source-only tests: source-only
@cd test ; ./test.sh @cd test ; ./test.sh
@ -252,6 +253,7 @@ clean:
rm -f $(PROGS) afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.1.tar.xz afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast *.so unicorn_mode/24f55a7973278f20f0de21b904851d99d4716263.tar.gz *.8 rm -f $(PROGS) afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.1.tar.xz afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast *.so unicorn_mode/24f55a7973278f20f0de21b904851d99d4716263.tar.gz *.8
rm -rf out_dir qemu_mode/qemu-3.1.1 unicorn_mode/unicorn *.dSYM */*.dSYM rm -rf out_dir qemu_mode/qemu-3.1.1 unicorn_mode/unicorn *.dSYM */*.dSYM
-$(MAKE) -C llvm_mode clean -$(MAKE) -C llvm_mode clean
-$(MAKE) -C gcc_plugin clean
$(MAKE) -C libdislocator clean $(MAKE) -C libdislocator clean
$(MAKE) -C libtokencap clean $(MAKE) -C libtokencap clean
$(MAKE) -C qemu_mode/unsigaction clean $(MAKE) -C qemu_mode/unsigaction clean
@ -259,6 +261,7 @@ clean:
distrib: all distrib: all
-$(MAKE) -C llvm_mode -$(MAKE) -C llvm_mode
-$(MAKE) -C gcc_plugin
$(MAKE) -C libdislocator $(MAKE) -C libdislocator
$(MAKE) -C libtokencap $(MAKE) -C libtokencap
cd qemu_mode && sh ./build_qemu_support.sh cd qemu_mode && sh ./build_qemu_support.sh
@ -272,6 +275,7 @@ binary-only: all
source-only: all source-only: all
-$(MAKE) -C llvm_mode -$(MAKE) -C llvm_mode
-$(MAKE) -C gcc_plugin
$(MAKE) -C libdislocator $(MAKE) -C libdislocator
$(MAKE) -C libtokencap $(MAKE) -C libtokencap

View File

@ -71,11 +71,11 @@ tools make fairly broad use of environmental variables:
- Setting AFL_CAL_FAST will speed up the initial calibration, if the - Setting AFL_CAL_FAST will speed up the initial calibration, if the
application is very slow application is very slow
2) Settings for afl-clang-fast / afl-clang-fast++ 2) Settings for afl-clang-fast / afl-clang-fast++ / afl-gcc-fast / afl-g++-fast
------------------------------------------------- ---------------------------------------------------------------------------------
The native LLVM instrumentation helper accepts a subset of the settings The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset
discussed in section #1, with the exception of: of the settings discussed in section #1, with the exception of:
- AFL_AS, since this toolchain does not directly invoke GNU as. - AFL_AS, since this toolchain does not directly invoke GNU as.
@ -100,7 +100,7 @@ Then there are a few specific features that are only available in llvm_mode:
- Setting AFL_LLVM_LAF_SPLIT_COMPARES will split all floating point and - Setting AFL_LLVM_LAF_SPLIT_COMPARES will split all floating point and
64, 32 and 16 bit integer CMP instructions 64, 32 and 16 bit integer CMP instructions
See llvm_mode/README.laf-intel for more information. See llvm_mode/README.laf-intel.md for more information.
WHITELIST WHITELIST
========= =========
@ -109,7 +109,7 @@ Then there are a few specific features that are only available in llvm_mode:
- Setting AFL_LLVM_WHITELIST with a filename will only instrument those - Setting AFL_LLVM_WHITELIST with a filename will only instrument those
files that match the names listed in this file. files that match the names listed in this file.
See llvm_mode/README.whitelist for more information. See llvm_mode/README.whitelist.md for more information.
INSTRIM INSTRIM
======= =======
@ -122,7 +122,7 @@ Then there are a few specific features that are only available in llvm_mode:
afl-fuzz will only be able to see the path the loop took, but not how afl-fuzz will only be able to see the path the loop took, but not how
many times it was called (unless it is a complex loop). many times it was called (unless it is a complex loop).
See llvm_mode/README.instrim See llvm_mode/README.instrim.md
NOT_ZERO NOT_ZERO
======== ========
@ -133,7 +133,18 @@ Then there are a few specific features that are only available in llvm_mode:
slowdown due a performance issue that is only fixed in llvm 9+. slowdown due a performance issue that is only fixed in llvm 9+.
This feature increases path discovery by a little bit. This feature increases path discovery by a little bit.
See llvm_mode/README.neverzero See llvm_mode/README.neverzero.md
Then there are a few specific features that are only available in the gcc_plugin:
WHITELIST
=========
This feature allows selective instrumentation of the source
- Setting AFL_GCC_WHITELIST with a filename will only instrument those
files that match the names listed in this file (one filename per line).
See gcc_plugin/README.whitelist.md for more information.
3) Settings for afl-fuzz 3) Settings for afl-fuzz
------------------------ ------------------------
@ -257,7 +268,7 @@ The QEMU wrapper used to instrument binary-only code supports several settings:
- Setting AFL_COMPCOV_LEVEL enables the CompareCoverage tracing of all cmp - Setting AFL_COMPCOV_LEVEL enables the CompareCoverage tracing of all cmp
and sub in x86 and x86_64 and memory comparions functions (e.g. strcmp, and sub in x86 and x86_64 and memory comparions functions (e.g. strcmp,
memcmp, ...) when libcompcov is preloaded using AFL_PRELOAD. memcmp, ...) when libcompcov is preloaded using AFL_PRELOAD.
More info at qemu_mode/libcompcov/README.compcov. More info at qemu_mode/libcompcov/README.md.
There are two levels at the moment, AFL_COMPCOV_LEVEL=1 that instruments There are two levels at the moment, AFL_COMPCOV_LEVEL=1 that instruments
only comparisons with immediate values / read-only memory and only comparisons with immediate values / read-only memory and
AFL_COMPCOV_LEVEL=2 that instruments all the comparions. Level 2 is more AFL_COMPCOV_LEVEL=2 that instruments all the comparions. Level 2 is more
@ -318,7 +329,7 @@ of decimal.
8) Settings for libdislocator.so 8) Settings for libdislocator.so
-------------------------------- --------------------------------
The library honors three environmental variables: The library honors these environmental variables:
- AFL_LD_LIMIT_MB caps the size of the maximum heap usage permitted by the - AFL_LD_LIMIT_MB caps the size of the maximum heap usage permitted by the
library, in megabytes. The default value is 1 GB. Once this is exceeded, library, in megabytes. The default value is 1 GB. Once this is exceeded,

130
gcc_plugin/Makefile Normal file
View File

@ -0,0 +1,130 @@
#
# american fuzzy lop - GCC plugin instrumentation
# -----------------------------------------------
#
# Written by Austin Seipp <aseipp@pobox.com> and
# Laszlo Szekeres <lszekeres@google.com> and
# Michal Zalewski <lcamtuf@google.com> and
# Heiko Eißfeldt <heiko@hexco.de>
#
# GCC integration design is based on the LLVM design, which comes
# from Laszlo Szekeres.
#
# Copyright 2015 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
PREFIX ?= /usr/local
HELPER_PATH = $(PREFIX)/lib/afl
BIN_PATH = $(PREFIX)/bin
CFLAGS ?= -O3 -g -funroll-loops
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -Wno-pointer-sign \
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
CXXFLAGS ?= -O3 -g -funroll-loops
CXXEFLAGS := $(CXXFLAGS) -Wall -D_FORTIFY_SOURCE=2
CC ?= gcc
CXX ?= g++
PLUGIN_FLAGS = -fPIC -fno-rtti -I"$(shell $(CC) -print-file-name=plugin)/include"
PROGS = ../afl-gcc-fast ../afl-gcc-pass.so ../afl-gcc-rt.o
ifeq "$(shell echo '\#include <sys/ipc.h>@\#include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 )" "1"
SHMAT_OK=1
else
SHMAT_OK=0
CFLAGS+=-DUSEMMAP=1
LDFLAGS += -lrt
endif
ifeq "$(TEST_MMAP)" "1"
SHMAT_OK=0
CFLAGS+=-DUSEMMAP=1
LDFLAGS += -lrt
endif
all: test_shm test_deps $(PROGS) afl-gcc-fast.8 test_build all_done
ifeq "$(SHMAT_OK)" "1"
test_shm:
@echo "[+] shmat seems to be working."
@rm -f .test2
else
test_shm:
@echo "[-] shmat seems not to be working, switching to mmap implementation"
endif
test_deps:
@echo "[*] Checking for working '$(CC)'..."
@which $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
@echo "[*] Checking for gcc for plugin support..."
@$(CC) -v 2>&1 | grep -q -- --enable-plugin || ( echo "[-] Oops, this gcc has not been configured with plugin support."; exit 1 )
@echo "[*] Checking for gcc plugin development header files..."
@test -d `$(CC) -print-file-name=plugin`/include || ( echo "[-] Oops, can't find gcc header files. Be sure to install 'gcc-X-plugin-dev'."; exit 1 )
@echo "[*] Checking for '../afl-showmap'..."
@test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 )
@echo "[+] All set and ready to build."
../afl-gcc-fast: afl-gcc-fast.c | test_deps
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
ln -sf afl-gcc-fast ../afl-g++-fast
../afl-gcc-pass.so: afl-gcc-pass.so.cc | test_deps
$(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@
../afl-gcc-rt.o: afl-gcc-rt.o.c | test_deps
$(CC) $(CFLAGS) -fPIC -c $< -o $@
test_build: $(PROGS)
@echo "[*] Testing the CC wrapper and instrumentation output..."
unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
# unset AFL_USE_ASAN AFL_USE_MSAN; AFL_INST_RATIO=100 AFL_PATH=. AFL_CC=$(CC) ../afl-gcc-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
../afl-showmap -m none -q -o .test-instr0 ./test-instr </dev/null
echo 1 | ../afl-showmap -m none -q -o .test-instr1 ./test-instr
@rm -f test-instr
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/vanhauser-thc/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
@echo "[+] All right, the instrumentation seems to be working!"
all_done: test_build
@echo "[+] All done! You can now use '../afl-gcc-fast' to compile programs."
.NOTPARALLEL: clean
vpath % ..
%.8: %
@echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ../$@
@echo .SH NAME >> ../$@
@echo .B $* >> ../$@
@echo >> ../$@
@echo .SH SYNOPSIS >> ../$@
@../$* -h 2>&1 | head -n 3 | tail -n 1 | sed 's/^\.\///' >> ../$@
@echo >> ../$@
@echo .SH OPTIONS >> ../$@
@echo .nf >> ../$@
@../$* -h 2>&1 | tail -n +4 >> ../$@
@echo >> ../$@
@echo .SH AUTHOR >> ../$@
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de> and Andrea Fioraldi <andreafioraldi@gmail.com>" >> ../$@
@echo The homepage of afl++ is: https://github.com/vanhauser-thc/AFLplusplus >> ../$@
@echo >> ../$@
@echo .SH LICENSE >> ../$@
@echo Apache License Version 2.0, January 2004 >> ../$@
ln -sf afl-gcc-fast.8 ../afl-g++-fast.8
clean:
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1
rm -f $(PROGS) ../afl-g++-fast ../afl-g*-fast.8

162
gcc_plugin/README.gcc.md Normal file
View File

@ -0,0 +1,162 @@
===========================================
GCC-based instrumentation for afl-fuzz
======================================
(See ../docs/README.md for the general instruction manual.)
(See ../llvm_mode/README.md for the LLVM-based instrumentation.)
!!! TODO items are:
!!! => inline instrumentation has to work!
!!!
## 1) Introduction
The code in this directory allows you 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:
- The compiler can make many optimizations that are hard to pull off when
manually inserting assembly. As a result, some slow, CPU-bound programs will
run up to around faster.
The gains are less pronounced for fast binaries, where the speed is limited
chiefly by the cost of creating new processes. In such cases, the gain will
probably stay within 10%.
- 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).
- 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).
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
co-exists with the original code.
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
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. 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...]
make
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
../docs/env_variables.txt). 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
directory.
## 3) Gotchas, feedback, bugs
This is an early-stage mechanism, so field reports are welcome. You can send bug
reports to <hexcoder-@github.com>.
## 4) Bonus feature #1: deferred initialization
AFL tries to optimize performance by executing the targeted binary just once,
stopping it just before main(), and then cloning this "master" process to get
a steady supply of targets to fuzz.
Although this approach eliminates much of the OS-, linker- and libc-level
costs of executing the program, it does not always help with binaries that
perform other time-consuming initialization steps - say, parsing a large config
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
fairly simple way.
First, locate a suitable location in the code where the delayed cloning can
take place. This needs to be done with *extreme* care to avoid breaking the
binary. In particular, the program will probably malfunction if you select
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 creation of temporary files, network sockets, offset-sensitive file
descriptors, and similar shared-state resources - but only provided that
their state meaningfully influences the behavior of the program later on.
- Any access to the fuzzed input, including reading the metadata about its
size.
With the location selected, add this code in the appropriate spot:
```
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
```
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.
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!
## 5) Bonus feature #2: persistent mode
Some libraries provide APIs that are stateless, or whose state can be reset in
between processing different input files. When such a reset is performed, a
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.
The basic structure of the program that does this would be:
```
while (__AFL_LOOP(1000)) {
/* Read input data. */
/* Call library code to be fuzzed. */
/* Reset state. */
}
/* Exit normally */
```
The numerical value specified within the loop controls the maximum number
of iterations before AFL will restart the process from scratch. This minimizes
the impact of memory leaks and similar glitches; 1000 is a good starting point.
A more detailed template is shown in ../experimental/persistent_demo/.
Similarly to the previous mode, the feature works only with afl-gcc-fast or
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 reset the critical state fully, you may end up with false positives or
waste a whole lot of CPU power doing nothing useful at all. Be particularly
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.
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,
should be a lot more robust.

View File

@ -0,0 +1,74 @@
========================================
Using afl++ with partial instrumentation
========================================
This file describes how you can selectively instrument only the source files
that are interesting to you using the gcc instrumentation provided by
afl++.
Originally developed by Christian Holler (:decoder) <choller@mozilla.com>,
adapted to gcc plugin by hexcoder-.
## 1) Description and purpose
When building and testing complex programs where only a part of the program is
the fuzzing target, it often helps to only instrument the necessary parts of
the program, leaving the rest uninstrumented. This helps to focus the fuzzer
on the important parts of the program, avoiding undesired noise and
disturbance by uninteresting code being exercised.
For this purpose, I have added a "partial instrumentation" support to the gcc
plugin of AFLFuzz that allows you to specify on a source file level which files
should be compiled with or without instrumentation.
## 2) Building the gcc plugin
The new code is part of the existing afl++ gcc plugin in the gcc_plugin/
subdirectory. There is nothing specifically to do :)
## 3) How to use the partial instrumentation mode
In order to build with partial instrumentation, you need to build with
afl-gcc-fast and afl-g++-fast respectively. The only required change is
that you need to set the environment variable AFL_GCC_WHITELIST when calling
the compiler.
The environment variable must point to a file containing all the filenames
that should be instrumented. For matching, the filename that is being compiled
must end in the filename entry contained in this whitelist (to avoid breaking
the matching when absolute paths are used during compilation).
For example if your source tree looks like this:
```
project/
project/feature_a/a1.cpp
project/feature_a/a2.cpp
project/feature_b/b1.cpp
project/feature_b/b2.cpp
```
and you only want to test feature_a, then create a whitelist file containing:
```
feature_a/a1.cpp
feature_a/a2.cpp
```
However if the whitelist file contains only this, it works as well:
```
a1.cpp
a2.cpp
```
but it might lead to files being unwantedly instrumented if the same filename
exists somewhere else in the project directories.
The created whitelist file is then set to AFL_GCC_WHITELIST when you compile
your program. For each file that didn't match the whitelist, the compiler will
issue a warning at the end stating that no blocks were instrumented. If you
didn't intend to instrument that file, then you can safely ignore that warning.

283
gcc_plugin/afl-gcc-fast.c Normal file
View File

@ -0,0 +1,283 @@
/*
american fuzzy lop - GCC wrapper for GCC plugin
------------------------------------------------
Written by Austin Seipp <aseipp@pobox.com> and
Laszlo Szekeres <lszekeres@google.com> and
Michal Zalewski <lcamtuf@google.com>
GCC integration design is based on the LLVM design, which comes
from Laszlo Szekeres.
Copyright 2015 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
This program is a drop-in replacement for gcc, similar in most
respects to ../afl-gcc, but with compiler instrumentation through a
plugin. It tries to figure out compilation mode, adds a bunch of
flags, and then calls the real compiler.
*/
#define AFL_MAIN
#include "../config.h"
#include "../types.h"
#include "../include/debug.h"
#include "../include/alloc-inl.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
static u8* obj_path; /* Path to runtime libraries */
static u8** cc_params; /* Parameters passed to the real CC */
static u32 cc_par_cnt = 1; /* Param count, including argv0 */
/* Try to find the runtime libraries. If that fails, abort. */
static void find_obj(u8* argv0) {
u8 *afl_path = getenv("AFL_PATH");
u8 *slash, *tmp;
if (afl_path) {
tmp = alloc_printf("%s/afl-gcc-rt.o", afl_path);
if (!access(tmp, R_OK)) {
obj_path = afl_path;
ck_free(tmp);
return;
}
ck_free(tmp);
}
slash = strrchr(argv0, '/');
if (slash) {
u8 *dir;
*slash = 0;
dir = ck_strdup(argv0);
*slash = '/';
tmp = alloc_printf("%s/afl-gcc-rt.o", dir);
if (!access(tmp, R_OK)) {
obj_path = dir;
ck_free(tmp);
return;
}
ck_free(tmp);
ck_free(dir);
}
if (!access(AFL_PATH "/afl-gcc-rt.o", R_OK)) {
obj_path = AFL_PATH;
return;
}
FATAL("Unable to find 'afl-gcc-rt.o' or 'afl-gcc-pass.so'. Please set AFL_PATH");
}
/* Copy argv to cc_params, making the necessary edits. */
static void edit_params(u32 argc, char** argv) {
u8 fortify_set = 0, asan_set = 0, x_set = 0, maybe_linking = 1;
u8 *name;
cc_params = ck_alloc((argc + 64) * sizeof(u8*));
name = strrchr(argv[0], '/');
if (!name) name = argv[0]; else name++;
if (!strcmp(name, "afl-g++-fast")) {
u8* alt_cxx = getenv("AFL_CXX");
cc_params[0] = alt_cxx ? alt_cxx : (u8*)"g++";
} else {
u8* alt_cc = getenv("AFL_CC");
cc_params[0] = alt_cc ? alt_cc : (u8*)"gcc";
}
char* fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path);
cc_params[cc_par_cnt++] = fplugin_arg;
while (--argc) {
u8* cur = *(++argv);
#if defined(__x86_64__)
if (!strcmp(cur, "-m32")) FATAL("-m32 is not supported");
#endif
if (!strcmp(cur, "-x")) x_set = 1;
if (!strcmp(cur, "-c") || !strcmp(cur, "-S") || !strcmp(cur, "-E") ||
!strcmp(cur, "-v")) maybe_linking = 0;
if (!strcmp(cur, "-fsanitize=address") ||
!strcmp(cur, "-fsanitize=memory")) asan_set = 1;
if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1;
cc_params[cc_par_cnt++] = cur;
}
if (getenv("AFL_HARDEN")) {
cc_params[cc_par_cnt++] = "-fstack-protector-all";
if (!fortify_set)
cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2";
}
if (!asan_set) {
if (getenv("AFL_USE_ASAN")) {
cc_params[cc_par_cnt++] = "-fsanitize=address";
if (getenv("AFL_USE_MSAN"))
FATAL("ASAN and MSAN are mutually exclusive");
} else if (getenv("AFL_USE_MSAN")) {
cc_params[cc_par_cnt++] = "-fsanitize=memory";
if (getenv("AFL_USE_ASAN"))
FATAL("ASAN and MSAN are mutually exclusive");
}
}
if (!getenv("AFL_DONT_OPTIMIZE")) {
cc_params[cc_par_cnt++] = "-g";
cc_params[cc_par_cnt++] = "-O3";
cc_params[cc_par_cnt++] = "-funroll-loops";
}
cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
/* When the user tries to use persistent or deferred forkserver modes by
appending a single line to the program, we want to reliably inject a
signature into the binary (to be picked up by afl-fuzz) and we want
to call a function from the runtime .o file. This is unnecessarily
painful for three reasons:
1) We need to convince the compiler not to optimize out the signature.
This is done with __attribute__((used)).
2) We need to convince the linker, when called with -Wl,--gc-sections,
not to do the same. This is done by forcing an assignment to a
'volatile' pointer.
3) We need to declare __afl_persistent_loop() in the global namespace,
but doing this within a method in a class is hard - :: and extern "C"
are forbidden and __attribute__((alias(...))) doesn't work. Hence the
__asm__ aliasing trick.
*/
cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)="
"({ static volatile char *_B __attribute__((used)); "
" _B = (char*)\"" PERSIST_SIG "\"; "
#ifdef __APPLE__
"int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); "
#else
"int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); "
#endif /* ^__APPLE__ */
"_L(_A); })";
cc_params[cc_par_cnt++] = "-D__AFL_INIT()="
"do { static volatile char *_A __attribute__((used)); "
" _A = (char*)\"" DEFER_SIG "\"; "
#ifdef __APPLE__
"void _I(void) __asm__(\"___afl_manual_init\"); "
#else
"void _I(void) __asm__(\"__afl_manual_init\"); "
#endif /* ^__APPLE__ */
"_I(); } while (0)";
if (maybe_linking) {
if (x_set) {
cc_params[cc_par_cnt++] = "-x";
cc_params[cc_par_cnt++] = "none";
}
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-gcc-rt.o", obj_path);
}
cc_params[cc_par_cnt] = NULL;
}
/* Main entry point */
int main(int argc, char** argv) {
if (isatty(2) && !getenv("AFL_QUIET")) {
SAYF(cCYA "afl-gcc-fast " cBRI VERSION cRST " initially by <aseipp@pobox.com>, maintainer: hexcoder-\n");
}
if (argc < 2) {
SAYF("\n"
"This is a helper application for afl-fuzz. It serves as a drop-in replacement\n"
"for gcc, letting you recompile third-party code with the required runtime\n"
"instrumentation. A common use pattern would be one of the following:\n\n"
" CC=%s/afl-gcc-fast ./configure\n"
" CXX=%s/afl-g++-fast ./configure\n\n"
"In contrast to the traditional afl-gcc tool, this version is implemented as\n"
"a GCC plugin and tends to offer improved performance with slow programs\n"
"(similarly to the LLVM plugin used by afl-clang-fast).\n\n"
"You can specify custom next-stage toolchain via AFL_CC and AFL_CXX. Setting\n"
"AFL_HARDEN enables hardening optimizations in the compiled code.\n\n",
BIN_PATH, BIN_PATH);
exit(1);
}
find_obj(argv[0]);
edit_params(argc, argv);
/*if (isatty(2) && !getenv("AFL_QUIET")) {
printf("Calling \"%s\" with:\n", cc_params[0]);
for(int i=1; i<cc_par_cnt; i++) printf("%s\n", cc_params[i]);
}
*/
execvp(cc_params[0], (char**)cc_params);
FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]);
return 0;
}

View File

@ -0,0 +1,505 @@
//
// There are some TODOs in this file:
// - fix instrumentation via external call
// - fix inline instrumentation
// - implement whitelist feature
// - dont instrument blocks that are uninteresting
// - implement neverZero
//
/*
american fuzzy lop - GCC instrumentation pass
---------------------------------------------
Written by Austin Seipp <aseipp@pobox.com> with bits from
Emese Revfy <re.emese@gmail.com>
Fixed by Heiko Eißfeldt 2019 for AFL++
GCC integration design is based on the LLVM design, which comes
from Laszlo Szekeres. Some of the boilerplate code below for
afl_pass to adapt to different GCC versions was taken from Emese
Revfy's Size Overflow plugin for GCC, licensed under the GPLv2/v3.
(NOTE: this plugin code is under GPLv3, in order to comply with the
GCC runtime library exception, which states that you may distribute
"Target Code" from the compiler under a license of your choice, as
long as the "Compilation Process" is "Eligible", and contains no
GPL-incompatible software in GCC "during the process of
transforming high level code to target code". In this case, the
plugin will be used to generate "Target Code" during the
"Compilation Process", and thus it must be GPLv3 to be "eligible".)
Copyright (C) 2015 Austin Seipp
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define BUILD_INLINE_INST
#include "../config.h"
#include "../include/debug.h"
/* clear helper AFL types pulls in, which intervene with gcc-plugin geaders from GCC-8 */
#ifdef likely
#undef likely
#endif
#ifdef unlikely
#undef unlikely
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <list>
#include <string>
#include <fstream>
#include <gcc-plugin.h>
#include <plugin-version.h>
#include <diagnostic.h>
#include <tree.h>
#include <tree-ssa.h>
#include <tree-pass.h>
#include <tree-ssa-alias.h>
#include <basic-block.h>
#include <gimple-expr.h>
#include <gimple.h>
#include <gimple-iterator.h>
#include <gimple-ssa.h>
#include <version.h>
#include <toplev.h>
#include <intl.h>
#include <context.h>
#include <stringpool.h>
#include <cgraph.h>
#include <cfgloop.h>
/* -------------------------------------------------------------------------- */
/* -- AFL instrumentation pass ---------------------------------------------- */
static int be_quiet = 0;
static unsigned int inst_ratio = 100;
static bool inst_ext = true;
static std::list<std::string> myWhitelist;
static unsigned int ext_call_instrument(function *fun) {
/* Instrument all the things! */
basic_block bb;
unsigned finst_blocks = 0;
unsigned fcnt_blocks = 0;
tree fntype = build_function_type_list(
void_type_node, /* return */
uint32_type_node, /* args */
NULL_TREE); /* done */
tree fndecl = build_fn_decl("__afl_trace", fntype);
TREE_STATIC(fndecl) = 1; /* Defined elsewhere */
TREE_PUBLIC(fndecl) = 1; /* Public */
DECL_EXTERNAL(fndecl) = 1; /* External linkage */
DECL_ARTIFICIAL(fndecl) = 1; /* Injected by compiler */
FOR_EACH_BB_FN(bb, fun) {
gimple_seq fcall;
gimple_seq seq = NULL;
gimple_stmt_iterator bentry;
++fcnt_blocks;
// only instrument if this basic block is the destination of a previous
// basic block that has multiple successors
// this gets rid of ~5-10% of instrumentations that are unnecessary
// result: a little more speed and less map pollution
int more_than_one = -1;
edge ep;
edge_iterator eip;
FOR_EACH_EDGE (ep, eip, bb->preds) {
int count = 0;
if (more_than_one == -1)
more_than_one = 0;
basic_block Pred = ep->src;
edge es;
edge_iterator eis;
FOR_EACH_EDGE (es, eis, Pred->succs) {
basic_block Succ = es->dest;
if (Succ != NULL) count++;
}
if (count > 1)
more_than_one = 1;
}
if (more_than_one != 1)
continue;
/* Bail on this block if we trip the specified ratio */
if (R(100) >= inst_ratio) continue;
/* Make up cur_loc */
unsigned int rand_loc = R(MAP_SIZE);
tree cur_loc = build_int_cst(uint32_type_node, rand_loc);
/* Update bitmap via external call */
/* to quote:
* /+ Trace a basic block with some ID +/
* void __afl_trace(u32 x);
*/
fcall = gimple_build_call(fndecl, 1, cur_loc); /* generate the function _call_ to above built reference, with *1* parameter -> the random const for the location */
gimple_seq_add_stmt(&seq, fcall); /* and insert into a sequence */
/* Done - grab the entry to the block and insert sequence */
bentry = gsi_after_labels(bb);
gsi_insert_seq_before(&bentry, seq, GSI_SAME_STMT);
++finst_blocks;
}
/* Say something nice. */
if (!be_quiet) {
if (!finst_blocks)
WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST ),
function_name(fun));
else if (finst_blocks < fcnt_blocks)
OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST ),
finst_blocks, fcnt_blocks,
function_name(fun));
else
OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST ),
finst_blocks,
function_name(fun));
}
return 0;
}
static unsigned int inline_instrument(function *fun) {
/* Instrument all the things! */
basic_block bb;
unsigned finst_blocks = 0;
unsigned fcnt_blocks = 0;
/* Set up global type declarations */
tree map_type = build_pointer_type(unsigned_char_type_node);
tree map_ptr_g = build_decl(UNKNOWN_LOCATION, VAR_DECL,
get_identifier_with_length("__afl_area_ptr", 14), map_type);
TREE_USED(map_ptr_g) = 1;
TREE_STATIC(map_ptr_g) = 1; /* Defined elsewhere */
DECL_EXTERNAL(map_ptr_g) = 1; /* External linkage */
DECL_PRESERVE_P(map_ptr_g) = 1;
DECL_ARTIFICIAL(map_ptr_g) = 1; /* Injected by compiler */
rest_of_decl_compilation(map_ptr_g, 1, 0);
tree prev_loc_g = build_decl(UNKNOWN_LOCATION, VAR_DECL,
get_identifier_with_length("__afl_prev_loc", 14), uint32_type_node);
TREE_USED(prev_loc_g) = 1;
TREE_STATIC(prev_loc_g) = 1; /* Defined elsewhere */
DECL_EXTERNAL(prev_loc_g) = 1; /* External linkage */
DECL_PRESERVE_P(prev_loc_g) = 1;
DECL_ARTIFICIAL(prev_loc_g) = 1; /* Injected by compiler */
rest_of_decl_compilation(prev_loc_g, 1, 0);
FOR_EACH_BB_FN(bb, fun) {
gimple_seq seq = NULL;
gimple_stmt_iterator bentry;
++fcnt_blocks;
// only instrument if this basic block is the destination of a previous
// basic block that has multiple successors
// this gets rid of ~5-10% of instrumentations that are unnecessary
// result: a little more speed and less map pollution
int more_than_one = -1;
edge ep;
edge_iterator eip;
FOR_EACH_EDGE (ep, eip, bb->preds) {
int count = 0;
if (more_than_one == -1)
more_than_one = 0;
basic_block Pred = ep->src;
edge es;
edge_iterator eis;
FOR_EACH_EDGE (es, eis, Pred->succs) {
basic_block Succ = es->dest;
if (Succ != NULL) count++;
}
if (count > 1)
more_than_one = 1;
}
if (more_than_one != 1)
continue;
/* Bail on this block if we trip the specified ratio */
if (R(100) >= inst_ratio) continue;
/* Make up cur_loc */
unsigned int rand_loc = R(MAP_SIZE);
tree cur_loc = build_int_cst(uint32_type_node, rand_loc);
/* Load prev_loc, xor with cur_loc */
// gimple_assign <var_decl, prev_loc.0_1, prev_loc, NULL, NULL>
tree prev_loc = create_tmp_var_raw(uint32_type_node, "prev_loc");
gassign *g = gimple_build_assign(prev_loc, VAR_DECL, prev_loc_g);
gimple_seq_add_stmt(&seq, g); // load prev_loc
update_stmt(g);
// gimple_assign <bit_xor_expr, _2, prev_loc.0_1, 47231, NULL>
tree area_off = create_tmp_var_raw(uint32_type_node, "area_off");
g = gimple_build_assign(area_off, BIT_XOR_EXPR, prev_loc, cur_loc);
gimple_seq_add_stmt(&seq, g); // area_off = prev_loc ^ cur_loc
update_stmt(g);
/* Update bitmap */
tree one = build_int_cst(unsigned_char_type_node, 1);
// tree zero = build_int_cst(unsigned_char_type_node, 0);
// gimple_assign <addr_expr, p_6, &map[_2], NULL, NULL>
tree map_ptr = create_tmp_var(map_type, "map_ptr");
tree map_ptr2 = create_tmp_var(map_type, "map_ptr2");
g = gimple_build_assign(map_ptr, map_ptr_g);
gimple_seq_add_stmt(&seq, g); // map_ptr = __afl_area_ptr
update_stmt(g);
#if 0
tree addr = build2(ADDR_EXPR, map_type, map_ptr, area_off);
g = gimple_build_assign(map_ptr2, MODIFY_EXPR, addr);
gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off
update_stmt(g);
#else
g = gimple_build_assign(map_ptr2, PLUS_EXPR, map_ptr, area_off);
gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off
update_stmt(g);
#endif
// gimple_assign <mem_ref, _3, *p_6, NULL, NULL>
tree tmp1 = create_tmp_var_raw(unsigned_char_type_node, "tmp1");
g = gimple_build_assign(tmp1, MEM_REF, map_ptr2);
gimple_seq_add_stmt(&seq, g); // tmp1 = *map_ptr2
update_stmt(g);
// gimple_assign <plus_expr, _4, _3, 1, NULL>
tree tmp2 = create_tmp_var_raw(unsigned_char_type_node, "tmp2");
g = gimple_build_assign(tmp2, PLUS_EXPR, tmp1, one);
gimple_seq_add_stmt(&seq, g); // tmp2 = tmp1 + 1
update_stmt(g);
// TODO: neverZero: here we have to check if tmp3 == 0
// and add 1 if so
// gimple_assign <ssa_name, *p_6, _4, NULL, NULL>
// tree map_ptr3 = create_tmp_var_raw(map_type, "map_ptr3");
g = gimple_build_assign(map_ptr_g, INDIRECT_REF, tmp2);
gimple_seq_add_stmt(&seq, g); // *map_ptr3 = tmp2
update_stmt(g);
/* Set prev_loc to cur_loc >> 1 */
// gimple_assign <integer_cst, prev_loc, 23615, NULL, NULL>
tree shifted_loc = build_int_cst(TREE_TYPE(prev_loc_g), rand_loc >> 1);
tree prev_loc2 = create_tmp_var_raw(uint32_type_node, "prev_loc2");
g = gimple_build_assign(prev_loc2, shifted_loc);
gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1
update_stmt(g);
g = gimple_build_assign(prev_loc_g, prev_loc2);
gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1
update_stmt(g);
/* Done - grab the entry to the block and insert sequence */
bentry = gsi_after_labels(bb);
gsi_insert_seq_before(&bentry, seq, GSI_NEW_STMT);
++finst_blocks;
}
/* Say something nice. */
if (!be_quiet) {
if (!finst_blocks)
WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST ),
function_name(fun));
else if (finst_blocks < fcnt_blocks)
OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST ),
finst_blocks, fcnt_blocks,
function_name(fun));
else
OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST ),
finst_blocks,
function_name(fun));
}
return 0;
}
/* -------------------------------------------------------------------------- */
/* -- Boilerplate and initialization ---------------------------------------- */
static const struct pass_data afl_pass_data = {
.type = GIMPLE_PASS,
.name = "afl-inst",
.optinfo_flags = OPTGROUP_NONE,
.tv_id = TV_NONE,
.properties_required = 0,
.properties_provided = 0,
.properties_destroyed = 0,
.todo_flags_start = 0,
// NOTE(aseipp): it's very, very important to include
// at least 'TODO_update_ssa' here so that GCC will
// properly update the resulting SSA form, e.g., to
// include new PHI nodes for newly added symbols or
// names. Do not remove this. Do not taunt Happy Fun
// Ball.
.todo_flags_finish = TODO_update_ssa | TODO_verify_il | TODO_cleanup_cfg,
};
namespace {
class afl_pass : public gimple_opt_pass {
private:
bool do_ext_call;
public:
afl_pass(bool ext_call, gcc::context *g) : gimple_opt_pass(afl_pass_data, g), do_ext_call(ext_call) {}
virtual unsigned int execute(function *fun) {
if (!myWhitelist.empty()) {
bool instrumentBlock = false;
/* EXPR_FILENAME
This macro returns the name of the file in which the entity was declared, as
a char*. For an entity declared implicitly by the compiler (like __builtin_
memcpy), this will be the string "<internal>".
*/
const char *fname = DECL_SOURCE_FILE(fun->decl);
if (0 != strncmp("<internal>", fname, 10)
&& 0 != strncmp("<built-in>", fname, 10))
{
std::string instFilename(fname);
/* Continue only if we know where we actually are */
if (!instFilename.empty()) {
for (std::list<std::string>::iterator it = myWhitelist.begin(); it != myWhitelist.end(); ++it) {
/* We don't check for filename equality here because
* filenames might actually be full paths. Instead we
* check that the actual filename ends in the filename
* specified in the list. */
if (instFilename.length() >= it->length()) {
if (instFilename.compare(instFilename.length() - it->length(), it->length(), *it) == 0) {
instrumentBlock = true;
break;
}
}
}
}
}
/* Either we couldn't figure out our location or the location is
* not whitelisted, so we skip instrumentation. */
if (!instrumentBlock) return 0;;
}
return do_ext_call ? ext_call_instrument(fun) : inline_instrument(fun);
}
}; /* class afl_pass */
} /* anon namespace */
static struct opt_pass *make_afl_pass(bool ext_call, gcc::context *ctxt) {
return new afl_pass(ext_call, ctxt);
}
/* -------------------------------------------------------------------------- */
/* -- Initialization -------------------------------------------------------- */
int plugin_is_GPL_compatible = 1;
static struct plugin_info afl_plugin_info = {
.version = "20191015",
.help = "AFL++ gcc plugin\n",
};
int plugin_init(struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version) {
struct register_pass_info afl_pass_info;
struct timeval tv;
struct timezone tz;
u32 rand_seed;
/* Setup random() so we get Actually Random(TM) outputs from R() */
gettimeofday(&tv, &tz);
rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
srandom(rand_seed);
/* Pass information */
afl_pass_info.pass = make_afl_pass(inst_ext, g);
afl_pass_info.reference_pass_name = "ssa";
afl_pass_info.ref_pass_instance_number = 1;
afl_pass_info.pos_op = PASS_POS_INSERT_AFTER;
if (!plugin_default_version_check(version, &gcc_version)) {
FATAL(G_("Incompatible gcc/plugin versions!"));
}
/* Show a banner */
if (isatty(2) && !getenv("AFL_QUIET")) {
SAYF(G_(cCYA "afl-gcc-pass " cBRI VERSION cRST " initially by <aseipp@pobox.com>, maintainer: hexcoder-\n"));
} else
be_quiet = 1;
/* Decide instrumentation ratio */
char* inst_ratio_str = getenv("AFL_INST_RATIO");
if (inst_ratio_str) {
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || inst_ratio > 100)
FATAL(G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)"));
else {
if (!be_quiet)
ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."),
inst_ext ? G_("Call-based") : G_("Inline"),
inst_ratio,
getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened"));
}
}
char* instWhiteListFilename = getenv("AFL_GCC_WHITELIST");
if (instWhiteListFilename) {
std::string line;
std::ifstream fileStream;
fileStream.open(instWhiteListFilename);
if (!fileStream)
fatal_error(0, "Unable to open AFL_GCC_WHITELIST");
getline(fileStream, line);
while (fileStream) {
myWhitelist.push_back(line);
getline(fileStream, line);
}
} else if (!be_quiet && getenv("AFL_LLVM_WHITELIST"))
SAYF(cYEL "[-] " cRST "AFL_LLVM_WHITELIST environment variable detected - did you mean AFL_GCC_WHITELIST?\n");
/* Go go gadget */
register_callback(plugin_info->base_name, PLUGIN_INFO, NULL, &afl_plugin_info);
register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &afl_pass_info);
return 0;
}

224
gcc_plugin/afl-gcc-rt.o.c Normal file
View File

@ -0,0 +1,224 @@
/*
american fuzzy lop - GCC plugin instrumentation bootstrap
---------------------------------------------------------
Written by Austin Seipp <aseipp@pobox.com> and
Laszlo Szekeres <lszekeres@google.com> and
Michal Zalewski <lcamtuf@google.com>
GCC integration design is based on the LLVM design, which comes
from Laszlo Szekeres.
Copyright 2015 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
This code is the rewrite of afl-as.h's main_payload.
*/
#include "../config.h"
#include "../types.h"
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/types.h>
/* Globals needed by the injected instrumentation. The __afl_area_initial region
is used for instrumentation output before __afl_map_shm() has a chance to run.
It will end up as .comm, so it shouldn't be too wasteful. */
u8 __afl_area_initial[MAP_SIZE];
u8* __afl_area_ptr = __afl_area_initial;
u32 __afl_prev_loc;
/* Running in persistent mode? */
static u8 is_persistent;
/* Trace a basic block with some ID */
void __afl_trace(u32 x) {
u32 l = __afl_prev_loc;
u32 n = l ^ x;
*(__afl_area_ptr+n) += 1;
__afl_prev_loc = (x >> 1);
return;
}
/* SHM setup. */
static void __afl_map_shm(void) {
u8 *id_str = getenv(SHM_ENV_VAR);
/* If we're running under AFL, attach to the appropriate region, replacing the
early-stage __afl_area_initial region that is needed to allow some really
hacky .init code to work correctly in projects such as OpenSSL. */
if (id_str) {
u32 shm_id = atoi(id_str);
__afl_area_ptr = shmat(shm_id, NULL, 0);
/* Whooooops. */
if (__afl_area_ptr == (void *)-1) exit(1);
/* Write something into the bitmap so that even with low AFL_INST_RATIO,
our parent doesn't give up on us. */
__afl_area_ptr[0] = 1;
}
}
/* Fork server logic. */
static void __afl_start_forkserver(void) {
static u8 tmp[4];
s32 child_pid;
u8 child_stopped = 0;
/* Phone home and tell the parent that we're OK. If parent isn't there,
assume we're not running in forkserver mode and just execute program. */
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
while (1) {
u32 was_killed;
int status;
/* Wait for parent by reading from the pipe. Abort if read fails. */
if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(1);
/* If we stopped the child in persistent mode, but there was a race
condition and afl-fuzz already issued SIGKILL, write off the old
process. */
if (child_stopped && was_killed) {
child_stopped = 0;
if (waitpid(child_pid, &status, 0) < 0) exit(1);
}
if (!child_stopped) {
/* Once woken up, create a clone of our process. */
child_pid = fork();
if (child_pid < 0) exit(1);
/* In child process: close fds, resume execution. */
if (!child_pid) {
close(FORKSRV_FD);
close(FORKSRV_FD + 1);
return;
}
} else {
/* Special handling for persistent mode: if the child is alive but
currently stopped, simply restart it with SIGCONT. */
kill(child_pid, SIGCONT);
child_stopped = 0;
}
/* In parent process: write PID to pipe, then wait for child. */
if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(1);
if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0)
exit(1);
/* In persistent mode, the child stops itself with SIGSTOP to indicate
a successful run. In this case, we want to wake it up without forking
again. */
if (WIFSTOPPED(status)) child_stopped = 1;
/* Relay wait status to pipe, then loop back. */
if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1);
}
}
/* A simplified persistent mode handler, used as explained in README.llvm. */
int __afl_persistent_loop(unsigned int max_cnt) {
static u8 first_pass = 1;
static u32 cycle_cnt;
if (first_pass) {
cycle_cnt = max_cnt;
first_pass = 0;
return 1;
}
if (is_persistent && --cycle_cnt) {
raise(SIGSTOP);
return 1;
} else return 0;
}
/* This one can be called from user code when deferred forkserver mode
is enabled. */
void __afl_manual_init(void) {
static u8 init_done;
if (!init_done) {
__afl_map_shm();
__afl_start_forkserver();
init_done = 1;
}
}
/* Proper initialization routine. */
__attribute__((constructor(101))) void __afl_auto_init(void) {
is_persistent = !!getenv(PERSIST_ENV_VAR);
if (getenv(DEFER_ENV_VAR)) return;
__afl_manual_init();
}

View File

@ -64,7 +64,7 @@ a2.cpp
but it might lead to files being unwantedly instrumented if the same filename but it might lead to files being unwantedly instrumented if the same filename
exists somewhere else in the project directories. exists somewhere else in the project directories.
The created whitelist file is then set to AFL_INST_WHITELIST when you compile The created whitelist file is then set to AFL_LLVM_WHITELIST when you compile
your program. For each file that didn't match the whitelist, the compiler will your program. For each file that didn't match the whitelist, the compiler will
issue a warning at the end stating that no blocks were instrumented. If you issue a warning at the end stating that no blocks were instrumented. If you
didn't intend to instrument that file, then you can safely ignore that warning. didn't intend to instrument that file, then you can safely ignore that warning.