diff options
author | Andrea Fioraldi <andreafioraldi@gmail.com> | 2020-02-18 14:52:28 +0100 |
---|---|---|
committer | Andrea Fioraldi <andreafioraldi@gmail.com> | 2020-02-18 14:52:28 +0100 |
commit | 706718ca2e7ef0becb32fc4548fadeb19a0f6212 (patch) | |
tree | 1f14be1bc6ee1f8385d338a8ea1efbfdc362062e | |
parent | a971fc8f3662d3c5881d46c63682fd8a26d46dc5 (diff) | |
download | afl++-706718ca2e7ef0becb32fc4548fadeb19a0f6212.tar.gz |
cmplog routines llvm pass
-rw-r--r-- | include/cmplog.h | 1 | ||||
-rw-r--r-- | llvm_mode/Makefile | 5 | ||||
-rw-r--r-- | llvm_mode/afl-clang-fast.c | 6 | ||||
-rw-r--r-- | llvm_mode/afl-llvm-cmplog-rt.o.c | 40 | ||||
-rw-r--r-- | llvm_mode/cmplog-routines-pass.cc | 325 |
5 files changed, 376 insertions, 1 deletions
diff --git a/include/cmplog.h b/include/cmplog.h index c02650ee..18c6a7ef 100644 --- a/include/cmplog.h +++ b/include/cmplog.h @@ -31,6 +31,7 @@ #define CMP_MAP_W 65536 #define CMP_MAP_H 256 +#define CMP_MAP_RTN_H (CMP_MAP_H / 4) #define SHAPE_BYTES(x) (x + 1) diff --git a/llvm_mode/Makefile b/llvm_mode/Makefile index cdd89f27..579d1237 100644 --- a/llvm_mode/Makefile +++ b/llvm_mode/Makefile @@ -132,7 +132,7 @@ ifeq "$(TEST_MMAP)" "1" endif ifndef AFL_TRACE_PC - PROGS = ../afl-clang-fast ../afl-llvm-cmplog-rt.o ../afl-llvm-cmplog-rt-32.o ../afl-llvm-cmplog-rt-64.o ../afl-llvm-pass.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so + PROGS = ../afl-clang-fast ../afl-llvm-cmplog-rt.o ../afl-llvm-cmplog-rt-32.o ../afl-llvm-cmplog-rt-64.o ../afl-llvm-pass.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so ../cmplog-routines-pass.so else PROGS = ../afl-clang-fast ../afl-llvm-cmplog-rt.o ../afl-llvm-cmplog-rt-32.o ../afl-llvm-cmplog-rt-64.o ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so endif @@ -219,6 +219,9 @@ afl-common.o: ../src/afl-common.c $(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) # /laf +../cmplog-routines-pass.so: cmplog-routines-pass.cc | test_deps + $(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) + ../afl-llvm-rt.o: afl-llvm-rt.o.c | test_deps $(CC) $(CFLAGS) -fPIC -c $< -o $@ diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index a760959f..d9e2cd95 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -202,6 +202,12 @@ static void edit_params(u32 argc, char** argv) { if (cmplog_mode) { cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard,trace-cmp"; + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-routines-pass.so", obj_path); } else { diff --git a/llvm_mode/afl-llvm-cmplog-rt.o.c b/llvm_mode/afl-llvm-cmplog-rt.o.c index 7a513c0d..65d1d9d5 100644 --- a/llvm_mode/afl-llvm-cmplog-rt.o.c +++ b/llvm_mode/afl-llvm-cmplog-rt.o.c @@ -40,6 +40,8 @@ #include <sys/wait.h> #include <sys/types.h> +#include <errno.h> + /* This is a somewhat ugly hack for the experimental 'trace-pc-guard' mode. Basically, we need to make sure that the forkserver is initialized after the LLVM-generated runtime initialization pass, not before. */ @@ -397,6 +399,8 @@ void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t* Cases) { k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + u32 hits = __afl_cmp_map->headers[k].hits; __afl_cmp_map->headers[k].hits = hits + 1; @@ -410,3 +414,39 @@ void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t* Cases) { } +// POSIX shenanigan to see if an area is mapped. +// If it is mapped as X-only, we have a problem, so maybe we should add a check +// to avoid to call it on .text addresses +static int area_is_mapped(void* ptr, size_t len) { + + char * p = ptr; + char * page = (char*)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) -1)); + + int r = msync(page, (p - page) + len, MS_ASYNC); + if (r < 0) + return errno != ENOMEM; + return 1; + +} + +void __cmplog_rtn_hook(void* ptr1, void* ptr2) { + + if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) + return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_RTN; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = 31; + + hits &= CMP_MAP_RTN_H - 1; + __builtin_memcpy(((struct cmpfn_operands*)__afl_cmp_map->log[k])[hits].v0, ptr1, 32); + __builtin_memcpy(((struct cmpfn_operands*)__afl_cmp_map->log[k])[hits].v1, ptr2, 32); + +} diff --git a/llvm_mode/cmplog-routines-pass.cc b/llvm_mode/cmplog-routines-pass.cc new file mode 100644 index 00000000..309ea65e --- /dev/null +++ b/llvm_mode/cmplog-routines-pass.cc @@ -0,0 +1,325 @@ +/* + * Copyright 2016 laf-intel + * + * 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <list> +#include <string> +#include <fstream> +#include <sys/time.h> +#include "llvm/Config/llvm-config.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/ValueTracking.h" + +#if LLVM_VERSION_MAJOR > 3 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) +#include "llvm/IR/Verifier.h" +#include "llvm/IR/DebugInfo.h" +#else +#include "llvm/Analysis/Verifier.h" +#include "llvm/DebugInfo.h" +#define nullptr 0 +#endif + +#include <set> + +using namespace llvm; + +namespace { + +class CmpLogRoutines : public ModulePass { + + public: + static char ID; + CmpLogRoutines() : ModulePass(ID) { + + char *instWhiteListFilename = getenv("AFL_LLVM_WHITELIST"); + if (instWhiteListFilename) { + + std::string line; + std::ifstream fileStream; + fileStream.open(instWhiteListFilename); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_WHITELIST"); + getline(fileStream, line); + while (fileStream) { + + myWhitelist.push_back(line); + getline(fileStream, line); + + } + + } + + } + + bool runOnModule(Module &M) override; + +#if LLVM_VERSION_MAJOR < 4 + const char *getPassName() const override { + +#else + StringRef getPassName() const override { + +#endif + return "cmplog routines"; + + } + + protected: + std::list<std::string> myWhitelist; + + private: + bool hookRtns(Module &M); + +}; + +} // namespace + +char CmpLogRoutines::ID = 0; + +bool CmpLogRoutines::hookRtns(Module &M) { + + std::vector<CallInst *> calls; + LLVMContext & C = M.getContext(); + + Type * VoidTy = Type::getVoidTy(C); + PointerType * VoidPtrTy = PointerType::get(VoidTy, 0); + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c = M.getOrInsertFunction("__cmplog_rtn_hook", VoidTy, VoidPtrTy, VoidPtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookFn = cast<Function>(c); +#else + FunctionCallee cmplogHookFn = c; +#endif + + /* iterate over all functions, bbs and instruction and add suitable calls */ + for (auto &F : M) { + + for (auto &BB : F) { + + if (!myWhitelist.empty()) { + + BasicBlock::iterator IP = BB.getFirstInsertionPt(); + + bool instrumentBlock = false; + + /* Get the current location using debug information. + * For now, just instrument the block if we are not able + * to determine our location. */ + DebugLoc Loc = IP->getDebugLoc(); +#if LLVM_VERSION_MAJOR >= 4 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) + if (Loc) { + + DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode()); + + unsigned int instLine = cDILoc->getLine(); + StringRef instFilename = cDILoc->getFilename(); + + if (instFilename.str().empty()) { + + /* If the original location is empty, try using the inlined location + */ + DILocation *oDILoc = cDILoc->getInlinedAt(); + if (oDILoc) { + + instFilename = oDILoc->getFilename(); + instLine = oDILoc->getLine(); + + } + + } + + (void)instLine; + + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list<std::string>::iterator it = myWhitelist.begin(); + it != myWhitelist.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. */ + if (instFilename.str().length() >= it->length()) { + + if (instFilename.str().compare( + instFilename.str().length() - it->length(), + it->length(), *it) == 0) { + + instrumentBlock = true; + break; + + } + + } + + } + + } + + } + +#else + if (!Loc.isUnknown()) { + + DILocation cDILoc(Loc.getAsMDNode(C)); + + unsigned int instLine = cDILoc.getLineNumber(); + StringRef instFilename = cDILoc.getFilename(); + + (void)instLine; + + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list<std::string>::iterator it = myWhitelist.begin(); + it != myWhitelist.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. */ + if (instFilename.str().length() >= it->length()) { + + if (instFilename.str().compare( + instFilename.str().length() - it->length(), + it->length(), *it) == 0) { + + instrumentBlock = true; + break; + + } + + } + + } + + } + + } + +#endif + + /* Either we couldn't figure out our location or the location is + * not whitelisted, so we skip instrumentation. */ + if (!instrumentBlock) continue; + + } + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast<CallInst>(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + + FunctionType *FT = Callee->getFunctionType(); + + bool isPtrRtn = + FT->getNumParams() >= 2 && !FT->getReturnType()->isVoidTy() && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0)->isPointerTy(); + + if (!isPtrRtn) + continue; + + calls.push_back(callInst); + + } + + } + + } + + } + + if (!calls.size()) return false; + errs() << "Hooking " << calls.size() << " calls with pointers as arguments\n"; + + for (auto &callInst : calls) { + + Value *v1P = callInst->getArgOperand(0), + *v2P = callInst->getArgOperand(1); + + BasicBlock *bb = callInst->getParent(); + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&*IP); + + std::vector<Value*> args; + args.push_back(v1P); + args.push_back(v2P); + + IRB.CreateCall(cmplogHookFn, args, "tmp"); + + errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + + return true; + +} + +bool CmpLogRoutines::runOnModule(Module &M) { + + if (getenv("AFL_QUIET") == NULL) + llvm::errs() << "Running cmplog-routines-pass by andreafioraldi@gmail.com\n"; + hookRtns(M); + verifyModule(M); + + return true; + +} + +static void registerCmpLogRoutinesPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new CmpLogRoutines(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterCmpLogRoutinesPass( + PassManagerBuilder::EP_OptimizerLast, registerCmpLogRoutinesPass); + +static RegisterStandardPasses RegisterCmpLogRoutinesPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogRoutinesPass); + |