Merge branch 'master' of github.com:vanhauser-thc/AFLplusplus

This commit is contained in:
Andrea Fioraldi
2019-09-22 19:38:57 +02:00
19 changed files with 850 additions and 122 deletions

View File

@ -80,6 +80,8 @@ endif
all: test_x86 test_shm test_python27 ready $(PROGS) afl-as test_build all_done all: test_x86 test_shm test_python27 ready $(PROGS) afl-as test_build all_done
tests: source-only
@cd test ; ./test.sh
help: help:
@echo "HELP --- the following make targets exist:" @echo "HELP --- the following make targets exist:"
@ -90,6 +92,8 @@ help:
@echo "distrib: everything (for both binary-only and source code fuzzing)" @echo "distrib: everything (for both binary-only and source code fuzzing)"
@echo "install: installs everything you have compiled with the build option above" @echo "install: installs everything you have compiled with the build option above"
@echo "clean: cleans everything. for qemu_mode and unicorn_mode it means it deletes all downloads as well" @echo "clean: cleans everything. for qemu_mode and unicorn_mode it means it deletes all downloads as well"
@echo "tests: this runs the test framework. It is more catered for the developers, but if you run into problems this helps pinpointing the problem"
@echo "document: creates afl-fuzz-document which will only do one run and save all manipulated inputs into out/queue/mutations"
@echo "help: shows these build options :-)" @echo "help: shows these build options :-)"
@echo "==========================================" @echo "=========================================="
@echo "Recommended: \"distrib\" or \"source-only\", then \"install\"" @echo "Recommended: \"distrib\" or \"source-only\", then \"install\""
@ -149,31 +153,36 @@ afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS) $(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
ln -sf afl-as as ln -sf afl-as as
afl-common.o : src/afl-common.c include/common.h src/afl-common.o : src/afl-common.c include/common.h
$(CC) $(CFLAGS) -c src/afl-common.c $(CC) $(CFLAGS) -c src/afl-common.c -o src/afl-common.o
afl-forkserver.o : src/afl-forkserver.c include/forkserver.h src/afl-forkserver.o : src/afl-forkserver.c include/forkserver.h
$(CC) $(CFLAGS) -c src/afl-forkserver.c $(CC) $(CFLAGS) -c src/afl-forkserver.c -o src/afl-forkserver.o
afl-sharedmem.o : src/afl-sharedmem.c include/sharedmem.h src/afl-sharedmem.o : src/afl-sharedmem.c include/sharedmem.h
$(CC) $(CFLAGS) -c src/afl-sharedmem.c $(CC) $(CFLAGS) -c src/afl-sharedmem.c -o src/afl-sharedmem.o
afl-fuzz: include/afl-fuzz.h $(AFL_FUZZ_FILES) afl-common.o afl-sharedmem.o afl-forkserver.o $(COMM_HDR) | test_x86 afl-fuzz: include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $(AFL_FUZZ_FILES) afl-common.o afl-sharedmem.o afl-forkserver.o -o $@ $(LDFLAGS) $(PYFLAGS) $(CC) $(CFLAGS) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS) $(PYFLAGS)
afl-showmap: src/afl-showmap.c afl-common.o afl-sharedmem.o $(COMM_HDR) | test_x86 afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) src/$@.c afl-common.o afl-sharedmem.o -o $@ $(LDFLAGS) $(CC) $(CFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o -o $@ $(LDFLAGS)
afl-tmin: src/afl-tmin.c afl-common.o afl-sharedmem.o afl-forkserver.o $(COMM_HDR) | test_x86 afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) src/$@.c afl-common.o afl-sharedmem.o afl-forkserver.o -o $@ $(LDFLAGS) $(CC) $(CFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o $@ $(LDFLAGS)
afl-analyze: src/afl-analyze.c afl-common.o afl-sharedmem.o $(COMM_HDR) | test_x86 afl-analyze: src/afl-analyze.c src/afl-common.o src/afl-sharedmem.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) src/$@.c afl-common.o afl-sharedmem.o -o $@ $(LDFLAGS) $(CC) $(CFLAGS) src/$@.c src/afl-common.o src/afl-sharedmem.o -o $@ $(LDFLAGS)
afl-gotcpu: src/afl-gotcpu.c $(COMM_HDR) | test_x86 afl-gotcpu: src/afl-gotcpu.c $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS) $(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
# document all mutations and only do one run (use with only one input file!)
document: include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o $(COMM_HDR) | test_x86
$(CC) $(CFLAGS) $(AFL_FUZZ_FILES) -D_AFL_DOCUMENT_MUTATIONS src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o -o afl-fuzz-document $(LDFLAGS) $(PYFLAGS)
code-format: code-format:
./.custom-format.py -i src/*.c ./.custom-format.py -i src/*.c
./.custom-format.py -i include/*.h ./.custom-format.py -i include/*.h
@ -219,7 +228,7 @@ all_done: test_build
.NOTPARALLEL: clean .NOTPARALLEL: clean
clean: clean:
rm -f $(PROGS) afl-as as afl-g++ afl-clang afl-clang++ *.o *~ a.out core core.[1-9][0-9]* *.stackdump test .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.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 .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 rm -rf out_dir qemu_mode/qemu-3.1.1 unicorn_mode/unicorn
$(MAKE) -C llvm_mode clean $(MAKE) -C llvm_mode clean
$(MAKE) -C libdislocator clean $(MAKE) -C libdislocator clean
@ -280,6 +289,8 @@ endif
if [ -f compare-transform-pass.so ]; then set -e; install -m 755 compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi if [ -f compare-transform-pass.so ]; then set -e; install -m 755 compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi
if [ -f split-compares-pass.so ]; then set -e; install -m 755 split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi if [ -f split-compares-pass.so ]; then set -e; install -m 755 split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi
if [ -f split-switches-pass.so ]; then set -e; install -m 755 split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi if [ -f split-switches-pass.so ]; then set -e; install -m 755 split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi
if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi
if [ -f libtokencap.so ]; then set -e; install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH); fi
if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi
set -e; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-g++ set -e; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-g++
@ -294,7 +305,7 @@ endif
cp -r testcases/ $${DESTDIR}$(MISC_PATH) cp -r testcases/ $${DESTDIR}$(MISC_PATH)
cp -r dictionaries/ $${DESTDIR}$(MISC_PATH) cp -r dictionaries/ $${DESTDIR}$(MISC_PATH)
publish: clean #publish: clean
# test "`basename $$PWD`" = "afl" || exit 1 # test "`basename $$PWD`" = "afl" || exit 1
# test -f ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz; if [ "$$?" = "0" ]; then echo; echo "Change program version in config.h, mmkay?"; echo; exit 1; fi # test -f ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz; if [ "$$?" = "0" ]; then echo; echo "Change program version in config.h, mmkay?"; echo; exit 1; fi
# cd ..; rm -rf $(PROGNAME)-$(VERSION); cp -pr $(PROGNAME) $(PROGNAME)-$(VERSION); \ # cd ..; rm -rf $(PROGNAME)-$(VERSION); cp -pr $(PROGNAME) $(PROGNAME)-$(VERSION); \

14
TODO
View File

@ -1,14 +1,3 @@
Roadmap 2.54d:
==============
afl-fuzz:
- enable python mutator for MOpt
- enable custom mutator for MOpt
- add superion?
remote feature
Roadmap 2.55d: Roadmap 2.55d:
============== ==============
@ -26,6 +15,9 @@ qemu_mode:
Idea: The static analyzer outputs a map in which each edge that must be Idea: The static analyzer outputs a map in which each edge that must be
skipped is marked with 1. QEMU loads it at startup in the parent process. skipped is marked with 1. QEMU loads it at startup in the parent process.
custom_mutators:
- rip what Superion is doing into custom mutators for js, php, etc.
unit testing / or large testcase campaign unit testing / or large testcase campaign

View File

@ -22,6 +22,15 @@ Version ++2.54d (dev):
add AFL_CUSTOM_MUTATOR_ONLY (that will trigger the previous behaviour) add AFL_CUSTOM_MUTATOR_ONLY (that will trigger the previous behaviour)
- no more unlinking the input file, this way the input file can also be a - no more unlinking the input file, this way the input file can also be a
FIFO or disk partition FIFO or disk partition
- setting LLVM_CONFIG for llvm_mode will now again switch to the selected
llvm version. If you setup is correct.
- fuzzing strategy yields for custom mutator were missing from the UI, added them :)
- added "make tests" which will perform checks to see that all functionality
is working as expected. this is currently the starting point, its not complete :)
- added mutation documentation feature ("make document"), creates afl-fuzz-document
and saves all mutations of the first run on the first file into out/queue/mutations
- libtokencap and libdislocator now compile to the afl_root directory and are
installed to the .../lib/afl directory when present during make install
- reducing duplicate code in afl-fuzz - reducing duplicate code in afl-fuzz
- added "make help" - added "make help"
- removed compile warnings from python internal stuff - removed compile warnings from python internal stuff

View File

@ -97,7 +97,8 @@ Then there are a few specific features that are only available in llvm_mode:
- Setting AFL_LLVM_LAF_TRANSFORM_COMPARES will split string compare functions - Setting AFL_LLVM_LAF_TRANSFORM_COMPARES will split string compare functions
- Setting AFL_LLVM_LAF_SPLIT_COMPARES will split > 8 bit CMP instructions - Setting AFL_LLVM_LAF_SPLIT_COMPARES will split all floating point and
64, 32 and 16 bit integer CMP instructions
See llvm_mode/README.laf-intel for more information. See llvm_mode/README.laf-intel for more information.

View File

@ -33,6 +33,7 @@
int main(int argc, char** argv) { int main(int argc, char** argv) {
ssize_t len; /* how much input did we read? */
char buf[100]; /* Example-only buffer, you'd replace it with other global or char buf[100]; /* Example-only buffer, you'd replace it with other global or
local variables appropriate for your use case. */ local variables appropriate for your use case. */
@ -57,11 +58,15 @@ int main(int argc, char** argv) {
Beware of reading from buffered FILE* objects such as stdin. Use Beware of reading from buffered FILE* objects such as stdin. Use
raw file descriptors or call fopen() / fdopen() in every pass. */ raw file descriptors or call fopen() / fdopen() in every pass. */
read(0, buf, 100); len = read(0, buf, 100);
/* STEP 3: This is where we'd call the tested library on the read data. /* STEP 3: This is where we'd call the tested library on the read data.
We just have some trivial inline code that faults on 'foo!'. */ We just have some trivial inline code that faults on 'foo!'. */
/* do we have enough data? */
if (len < 4)
return 0;
if (buf[0] == 'f') { if (buf[0] == 'f') {
printf("one\n"); printf("one\n");
if (buf[1] == 'o') { if (buf[1] == 'o') {

View File

@ -682,5 +682,10 @@ static u64 get_cur_time_us(void) {
} }
#ifdef _AFL_DOCUMENT_MUTATIONS
extern u8 do_document;
extern u32 document_counter;
#endif
#endif #endif

View File

@ -24,15 +24,15 @@ CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
all: libdislocator.so all: libdislocator.so
libdislocator.so: libdislocator.so.c ../config.h libdislocator.so: libdislocator.so.c ../config.h
$(CC) $(CFLAGS) -shared -fPIC $< -o $@ $(LDFLAGS) $(CC) $(CFLAGS) -shared -fPIC $< -o ../$@ $(LDFLAGS)
.NOTPARALLEL: clean .NOTPARALLEL: clean
clean: clean:
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* rm -f *.o *.so *~ a.out core core.[1-9][0-9]*
rm -f libdislocator.so rm -f ../libdislocator.so
install: all install: all
install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH) install -m 755 ../libdislocator.so $${DESTDIR}$(HELPER_PATH)
install -m 644 README.dislocator $${DESTDIR}$(HELPER_PATH) install -m 644 README.dislocator $${DESTDIR}$(HELPER_PATH)

View File

@ -24,15 +24,15 @@ CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
all: libtokencap.so all: libtokencap.so
libtokencap.so: libtokencap.so.c ../config.h libtokencap.so: libtokencap.so.c ../config.h
$(CC) $(CFLAGS) -shared -fPIC $< -o $@ $(LDFLAGS) $(CC) $(CFLAGS) -shared -fPIC $< -o ../$@ $(LDFLAGS)
.NOTPARALLEL: clean .NOTPARALLEL: clean
clean: clean:
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* rm -f *.o *.so *~ a.out core core.[1-9][0-9]*
rm -f libtokencap.so rm -f ../libtokencap.so
install: all install: all
install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH) install -m 755 ../libtokencap.so $${DESTDIR}$(HELPER_PATH)
install -m 644 README.tokencap $${DESTDIR}$(HELPER_PATH) install -m 644 README.tokencap $${DESTDIR}$(HELPER_PATH)

View File

@ -37,7 +37,8 @@ endif
LLVMVER = $(shell $(LLVM_CONFIG) --version) LLVMVER = $(shell $(LLVM_CONFIG) --version)
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version | egrep -q '^[12]|^3\.0|^1[0-9]' && echo 1 || echo 0 ) LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version | egrep -q '^[12]|^3\.0|^1[0-9]' && echo 1 || echo 0 )
LLVM_MAJOR = ($shell $(LLVM_CONFIG) --version | sed 's/\..*//') LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version | sed 's/\..*//')
LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir)
ifeq "$(LLVM_UNSUPPORTED)" "1" ifeq "$(LLVM_UNSUPPORTED)" "1"
$(warn llvm_mode only supports versions 3.8.0 up to 9) $(warn llvm_mode only supports versions 3.8.0 up to 9)
@ -77,11 +78,18 @@ endif
# this seems to be busted on some distros, so using the one in $PATH is # this seems to be busted on some distros, so using the one in $PATH is
# probably better. # probably better.
ifeq "$(origin CC)" "default" CC = $(LLVM_BINDIR)/clang
ifeq "$(shell uname)" "OpenBSD" CXX = $(LLVM_BINDIR)/clang++
CC = $(BIN_PATH)/clang
CXX = $(BIN_PATH)/clang++ ifeq "$(shell test -e $(CC) || echo 1 )" "1"
# llvm-config --bindir is not providing a valid path, so ...
ifeq "$(shell test -e "$(BIN_DIR)/clang" && echo 1)" "1"
# we found one in the local install directory, lets use these
CC = $(BIN_DIR)/clang
CXX = $(BIN_DIR)/clang++
else else
# hope for the best
$(warn we have trouble finding clang/clang++ - llvm-config is not helping us)
CC = clang CC = clang
CXX = clang++ CXX = clang++
endif endif

View File

@ -41,6 +41,10 @@ In order to leverage this mechanism, you need to have clang installed on your
system. You should also make sure that the llvm-config tool is in your path system. You should also make sure that the llvm-config tool is in your path
(or pointed to via LLVM_CONFIG in the environment). (or pointed to via LLVM_CONFIG in the environment).
Note that if you have several LLVM versions installed, pointing LLVM_CONFIG
to the version you want to use will switch compiling to this specific
version - if you installation is set up correctly :-)
Unfortunately, some systems that do have clang come without llvm-config or the Unfortunately, some systems that do have clang come without llvm-config or the
LLVM development headers; one example of this is FreeBSD. FreeBSD users will LLVM development headers; one example of this is FreeBSD. FreeBSD users will
also run into problems with clang being built statically and not being able to also run into problems with clang being built statically and not being able to

View File

@ -105,9 +105,7 @@ bool AFLCoverage::runOnModule(Module &M) {
SAYF(cCYA "afl-llvm-pass" VERSION cRST " by <lszekeres@google.com>\n"); SAYF(cCYA "afl-llvm-pass" VERSION cRST " by <lszekeres@google.com>\n");
} else } else if (getenv("AFL_QUIET")) be_quiet = 1;
be_quiet = 1;
/* Decide instrumentation ratio */ /* Decide instrumentation ratio */

View File

@ -1,5 +1,6 @@
/* /*
* Copyright 2016 laf-intel * Copyright 2016 laf-intel
* extended for floating point by Heiko Eißfeldt
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,6 +22,7 @@
#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/IR/Verifier.h" #include "llvm/IR/Verifier.h"
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/IR/IRBuilder.h" #include "llvm/IR/IRBuilder.h"
@ -49,9 +51,11 @@ class SplitComparesTransform : public ModulePass {
} }
private: private:
bool splitCompares(Module &M, unsigned bitw); size_t splitIntCompares(Module &M, unsigned bitw);
size_t splitFPCompares(Module &M);
bool simplifyCompares(Module &M); bool simplifyCompares(Module &M);
bool simplifySignedness(Module &M); bool simplifyIntSignedness(Module &M);
size_t nextPowerOfTwo(size_t in);
}; };
@ -65,6 +69,7 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
LLVMContext & C = M.getContext(); LLVMContext & C = M.getContext();
std::vector<Instruction *> icomps; std::vector<Instruction *> icomps;
std::vector<Instruction *> fcomps;
IntegerType * Int1Ty = IntegerType::getInt1Ty(C); IntegerType * Int1Ty = IntegerType::getInt1Ty(C);
/* iterate over all functions, bbs and instruction and add /* iterate over all functions, bbs and instruction and add
@ -79,25 +84,41 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) { if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) {
if (selectcmpInst->getPredicate() != CmpInst::ICMP_UGE && if (selectcmpInst->getPredicate() == CmpInst::ICMP_UGE ||
selectcmpInst->getPredicate() != CmpInst::ICMP_SGE && selectcmpInst->getPredicate() == CmpInst::ICMP_SGE ||
selectcmpInst->getPredicate() != CmpInst::ICMP_ULE && selectcmpInst->getPredicate() == CmpInst::ICMP_ULE ||
selectcmpInst->getPredicate() != CmpInst::ICMP_SLE) { selectcmpInst->getPredicate() == CmpInst::ICMP_SLE) {
continue; auto op0 = selectcmpInst->getOperand(0);
auto op1 = selectcmpInst->getOperand(1);
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
/* this is probably not needed but we do it anyway */
if (!intTyOp0 || !intTyOp1) { continue; }
icomps.push_back(selectcmpInst);
} }
auto op0 = selectcmpInst->getOperand(0); if (selectcmpInst->getPredicate() == CmpInst::FCMP_OGE ||
auto op1 = selectcmpInst->getOperand(1); selectcmpInst->getPredicate() == CmpInst::FCMP_UGE ||
selectcmpInst->getPredicate() == CmpInst::FCMP_OLE ||
selectcmpInst->getPredicate() == CmpInst::FCMP_ULE) {
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType()); auto op0 = selectcmpInst->getOperand(0);
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType()); auto op1 = selectcmpInst->getOperand(1);
/* this is probably not needed but we do it anyway */ Type *TyOp0 = op0->getType();
if (!intTyOp0 || !intTyOp1) { continue; } Type *TyOp1 = op1->getType();
icomps.push_back(selectcmpInst); /* this is probably not needed but we do it anyway */
if (TyOp0 != TyOp1) { continue; }
fcomps.push_back(selectcmpInst);
}
} }
@ -107,7 +128,7 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
} }
if (!icomps.size()) { return false; } if (!icomps.size() && !fcomps.size()) { return false; }
for (auto &IcmpInst : icomps) { for (auto &IcmpInst : icomps) {
@ -173,18 +194,83 @@ bool SplitComparesTransform::simplifyCompares(Module &M) {
} }
/* now for floating point */
for (auto &FcmpInst : fcomps) {
BasicBlock *bb = FcmpInst->getParent();
auto op0 = FcmpInst->getOperand(0);
auto op1 = FcmpInst->getOperand(1);
/* find out what the new predicate is going to be */
auto pred = dyn_cast<CmpInst>(FcmpInst)->getPredicate();
CmpInst::Predicate new_pred;
switch (pred) {
case CmpInst::FCMP_UGE: new_pred = CmpInst::FCMP_UGT; break;
case CmpInst::FCMP_OGE: new_pred = CmpInst::FCMP_OGT; break;
case CmpInst::FCMP_ULE: new_pred = CmpInst::FCMP_ULT; break;
case CmpInst::FCMP_OLE: new_pred = CmpInst::FCMP_OLT; break;
default: // keep the compiler happy
continue;
}
/* split before the icmp instruction */
BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(FcmpInst));
/* the old bb now contains a unconditional jump to the new one (end_bb)
* we need to delete it later */
/* create the ICMP instruction with new_pred and add it to the old basic
* block bb it is now at the position where the old IcmpInst was */
Instruction *fcmp_np;
fcmp_np = CmpInst::Create(Instruction::FCmp, new_pred, op0, op1);
bb->getInstList().insert(bb->getTerminator()->getIterator(), fcmp_np);
/* create a new basic block which holds the new EQ fcmp */
Instruction *fcmp_eq;
/* insert middle_bb before end_bb */
BasicBlock *middle_bb =
BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
fcmp_eq = CmpInst::Create(Instruction::FCmp, CmpInst::FCMP_OEQ, op0, op1);
middle_bb->getInstList().push_back(fcmp_eq);
/* add an unconditional branch to the end of middle_bb with destination
* end_bb */
BranchInst::Create(end_bb, middle_bb);
/* replace the uncond branch with a conditional one, which depends on the
* new_pred icmp. True goes to end, false to the middle (injected) bb */
auto term = bb->getTerminator();
BranchInst::Create(end_bb, middle_bb, fcmp_np, bb);
term->eraseFromParent();
/* replace the old IcmpInst (which is the first inst in end_bb) with a PHI
* inst to wire up the loose ends */
PHINode *PN = PHINode::Create(Int1Ty, 2, "");
/* the first result depends on the outcome of icmp_eq */
PN->addIncoming(fcmp_eq, middle_bb);
/* if the source was the original bb we know that the icmp_np yielded true
* hence we can hardcode this value */
PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb);
/* replace the old IcmpInst with our new and shiny PHI inst */
BasicBlock::iterator ii(FcmpInst);
ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN);
}
return true; return true;
} }
/* this function transforms signed compares to equivalent unsigned compares */ /* this function transforms signed compares to equivalent unsigned compares */
bool SplitComparesTransform::simplifySignedness(Module &M) { bool SplitComparesTransform::simplifyIntSignedness(Module &M) {
LLVMContext & C = M.getContext(); LLVMContext & C = M.getContext();
std::vector<Instruction *> icomps; std::vector<Instruction *> icomps;
IntegerType * Int1Ty = IntegerType::getInt1Ty(C); IntegerType * Int1Ty = IntegerType::getInt1Ty(C);
/* iterate over all functions, bbs and instruction and add /* iterate over all functions, bbs and instructions and add
* all signed compares to icomps vector */ * all signed compares to icomps vector */
for (auto &F : M) { for (auto &F : M) {
@ -196,27 +282,25 @@ bool SplitComparesTransform::simplifySignedness(Module &M) {
if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) { if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) {
if (selectcmpInst->getPredicate() != CmpInst::ICMP_SGT && if (selectcmpInst->getPredicate() == CmpInst::ICMP_SGT ||
selectcmpInst->getPredicate() != CmpInst::ICMP_SLT) { selectcmpInst->getPredicate() == CmpInst::ICMP_SLT) {
continue; auto op0 = selectcmpInst->getOperand(0);
auto op1 = selectcmpInst->getOperand(1);
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
/* see above */
if (!intTyOp0 || !intTyOp1) { continue; }
/* i think this is not possible but to lazy to look it up */
if (intTyOp0->getBitWidth() != intTyOp1->getBitWidth()) { continue; }
icomps.push_back(selectcmpInst);
} }
auto op0 = selectcmpInst->getOperand(0);
auto op1 = selectcmpInst->getOperand(1);
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
/* see above */
if (!intTyOp0 || !intTyOp1) { continue; }
/* i think this is not possible but to lazy to look it up */
if (intTyOp0->getBitWidth() != intTyOp1->getBitWidth()) { continue; }
icomps.push_back(selectcmpInst);
} }
} }
@ -328,24 +412,37 @@ bool SplitComparesTransform::simplifySignedness(Module &M) {
} }
/* splits icmps of size bitw into two nested icmps with bitw/2 size each */ size_t SplitComparesTransform::nextPowerOfTwo(size_t in) {
bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) { --in;
in |= in >> 1;
in |= in >> 2;
in |= in >> 4;
// in |= in >> 8;
// in |= in >> 16;
return in + 1;
}
/* splits fcmps into two nested fcmps with sign compare and the rest */
size_t SplitComparesTransform::splitFPCompares(Module &M) {
size_t count = 0;
LLVMContext &C = M.getContext(); LLVMContext &C = M.getContext();
IntegerType *Int1Ty = IntegerType::getInt1Ty(C); const DataLayout &dl = M.getDataLayout();
IntegerType *OldIntType = IntegerType::get(C, bitw);
IntegerType *NewIntType = IntegerType::get(C, bitw / 2);
std::vector<Instruction *> icomps; /* define unions with floating point and (sign, exponent, mantissa) triples */
if (dl.isLittleEndian()) {
}
else if (dl.isBigEndian()) {
}
else {
return count;
}
if (bitw % 2) { return false; } std::vector<CmpInst *> fcomps;
/* not supported yet */ /* get all EQ, NE, GT, and LT fcmps. if the other two
if (bitw > 64) { return false; } * functions were executed only these four predicates should exist */
/* get all EQ, NE, UGT, and ULT icmps of width bitw. if the other two
* unctions were executed only these four predicates should exist */
for (auto &F : M) { for (auto &F : M) {
for (auto &BB : F) { for (auto &BB : F) {
@ -356,33 +453,344 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) { if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) {
if (selectcmpInst->getPredicate() != CmpInst::ICMP_EQ && if (selectcmpInst->getPredicate() == CmpInst::FCMP_OEQ ||
selectcmpInst->getPredicate() != CmpInst::ICMP_NE && selectcmpInst->getPredicate() == CmpInst::FCMP_ONE ||
selectcmpInst->getPredicate() != CmpInst::ICMP_UGT && selectcmpInst->getPredicate() == CmpInst::FCMP_UNE ||
selectcmpInst->getPredicate() != CmpInst::ICMP_ULT) { selectcmpInst->getPredicate() == CmpInst::FCMP_OGT ||
selectcmpInst->getPredicate() == CmpInst::FCMP_OLT) {
continue; auto op0 = selectcmpInst->getOperand(0);
auto op1 = selectcmpInst->getOperand(1);
Type *TyOp0 = op0->getType();
Type *TyOp1 = op1->getType();
if (TyOp0 != TyOp1) { continue; }
fcomps.push_back(selectcmpInst);
} }
auto op0 = selectcmpInst->getOperand(0); }
auto op1 = selectcmpInst->getOperand(1);
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType()); }
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
if (!intTyOp0 || !intTyOp1) { continue; } }
/* check if the bitwidths are the one we are looking for */ }
if (intTyOp0->getBitWidth() != bitw || if (!fcomps.size()) { return count; }
intTyOp1->getBitWidth() != bitw) {
continue; IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
for (auto &FcmpInst : fcomps) {
BasicBlock *bb = FcmpInst->getParent();
auto op0 = FcmpInst->getOperand(0);
auto op1 = FcmpInst->getOperand(1);
unsigned op0_size, op1_size;
op0_size = op0->getType()->getPrimitiveSizeInBits();
op1_size = op1->getType()->getPrimitiveSizeInBits();
if (op0_size != op1_size) {
continue;
}
const unsigned int precision = llvm::APFloatBase::semanticsPrecision(op0->getType()->getFltSemantics());
const unsigned int sizeInBits = llvm::APFloatBase::semanticsSizeInBits(op0->getType()->getFltSemantics());
const unsigned shiftR_exponent = precision - 1;
const unsigned long long mask_fraction = ((1 << (precision - 2))) | ((1 << (precision - 2)) - 1);
const unsigned long long mask_exponent = (1 << (sizeInBits - precision)) - 1;
// round up sizes to the next power of two
// this should help with integer compare splitting
size_t exTySizeBytes = ((sizeInBits - precision + 7) >> 3);
size_t frTySizeBytes = ((precision - 1 + 7) >> 3);
IntegerType *IntExponentTy = IntegerType::get(C, nextPowerOfTwo(exTySizeBytes) << 3);
IntegerType *IntFractionTy = IntegerType::get(C, nextPowerOfTwo(frTySizeBytes) << 3);
BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(FcmpInst));
/* create the integers from floats directly */
Instruction *b_op0, *b_op1;
b_op0 = CastInst::Create(Instruction::BitCast, op0, IntegerType::get(C, op0_size));
bb->getInstList().insert(bb->getTerminator()->getIterator(), b_op0);
b_op1 = CastInst::Create(Instruction::BitCast, op1, IntegerType::get(C, op1_size));
bb->getInstList().insert(bb->getTerminator()->getIterator(), b_op1);
/* isolate signs of value of floating point type */
/* create a 1 bit compare for the sign bit. to do this shift and trunc
* the original operands so only the first bit remains.*/
Instruction *s_s0, *t_s0, *s_s1, *t_s1, *icmp_sign_bit;
s_s0 = BinaryOperator::Create(Instruction::LShr, b_op0,
ConstantInt::get(b_op0->getType(), op0_size - 1));
bb->getInstList().insert(bb->getTerminator()->getIterator(), s_s0);
t_s0 = new TruncInst(s_s0, Int1Ty);
bb->getInstList().insert(bb->getTerminator()->getIterator(), t_s0);
s_s1 = BinaryOperator::Create(Instruction::LShr, b_op1,
ConstantInt::get(b_op1->getType(), op1_size - 1));
bb->getInstList().insert(bb->getTerminator()->getIterator(), s_s1);
t_s1 = new TruncInst(s_s1, Int1Ty);
bb->getInstList().insert(bb->getTerminator()->getIterator(), t_s1);
/* compare of the sign bits */
icmp_sign_bit = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_s0, t_s1);
bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_sign_bit);
/* create a new basic block which is executed if the signedness bits are
* equal */
BasicBlock * signequal_bb =
BasicBlock::Create(C, "signequal", end_bb->getParent(), end_bb);
BranchInst::Create(end_bb, signequal_bb);
/* create a new bb which is executed if exponents are equal */
BasicBlock * middle_bb =
BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
BranchInst::Create(end_bb, middle_bb);
auto term = bb->getTerminator();
/* if the signs are different goto end_bb else to signequal_bb */
BranchInst::Create(signequal_bb, end_bb, icmp_sign_bit, bb);
term->eraseFromParent();
/* insert code for equal signs */
/* isolate the exponents */
Instruction *s_e0, *m_e0, *t_e0, *s_e1, *m_e1, *t_e1;
s_e0 = BinaryOperator::Create(Instruction::LShr, b_op0, ConstantInt::get(b_op0->getType(), shiftR_exponent));
s_e1 = BinaryOperator::Create(Instruction::LShr, b_op1, ConstantInt::get(b_op1->getType(), shiftR_exponent));
signequal_bb->getInstList().insert(signequal_bb->getTerminator()->getIterator(), s_e0);
signequal_bb->getInstList().insert(signequal_bb->getTerminator()->getIterator(), s_e1);
if (sizeInBits - precision < exTySizeBytes * 8) {
m_e0 = BinaryOperator::Create(Instruction::And, s_e0, ConstantInt::get(s_e0->getType(), mask_exponent));
m_e1 = BinaryOperator::Create(Instruction::And, s_e1, ConstantInt::get(s_e1->getType(), mask_exponent));
signequal_bb->getInstList().insert(signequal_bb->getTerminator()->getIterator(), m_e0);
signequal_bb->getInstList().insert(signequal_bb->getTerminator()->getIterator(), m_e1);
t_e0 = new TruncInst(m_e0, IntExponentTy);
t_e1 = new TruncInst(m_e1, IntExponentTy);
} else {
t_e0 = new TruncInst(s_e0, IntExponentTy);
t_e1 = new TruncInst(s_e1, IntExponentTy);
}
signequal_bb->getInstList().insert(signequal_bb->getTerminator()->getIterator(), t_e0);
signequal_bb->getInstList().insert(signequal_bb->getTerminator()->getIterator(), t_e1);
/* compare the exponents of the operands */
Instruction *icmp_exponent_result;
switch (FcmpInst->getPredicate()) {
case CmpInst::FCMP_OEQ:
icmp_exponent_result =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_e0, t_e1);
break;
case CmpInst::FCMP_ONE:
case CmpInst::FCMP_UNE:
icmp_exponent_result =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_NE, t_e0, t_e1);
break;
case CmpInst::FCMP_OGT:
Instruction *icmp_exponent;
icmp_exponent =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_e0, t_e1);
signequal_bb->getInstList().insert(signequal_bb->getTerminator()->getIterator(), icmp_exponent);
icmp_exponent_result = BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0);
break;
case CmpInst::FCMP_OLT:
icmp_exponent =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_e0, t_e1);
signequal_bb->getInstList().insert(signequal_bb->getTerminator()->getIterator(), icmp_exponent);
icmp_exponent_result = BinaryOperator::Create(Instruction::Xor, icmp_exponent, t_s0);
break;
default:
continue;
}
signequal_bb->getInstList().insert(signequal_bb->getTerminator()->getIterator(), icmp_exponent_result);
{
auto term = signequal_bb->getTerminator();
/* if the exponents are different do a fraction cmp */
BranchInst::Create(middle_bb, end_bb, icmp_exponent_result, signequal_bb);
term->eraseFromParent();
}
/* isolate the mantissa aka fraction */
Instruction *t_f0, *t_f1;
bool needTrunc = IntFractionTy->getPrimitiveSizeInBits() < op0_size;
//errs() << "Fractions: IntFractionTy size " << IntFractionTy->getPrimitiveSizeInBits() << ", op0_size " << op0_size << ", needTrunc " << needTrunc << "\n";
if (precision - 1 < frTySizeBytes * 8) {
Instruction *m_f0, *m_f1;
m_f0 = BinaryOperator::Create(Instruction::And, b_op0, ConstantInt::get(b_op0->getType(), mask_fraction));
m_f1 = BinaryOperator::Create(Instruction::And, b_op1, ConstantInt::get(b_op1->getType(), mask_fraction));
middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(), m_f0);
middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(), m_f1);
if (needTrunc) {
t_f0 = new TruncInst(m_f0, IntFractionTy);
t_f1 = new TruncInst(m_f1, IntFractionTy);
middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(), t_f0);
middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(), t_f1);
} else {
t_f0 = m_f0;
t_f1 = m_f1;
}
} else {
if (needTrunc) {
t_f0 = new TruncInst(b_op0, IntFractionTy);
t_f1 = new TruncInst(b_op1, IntFractionTy);
middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(), t_f0);
middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(), t_f1);
} else {
t_f0 = b_op0;
t_f1 = b_op1;
}
}
/* compare the fractions of the operands */
Instruction *icmp_fraction_result;
switch (FcmpInst->getPredicate()) {
case CmpInst::FCMP_OEQ:
icmp_fraction_result =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_f0, t_f1);
break;
case CmpInst::FCMP_UNE:
case CmpInst::FCMP_ONE:
icmp_fraction_result =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_NE, t_f0, t_f1);
break;
case CmpInst::FCMP_OGT:
Instruction *icmp_fraction;
icmp_fraction =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_f0, t_f1);
middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(), icmp_fraction);
icmp_fraction_result = BinaryOperator::Create(Instruction::Xor, icmp_fraction, t_s0);
break;
case CmpInst::FCMP_OLT:
icmp_fraction =
CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_f0, t_f1);
middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(), icmp_fraction);
icmp_fraction_result = BinaryOperator::Create(Instruction::Xor, icmp_fraction, t_s0);
break;
default:
continue;
}
middle_bb->getInstList().insert(middle_bb->getTerminator()->getIterator(), icmp_fraction_result);
PHINode *PN = PHINode::Create(Int1Ty, 3, "");
switch (FcmpInst->getPredicate()) {
case CmpInst::FCMP_OEQ:
/* unequal signs cannot be equal values */
/* goto false branch */
PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb);
/* unequal exponents cannot be equal values, too */
PN->addIncoming(ConstantInt::get(Int1Ty, 0), signequal_bb);
/* fractions comparison */
PN->addIncoming(icmp_fraction_result, middle_bb);
break;
case CmpInst::FCMP_ONE:
case CmpInst::FCMP_UNE:
/* unequal signs are unequal values */
/* goto true branch */
PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb);
/* unequal exponents are unequal values, too */
PN->addIncoming(ConstantInt::get(Int1Ty, 1), signequal_bb);
/* fractions comparison */
PN->addIncoming(icmp_fraction_result, middle_bb);
break;
case CmpInst::FCMP_OGT:
/* if op1 is negative goto true branch,
else go on comparing */
PN->addIncoming(t_s1, bb);
PN->addIncoming(icmp_exponent_result, signequal_bb);
PN->addIncoming(icmp_fraction_result, middle_bb);
break;
case CmpInst::FCMP_OLT:
/* if op0 is negative goto true branch,
else go on comparing */
PN->addIncoming(t_s0, bb);
PN->addIncoming(icmp_exponent_result, signequal_bb);
PN->addIncoming(icmp_fraction_result, middle_bb);
break;
default:
continue;
}
BasicBlock::iterator ii(FcmpInst);
ReplaceInstWithInst(FcmpInst->getParent()->getInstList(), ii, PN);
++count;
}
return count;
}
/* splits icmps of size bitw into two nested icmps with bitw/2 size each */
size_t SplitComparesTransform::splitIntCompares(Module &M, unsigned bitw) {
size_t count = 0;
LLVMContext &C = M.getContext();
IntegerType *Int1Ty = IntegerType::getInt1Ty(C);
IntegerType *OldIntType = IntegerType::get(C, bitw);
IntegerType *NewIntType = IntegerType::get(C, bitw / 2);
std::vector<Instruction *> icomps;
if (bitw % 2) { return 0; }
/* not supported yet */
if (bitw > 64) { return 0; }
/* get all EQ, NE, UGT, and ULT icmps of width bitw. if the
* functions simplifyCompares() and simplifyIntSignedness()
* were executed only these four predicates should exist */
for (auto &F : M) {
for (auto &BB : F) {
for (auto &IN : BB) {
CmpInst *selectcmpInst = nullptr;
if ((selectcmpInst = dyn_cast<CmpInst>(&IN))) {
if (selectcmpInst->getPredicate() == CmpInst::ICMP_EQ ||
selectcmpInst->getPredicate() == CmpInst::ICMP_NE ||
selectcmpInst->getPredicate() == CmpInst::ICMP_UGT ||
selectcmpInst->getPredicate() == CmpInst::ICMP_ULT) {
auto op0 = selectcmpInst->getOperand(0);
auto op1 = selectcmpInst->getOperand(1);
IntegerType *intTyOp0 = dyn_cast<IntegerType>(op0->getType());
IntegerType *intTyOp1 = dyn_cast<IntegerType>(op1->getType());
if (!intTyOp0 || !intTyOp1) { continue; }
/* check if the bitwidths are the one we are looking for */
if (intTyOp0->getBitWidth() != bitw ||
intTyOp1->getBitWidth() != bitw) {
continue;
}
icomps.push_back(selectcmpInst);
} }
icomps.push_back(selectcmpInst);
} }
} }
@ -391,7 +799,7 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
} }
if (!icomps.size()) { return false; } if (!icomps.size()) { return 0; }
for (auto &IcmpInst : icomps) { for (auto &IcmpInst : icomps) {
@ -482,7 +890,7 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
/* transformations for < and > */ /* transformations for < and > */
/* create a basic block which checks for the inverse predicate. /* create a basic block which checks for the inverse predicate.
* if this is true we can go to the end if not we have to got to the * if this is true we can go to the end if not we have to go to the
* bb which checks the lower half of the operands */ * bb which checks the lower half of the operands */
Instruction *icmp_inv_cmp, *op0_low, *op1_low, *icmp_low; Instruction *icmp_inv_cmp, *op0_low, *op1_low, *icmp_low;
BasicBlock * inv_cmp_bb = BasicBlock * inv_cmp_bb =
@ -528,10 +936,10 @@ bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) {
ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN);
} }
++count;
} }
return true; return count;
} }
@ -545,26 +953,32 @@ bool SplitComparesTransform::runOnModule(Module &M) {
simplifyCompares(M); simplifyCompares(M);
simplifySignedness(M); simplifyIntSignedness(M);
if (getenv("AFL_QUIET") == NULL) if (getenv("AFL_QUIET") == NULL)
errs() << "Split-compare-pass by laf.intel@gmail.com\n"; errs() << "Split-compare-pass by laf.intel@gmail.com, extended by heiko@hexco.de\n";
errs() << "Split-floatingpoint-compare-pass: " << splitFPCompares(M) << " FP comparisons splitted\n";
switch (bitw) { switch (bitw) {
case 64: case 64:
errs() << "Running split-compare-pass " << 64 << "\n"; errs() << "Split-integer-compare-pass " << bitw << "bit: "
splitCompares(M, 64); << splitIntCompares(M, bitw) << " splitted\n";
bitw >>= 1;
[[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */
case 32: case 32:
errs() << "Running split-compare-pass " << 32 << "\n"; errs() << "Split-integer-compare-pass " << bitw << "bit: "
splitCompares(M, 32); << splitIntCompares(M, bitw) << " splitted\n";
bitw >>= 1;
[[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */ [[clang::fallthrough]]; /*FALLTHRU*/ /* FALLTHROUGH */
case 16: case 16:
errs() << "Running split-compare-pass " << 16 << "\n"; errs() << "Split-integer-compare-pass " << bitw << "bit: "
splitCompares(M, 16); << splitIntCompares(M, bitw) << " splitted\n";
bitw >>= 1;
break; break;
default: default:

View File

@ -259,3 +259,7 @@ PyObject *py_functions[PY_FUNC_COUNT];
#endif #endif
#ifdef _AFL_DOCUMENT_MUTATIONS
u8 do_document;
u32 document_counter;
#endif

View File

@ -1478,8 +1478,9 @@ void check_cpu_governor(void) {
" to make afl-fuzz skip this check - but expect some performance " " to make afl-fuzz skip this check - but expect some performance "
"drop.\n", "drop.\n",
min / 1024, max / 1024); min / 1024, max / 1024);
FATAL("Suboptimal CPU scaling governor");
#else #elif defined __APPLE__
u64 min = 0, max = 0; u64 min = 0, max = 0;
size_t mlen = sizeof(min); size_t mlen = sizeof(min);
if (getenv("AFL_SKIP_CPUFREQ")) return; if (getenv("AFL_SKIP_CPUFREQ")) return;
@ -1510,8 +1511,8 @@ void check_cpu_governor(void) {
" to make afl-fuzz skip this check - but expect some performance " " to make afl-fuzz skip this check - but expect some performance "
"drop.\n", "drop.\n",
min / 1024, max / 1024); min / 1024, max / 1024);
#endif
FATAL("Suboptimal CPU scaling governor"); FATAL("Suboptimal CPU scaling governor");
#endif
} }
/* Count the number of logical CPU cores. */ /* Count the number of logical CPU cores. */

View File

@ -4231,6 +4231,7 @@ pacemaker_fuzzing:
#define core_fuzzing(a) common_fuzzing((a), MOpt_globals_core) #define core_fuzzing(a) common_fuzzing((a), MOpt_globals_core)
void pso_updating(void) { void pso_updating(void) {
g_now += 1; g_now += 1;
@ -4310,6 +4311,22 @@ void pso_updating(void) {
u8 fuzz_one(char** argv) { u8 fuzz_one(char** argv) {
int key_val_lv = 0; int key_val_lv = 0;
#ifdef _AFL_DOCUMENT_MUTATIONS
if (do_document == 0) {
char *fn = alloc_printf("%s/mutations", out_dir);
if (fn) {
do_document = mkdir(fn, 0700); // if it exists we do not care
do_document = 1;
ck_free(fn);
} else
PFATAL("malloc()");
} else {
do_document = 2;
stop_soon = 2;
}
#endif
if (limit_time_sig == 0) { if (limit_time_sig == 0) {
key_val_lv = fuzz_one_original(argv); key_val_lv = fuzz_one_original(argv);

View File

@ -251,6 +251,18 @@ void write_to_testcase(void* mem, u32 len) {
s32 fd = out_fd; s32 fd = out_fd;
#ifdef _AFL_DOCUMENT_MUTATIONS
s32 doc_fd;
char *fn = alloc_printf("%s/mutations/%09u:%s", out_dir, document_counter++, describe_op(0));
if (fn != NULL) {
if ((doc_fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600)) >= 0) {
if (write(doc_fd, mem, len) != len) PFATAL("write to mutation file failed: %s", fn);
close(doc_fd);
}
ck_free(fn);
}
#endif
if (out_file) { if (out_file) {
// unlink(out_file); /* Ignore errors. // unlink(out_file); /* Ignore errors.

View File

@ -576,12 +576,13 @@ void show_stats(void) {
" imported : " cRST "%-10s" bSTG bV "\n", " imported : " cRST "%-10s" bSTG bV "\n",
tmp, sync_id ? DI(queued_imported) : (u8*)"n/a"); tmp, sync_id ? DI(queued_imported) : (u8*)"n/a");
sprintf(tmp, "%s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_HAVOC]), sprintf(tmp, "%s/%s, %s/%s, %s/%s, %s/%s", DI(stage_finds[STAGE_HAVOC]),
DI(stage_cycles[STAGE_HAVOC]), DI(stage_finds[STAGE_SPLICE]), DI(stage_cycles[STAGE_HAVOC]), DI(stage_finds[STAGE_SPLICE]),
DI(stage_cycles[STAGE_SPLICE]), DI(stage_finds[STAGE_PYTHON]), DI(stage_cycles[STAGE_SPLICE]), DI(stage_finds[STAGE_PYTHON]),
DI(stage_cycles[STAGE_PYTHON])); DI(stage_cycles[STAGE_PYTHON]), DI(stage_finds[STAGE_CUSTOM_MUTATOR]),
DI(stage_cycles[STAGE_CUSTOM_MUTATOR]));
SAYF(bV bSTOP " havoc : " cRST "%-36s " bSTG bV bSTOP, tmp); SAYF(bV bSTOP "havoc/custom : " cRST "%-36s " bSTG bV bSTOP, tmp);
if (t_bytes) if (t_bytes)
sprintf(tmp, "%0.02f%%", stab_ratio); sprintf(tmp, "%0.02f%%", stab_ratio);

30
test/test-compcov.c Normal file
View File

@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char** argv) {
char *input = argv[1], *buf, buffer[20];
if (argc < 2) {
ssize_t ret = read(0, buffer, sizeof(buffer) - 1);
buffer[ret] = 0;
input = buffer;
}
if (strcmp(input, "LIBTOKENCAP") == 0)
printf("your string was libtokencap\n");
else if (strcmp(input, "BUGMENOT") == 0)
printf("your string was bugmenot\n");
else if (strcmp(input, "BUFFEROVERFLOW") == 0) {
buf = malloc(16);
strcpy(buf, "TEST");
strcat(buf, input);
printf("This will only crash with libdislocator: %s\n", buf);
return 0;
} else
printf("I do not know your string\n");
return 0;
}

216
test/test.sh Executable file
View File

@ -0,0 +1,216 @@
#!/bin/bash
#
# Ensure we have: test, type, diff -q, echo -e, grep -aqE, timeout
#
test -z "" 2> /dev/null || { echo Error: test command not found ; exit 1 ; }
GREP=`type grep > /dev/null 2>&1 && echo OK`
TIMEOUT=`type timeout > /dev/null 2>&1 && echo OK`
test "$GREP" = OK || { echo Error: grep command not found ; exit 1 ; }
echo foobar | grep -aqE 'asd|oob' 2> /dev/null || { echo Error: grep command does not support -q, -a and/or -E option ; exit 1 ; }
echo 1 > test.1
echo 1 > test.2
OK=OK
diff -q test.1 test.2 >/dev/null 2>&1 || OK=
rm -f test.1 test.2
test -z "$OK" && { echo Error: diff -q is not working ; exit 1 ; }
ECHO="echo -e"
$ECHO '\x41' 2>&1 | grep -qE '^A' || {
ECHO=
test -e /bin/echo && {
ECHO="/bin/echo -e"
$ECHO '\x41' 2>&1 | grep -qE '^A' || ECHO=
}
}
test -z "$ECHO" && { echo Error: echo command does not support -e option ; exit 1 ; }
export AFL_EXIT_WHEN_DONE=1
export AFL_SKIP_CPUFREQ=1
unset AFL_QUIET
unset AFL_DEBUG
unset AFL_HARDEN
unset AFL_USE_ASAN
unset AFL_USE_MSAN
unset AFL_CC
unset AFL_PRELOAD
unset AFL_LLVM_WHITELIST
unset AFL_LLVM_INSTRIM
unset AFL_LLVM_LAF_SPLIT_SWITCHES
unset AFL_LLVM_LAF_TRANSFORM_COMPARES
unset AFL_LLVM_LAF_SPLIT_COMPARES
GREY="\\x1b[1;90m"
BLUE="\\x1b[1;94m"
GREEN="\\x1b[0;32m"
RED="\\x1b[0;31m"
YELLOW="\\x1b[1;93m"
RESET="\\x1b[0m"
$ECHO "${RESET}${GREY}[*] starting afl++ test framework ..."
$ECHO "$BLUE[*] Testing: afl-gcc, afl-showmap and afl-fuzz"
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_HARDEN=1 ../afl-gcc -o test-instr.harden ../test-instr.c > /dev/null 2>&1
test -e test-instr.plain && {
$ECHO "$GREEN[+] afl-gcc compilation succeeded"
echo 0 | ../afl-showmap -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1
../afl-showmap -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1
test -e test-instr.plain.0 -a -e test-instr.plain.1 && {
diff -q test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && {
$ECHO "$RED[!] afl-gcc instrumentation should be different on different input but is not"
} || $ECHO "$GREEN[+] afl-gcc instrumentation present and working correctly"
} || $ECHO "$RED[!] afl-gcc instrumentation failed"
rm -f test-instr.plain.0 test-instr.plain.1
} || $ECHO "$RED[!] afl-gcc failed"
test -e test-instr.harden && {
grep -qa fstack-protector-all test-instr.harden > /dev/null 2>&1 && {
$ECHO "$GREEN[+] afl-gcc hardened mode succeeded and is working"
} || $ECHO "$RED[!] afl-gcc hardened mode is not hardened"
rm -f test-instr.harden
} || $ECHO "$RED[!] afl-gcc hardened mode compilation failed"
# now we want to be sure that afl-fuzz is working
test -n "$TIMEOUT" && {
mkdir -p in
echo 0 > in/in
$ECHO "$GREY[*] running afl-fuzz for afl-gcc, this will take approx 10 seconds"
{
timeout -s KILL 10 ../afl-fuzz -i in -o out -- ./test-instr.plain > /dev/null 2>&1
} > /dev/null 2>&1
test -n "$( ls out/queue/id:000002* 2> /dev/null )" && {
$ECHO "$GREEN[+] afl-fuzz is working correctly with afl-gcc"
} || $ECHO "$RED[!] afl-fuzz is not working correctly with afl-gcc"
rm -rf in out
} || $ECHO "$YELLOW[-] we cannot test afl-fuzz because we are missing the timeout command"
rm -f test-instr.plain
} || $ECHO "$YELLOW[-] afl is not compiled, cannot test"
$ECHO "$BLUE[*] Testing: llvm_mode"
test -e ../afl-clang-fast && {
../afl-clang-fast -o test-instr.plain ../test-instr.c > /dev/null 2>&1
AFL_HARDEN=1 ../afl-clang-fast -o test-compcov.harden test-compcov.c > /dev/null 2>&1
test -e test-instr.plain && {
$ECHO "$GREEN[+] llvm_mode compilation succeeded"
echo 0 | ../afl-showmap -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1
../afl-showmap -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1
test -e test-instr.plain.0 -a -e test-instr.plain.1 && {
diff -q test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && {
$ECHO "$RED[!] llvm_mode instrumentation should be different on different input but is not"
} || $ECHO "$GREEN[+] llvm_mode instrumentation present and working correctly"
} || $ECHO "$RED[!] llvm_mode instrumentation failed"
rm -f test-instr.plain.0 test-instr.plain.1
} || $ECHO "$RED[!] llvm_mode failed"
test -e test-compcov.harden && {
grep -Eqa 'stack_chk_fail|fstack-protector-all|fortified' test-compcov.harden > /dev/null 2>&1 && {
$ECHO "$GREEN[+] llvm_mode hardened mode succeeded and is working"
} || $ECHO "$RED[!] llvm_mode hardened mode is not hardened"
rm -f test-compcov.harden
} || $ECHO "$RED[!] llvm_mode hardened mode compilation failed"
# now we want to be sure that afl-fuzz is working
test -n "$TIMEOUT" && {
mkdir -p in
echo 0 > in/in
$ECHO "$GREY[*] running afl-fuzz for llvm_mode, this will take approx 10 seconds"
{
timeout -s KILL 10 ../afl-fuzz -i in -o out -- ./test-instr.plain > /dev/null 2>&1
} > /dev/null 2>&1
test -n "$( ls out/queue/id:000002* 2> /dev/null )" && {
$ECHO "$GREEN[+] afl-fuzz is working correctly with llvm_mode"
} || $ECHO "$RED[!] afl-fuzz is not working correctly with llvm_mode"
rm -rf in out
} || $ECHO "$YELLOW[-] we cannot test afl-fuzz because we are missing the timeout command"
rm -f test-instr.plain
# now for the special llvm_mode things
AFL_LLVM_INSTRIM=1 AFL_LLVM_INSTRIM_LOOPHEAD=1 ../afl-clang-fast -o test-compcov.instrim test-compcov.c > /dev/null 2> test.out
test -e test-compcov.instrim && {
grep -Eq " [1-3] location" test.out && {
$ECHO "$GREEN[+] llvm_mode InsTrim feature works correctly"
} || $ECHO "$RED[!] llvm_mode InsTrim feature failed"
} || $ECHO "$RED[!] llvm_mode InsTrim feature compilation failed"
rm -f test-compcov.instrim test.out
AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast -o test-compcov.compcov test-compcov.c > /dev/null 2> test.out
test -e test-compcov.compcov && {
grep -Eq " [3-9][0-9] location" test.out && {
$ECHO "$GREEN[+] llvm_mode laf-intel/compcov feature works correctly"
} || $ECHO "$RED[!] llvm_mode laf-intel/compcov feature failed"
} || $ECHO "$RED[!] llvm_mode laf-intel/compcov feature compilation failed"
rm -f test-compcov.compcov test.out
echo foobar.c > whitelist.txt
AFL_LLVM_WHITELIST=whitelist.txt ../afl-clang-fast -o test-compcov test-compcov.c > test.out 2>&1
test -e test-compcov && {
grep -q "No instrumentation targets found" test.out && {
$ECHO "$GREEN[+] llvm_mode whitelist feature works correctly"
} || $ECHO "$RED[!] llvm_mode whitelist feature failed"
} || $ECHO "$RED[!] llvm_mode whitelist feature compilation failed"
rm -f test-compcov test.out whitelist.txt
../afl-clang-fast -o test-persistent ../experimental/persistent_demo/persistent_demo.c > /dev/null 2>&1
test -e test-persistent && {
echo foo | ../afl-showmap -o /dev/null -q -r ./test-persistent && {
$ECHO "$GREEN[+] lvm_mode persistent mode feature works correctly"
} || $ECHO "$RED[!] llvm_mode persistent mode feature failed to work"
} || $ECHO "$RED[!] llvm_mode persistent mode feature compilation failed"
rm -f test-persistent
} || $ECHO "$YELLOW[-] llvm_mode not compiled, cannot test"
$ECHO "$BLUE[*] Testing: shared library extensions"
gcc -o test-compcov test-compcov.c > /dev/null 2>&1
test -e ../libtokencap.so && {
AFL_TOKEN_FILE=token.out LD_PRELOAD=../libtokencap.so ./test-compcov foobar > /dev/null 2>&1
grep -q BUGMENOT token.out > /dev/null 2>&1 && {
$ECHO "$GREEN[+] libtokencap did successfully capture tokens"
} || $ECHO "$RED[!] libtokencap did not capture tokens"
rm -f token.out
} || $ECHO "$YELLOW[-] libtokencap is not compiled, cannot test"
test -e ../libdislocator.so && {
{
ulimit -c 1
LD_PRELOAD=../libdislocator.so ./test-compcov BUFFEROVERFLOW > test.out 2> /dev/null
} > /dev/null 2>&1
grep -q BUFFEROVERFLOW test.out > /dev/null 2>&1 && {
$ECHO "$RED[!] libdislocator did not detect the memory corruption"
} || $ECHO "$GREEN[+] libdislocator did successfully detect the memory corruption"
rm -f test.out core test-compcov.core core.test-compcov
} || $ECHO "$YELLOW[-] libdislocator is not compiled, cannot test"
rm -f test-compcov
$ECHO "$BLUE[*] Testing: qemu_mode"
test -e ../afl-qemu-trace && {
gcc -o test-instr ../test-instr.c
gcc -o test-compcov test-compcov.c
test -e test-instr -a -e test-compcov && {
test -n "$TIMEOUT" && {
mkdir -p in
echo 0 > in/in
$ECHO "$GREY[*] running afl-fuzz for qemu_mode, this will take approx 10 seconds"
{
timeout -s KILL 10 ../afl-fuzz -Q -i in -o out -- ./test-instr > /dev/null 2>&1
} > /dev/null 2>&1
test -n "$( ls out/queue/id:000002* 2> /dev/null )" && {
$ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode"
} || $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode"
test -e ../libcompcov.so && {
$ECHO "$GREY[*] running afl-fuzz for qemu_mode libcompcov, this will take approx 10 seconds"
{
export AFL_PRELOAD=../libcompcov.so
export AFL_COMPCOV_LEVEL=2
timeout -s KILL 10 ../afl-fuzz -Q -i in -o out -- ./test-compcov > /dev/null 2>&1
} > /dev/null 2>&1
test -n "$( ls out/queue/id:000002* 2> /dev/null )" && {
$ECHO "$GREEN[+] afl-fuzz is working correctly with qemu_mode libcompcov"
} || $ECHO "$RED[!] afl-fuzz is not working correctly with qemu_mode libcompcov"
} || $ECHO "$YELLOW[-] we cannot test qemu_mode libcompcov because it is not present"
rm -rf in out
} || $ECHO "$YELLOW[-] we cannot test afl-fuzz because we are missing the timeout command"
} || $ECHO "$RED[-] gcc compilation of test targets failed - what is going on??"
$ECHO "$YELLOW[?] we need a test case for qemu_mode persistent mode"
rm -f test-instr test-compcov
} || $ECHO "$YELLOW[-] qemu_mode is not compiled, cannot test"
$ECHO "$GREY[*] all test cases completed.$RESET"
# unicorn_mode ?