support new env var AFL_LLVM_THREADSAFE_INST to enable atomic counters.
add new test case for that.
MODIFY
include/envs.h
include/envs.h
@@ -114,6 +114,7 @@ static char *afl_environment_variables[] = {
"AFL_NGRAM_SIZE",
"AFL_LLVM_NOT_ZERO",
"AFL_LLVM_INSTRUMENT_FILE",
+ "AFL_LLVM_THREADSAFE_INST",
"AFL_LLVM_SKIP_NEVERZERO",
"AFL_NO_AFFINITY",
"AFL_LLVM_LTO_STARTID",

MODIFY
instrumentation/afl-llvm-pass.so.cc
instrumentation/afl-llvm-pass.so.cc
@@ -86,6 +86,7 @@ class AFLCoverage : public ModulePass {
uint32_t map_size = MAP_SIZE;
uint32_t function_minimum_size = 1;
char * ctx_str = NULL, *caller_str = NULL, *skip_nozero = NULL;
+ char * use_threadsafe_counters = nullptr;
};
@@ -182,6 +183,19 @@ bool AFLCoverage::runOnModule(Module &M) {
char *neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO");
#endif
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
+ use_threadsafe_counters = getenv("AFL_LLVM_THREADSAFE_INST");
+
+ if ((isatty(2) && !getenv("AFL_QUIET")) || !!getenv("AFL_DEBUG")) {
+
+ if (use_threadsafe_counters) {
+ SAYF(cCYA "afl-llvm-pass" VERSION cRST " using threadsafe instrumentation\n");
+ }
+ else
+ {
+ SAYF(cCYA "afl-llvm-pass" VERSION cRST " using non-threadsafe instrumentation\n");
+ }
+
+ }
unsigned PrevLocSize = 0;
unsigned PrevCallerSize = 0;
@@ -628,11 +642,12 @@ bool AFLCoverage::runOnModule(Module &M) {
/* Update bitmap */
-#if 1 /* Atomic */
+
+ if (use_threadsafe_counters) {/* Atomic */
+
#if LLVM_VERSION_MAJOR < 9
if (neverZero_counters_str !=
- NULL) { // with llvm 9 we make this the default as the bug in llvm is
- // then fixed
+ NULL) { // with llvm 9 we make this the default as the bug in llvm is then fixed
#else
if (!skip_nozero) {
@@ -640,11 +655,16 @@ bool AFLCoverage::runOnModule(Module &M) {
// register MapPtrIdx in a todo list
todo.push_back(MapPtrIdx);
- } else {
- IRB.CreateAtomicRMW(llvm::AtomicRMWInst::BinOp::Add, MapPtrIdx, One, llvm::AtomicOrdering::Monotonic);
}
+ else
+ {
+ IRB.CreateAtomicRMW(llvm::AtomicRMWInst::BinOp::Add, MapPtrIdx, One,
+ llvm::AtomicOrdering::Monotonic);
+ }
+ }
+ else
+ {
-#else
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
@@ -678,7 +698,7 @@ bool AFLCoverage::runOnModule(Module &M) {
IRB.CreateStore(Incr, MapPtrIdx)
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
-#endif /* non atomic case */
+ } /* non atomic case */
/* Update prev_loc history vector (by placing cur_loc at the head of the
vector and shuffle the other elements back by one) */
@@ -735,12 +755,10 @@ bool AFLCoverage::runOnModule(Module &M) {
}
-#if 1 /*Atomic NeverZero */
- // handle the todo list
+ if (use_threadsafe_counters) { /*Atomic NeverZero */
+ // handle the list of registered blocks to instrument
for (auto val : todo) {
-
- /* hexcoder: Realize a thread-safe counter that skips zero during overflow.
- * Once this counter reaches its maximum value, it next increments to 1
+ /* hexcoder: Realize a thread-safe counter that skips zero during overflow. Once this counter reaches its maximum value, it next increments to 1
*
* Instead of
* Counter + 1 -> Counter
@@ -750,7 +768,8 @@ bool AFLCoverage::runOnModule(Module &M) {
*/
/* equivalent c code looks like this
- * Thanks to https://preshing.com/20150402/you-can-do-any-kind-of-atomic-read-modify-write-operation/
+ * Thanks to
+ https://preshing.com/20150402/you-can-do-any-kind-of-atomic-read-modify-write-operation/
int old = atomic_load_explicit(&Counter, memory_order_relaxed);
int new;
@@ -760,7 +779,8 @@ bool AFLCoverage::runOnModule(Module &M) {
} else {
new = old + 1;
}
- } while (!atomic_compare_exchange_weak_explicit(&Counter, &old, new, memory_order_relaxed, memory_order_relaxed));
+ } while (!atomic_compare_exchange_weak_explicit(&Counter, &old, new,
+ memory_order_relaxed, memory_order_relaxed));
*/
@@ -780,12 +800,14 @@ bool AFLCoverage::runOnModule(Module &M) {
// insert a basic block with the corpus of a do while loop
// the calculation may need to repeat, if atomic compare_exchange is not successful
- BasicBlock::iterator it(*Counter); it++; // split after load counter
+ BasicBlock::iterator it(*Counter);
+ it++; // split after load counter
BasicBlock *end_bb = BB->splitBasicBlock(it);
end_bb->setName("injected");
// insert the block before the second half of the split
- BasicBlock * do_while_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
+ BasicBlock *do_while_bb =
+ BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb);
// set terminator of BB from target end_bb to target do_while_bb
auto term = BB->getTerminator();
@@ -807,16 +829,19 @@ bool AFLCoverage::runOnModule(Module &M) {
auto *Select = IRB.CreateSelect(Cmp, One, Incr);
// try to save back the new counter value
- auto * CmpXchg = IRB.CreateAtomicCmpXchg(MapPtrIdx, PN, Select,
- llvm::AtomicOrdering::Monotonic, llvm::AtomicOrdering::Monotonic);
+ auto *CmpXchg = IRB.CreateAtomicCmpXchg(
+ MapPtrIdx, PN, Select, llvm::AtomicOrdering::Monotonic,
+ llvm::AtomicOrdering::Monotonic);
CmpXchg->setAlignment(llvm::Align());
CmpXchg->setWeak(true);
CmpXchg->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
// get the result of trying to update the Counter
- Value * Success = IRB.CreateExtractValue(CmpXchg, ArrayRef<unsigned>({1}));
+ Value *Success =
+ IRB.CreateExtractValue(CmpXchg, ArrayRef<unsigned>({1}));
// get the (possibly updated) value of Counter
- Value * OldVal = IRB.CreateExtractValue(CmpXchg, ArrayRef<unsigned>({0}));
+ Value *OldVal =
+ IRB.CreateExtractValue(CmpXchg, ArrayRef<unsigned>({0}));
// initially we use Counter
PN->addIncoming(Counter, BB);
@@ -825,9 +850,9 @@ bool AFLCoverage::runOnModule(Module &M) {
// if the cmpXchg was not successful, retry
IRB.CreateCondBr(Success, end_bb, do_while_bb);
+ }
}
-#endif
}

MODIFY
test/test-llvm.sh
test/test-llvm.sh
@@ -43,6 +43,36 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
$ECHO "$RED[!] llvm_mode failed"
CODE=1
}
+ AFL_LLVM_INSTRUMENT=CLASSIC AFL_LLVM_THREADSAFE_INST=1 ../afl-clang-fast -o test-instr.ts ../test-instr.c > /dev/null 2>&1
+ test -e test-instr.ts && {
+ $ECHO "$GREEN[+] llvm_mode threadsafe compilation succeeded"
+ echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.ts.0 -r -- ./test-instr.ts > /dev/null 2>&1
+ AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.ts.1 -r -- ./test-instr.ts < /dev/null > /dev/null 2>&1
+ test -e test-instr.ts.0 -a -e test-instr.ts.1 && {
+ diff test-instr.ts.0 test-instr.ts.1 > /dev/null 2>&1 && {
+ $ECHO "$RED[!] llvm_mode threadsafe instrumentation should be different on different input but is not"
+ CODE=1
+ } || {
+ $ECHO "$GREEN[+] llvm_mode threadsafe instrumentation present and working correctly"
+ TUPLES=`echo 0|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.ts 2>&1 | grep Captur | awk '{print$3}'`
+ test "$TUPLES" -gt 2 -a "$TUPLES" -lt 8 && {
+ $ECHO "$GREEN[+] llvm_mode run reported $TUPLES threadsafe instrumented locations which is fine"
+ } || {
+ $ECHO "$RED[!] llvm_mode threadsafe instrumentation produces weird numbers: $TUPLES"
+ CODE=1
+ }
+ test "$TUPLES" -lt 3 && SKIP=1
+ true
+ }
+ } || {
+ $ECHO "$RED[!] llvm_mode threadsafe instrumentation failed"
+ CODE=1
+ }
+ rm -f test-instr.ts.0 test-instr.ts.1
+ } || {
+ $ECHO "$RED[!] llvm_mode (threadsafe) failed"
+ CODE=1
+ }
../afl-clang-fast -DTEST_SHARED_OBJECT=1 -z defs -fPIC -shared -o test-instr.so ../test-instr.c > /dev/null 2>&1
test -e test-instr.so && {
$ECHO "$GREEN[+] llvm_mode shared object with -z defs compilation succeeded"

support new env var AFL_LLVM_THREADSAFE_INST to enable atomic counters.
add new test case for that.