From 80ddb484deb82aefc9ba35c766ffca313d74e377 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 6 May 2020 11:51:28 +0200 Subject: added InsTrimLTO :-) --- llvm_mode/afl-llvm-lto-instrim.so.cc | 898 +++++++++++++++++++++++++++++++++++ 1 file changed, 898 insertions(+) create mode 100644 llvm_mode/afl-llvm-lto-instrim.so.cc (limited to 'llvm_mode/afl-llvm-lto-instrim.so.cc') diff --git a/llvm_mode/afl-llvm-lto-instrim.so.cc b/llvm_mode/afl-llvm-lto-instrim.so.cc new file mode 100644 index 00000000..a5bc337f --- /dev/null +++ b/llvm_mode/afl-llvm-lto-instrim.so.cc @@ -0,0 +1,898 @@ +/* + american fuzzy lop++ - LLVM-mode instrumentation pass + --------------------------------------------------- + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This library is plugged into LLVM when invoking clang through afl-clang-fast. + + */ + +#define AFL_LLVM_PASS + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemorySSAUpdater.h" +#include "llvm/Analysis/ValueTracking.h" + +#include "MarkNodes.h" +#include "afl-llvm-common.h" + +#include "config.h" +#include "debug.h" + +using namespace llvm; + +static cl::opt MarkSetOpt("markset", cl::desc("MarkSet"), + cl::init(false)); +static cl::opt LoopHeadOpt("loophead", cl::desc("LoopHead"), + cl::init(false)); + +namespace { + +struct InsTrimLTO : public ModulePass { + + protected: + uint32_t function_minimum_size = 1; + char * skip_nozero = NULL; + int afl_global_id = 1, debug = 0, autodictionary = 0; + uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0; + uint64_t map_addr = 0x10000; + + public: + static char ID; + + InsTrimLTO() : ModulePass(ID) { + + char *ptr; + + if (getenv("AFL_DEBUG")) debug = 1; + if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL) + if ((afl_global_id = atoi(ptr)) < 0 || afl_global_id >= MAP_SIZE) + FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %d\n", + ptr, MAP_SIZE - 1); + + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + + ModulePass::getAnalysisUsage(AU); + AU.addRequired(); + AU.addRequired(); + + } + + StringRef getPassName() const override { + + return "InstTrim LTO Instrumentation"; + + } + + bool runOnModule(Module &M) override { + + char be_quiet = 0; + char *ptr; + + if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + + SAYF(cCYA "LLVMInsTrimLTO" VERSION cRST + " by csienslab and Marc \"vanHauser\" Heuse\n"); + + } else + + be_quiet = 1; + + /* Process environment variables */ + + if (getenv("AFL_LLVM_AUTODICTIONARY") || + getenv("AFL_LLVM_LTO_AUTODICTIONARY")) + autodictionary = 1; + + if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; + + if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { + + uint64_t val; + if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) { + + map_addr = 0; + + } else if (map_addr == 0) { + + FATAL( + "AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used " + "together"); + + } else if (strncmp(ptr, "0x", 2) != 0) { + + map_addr = 0x10000; // the default + + } else { + + val = strtoull(ptr, NULL, 16); + if (val < 0x100 || val > 0xffffffff00000000) { + + FATAL( + "AFL_LLVM_MAP_ADDR must be a value between 0x100 and " + "0xffffffff00000000"); + + } + + map_addr = val; + + } + + } + + if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); } + + if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL || + getenv("LOOPHEAD") != NULL) { + + LoopHeadOpt = true; + + } + + if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") != NULL) + function_minimum_size = 2; + + // this is our default + MarkSetOpt = true; + + /* Initialize LLVM instrumentation */ + + LLVMContext & C = M.getContext(); + std::vector dictionary; + std::vector calls; + DenseMap valueMap; + + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); + IntegerType *Int32Ty = IntegerType::getInt32Ty(C); + IntegerType *Int64Ty = IntegerType::getInt64Ty(C); + + ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); + ConstantInt *One = ConstantInt::get(Int8Ty, 1); + + /* Get/set globals for the SHM region. */ + + GlobalVariable *AFLMapPtr = NULL; + Value * MapPtrFixed = NULL; + + if (!map_addr) { + + AFLMapPtr = + new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); + + } else { + + ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr); + MapPtrFixed = + ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Ty)); + + } + + if (autodictionary) { + + /* Some implementation notes. + * + * We try to handle 3 cases: + * - memcmp("foo", arg, 3) <- literal string + * - static char globalvar[] = "foo"; + * memcmp(globalvar, arg, 3) <- global variable + * - char localvar[] = "foo"; + * memcmp(locallvar, arg, 3) <- local variable + * + * The local variable case is the hardest. We can only detect that + * case if there is no reassignment or change in the variable. + * And it might not work across llvm version. + * What we do is hooking the initializer function for local variables + * (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned + * variable. And if that variable is then used in a compare function + * we use that noted string. + * This seems not to work for tokens that have a size <= 4 :-( + * + * - if the compared length is smaller than the string length we + * save the full string. This is likely better for fuzzing but + * might be wrong in a few cases depending on optimizers + * + * - not using StringRef because there is a bug in the llvm 11 + * checkout I am using which sometimes points to wrong strings + * + * Over and out. Took me a full day. damn. mh/vh + */ + + for (Function &F : M) { + + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + bool isStrcmp = true; + bool isMemcmp = true; + bool isStrncmp = true; + bool isStrcasecmp = true; + bool isStrncasecmp = true; + bool isIntMemcpy = true; + bool addedNull = false; + uint8_t optLen = 0; + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + std::string FuncName = Callee->getName().str(); + isStrcmp &= !FuncName.compare("strcmp"); + isMemcmp &= !FuncName.compare("memcmp"); + isStrncmp &= !FuncName.compare("strncmp"); + isStrcasecmp &= !FuncName.compare("strcasecmp"); + isStrncasecmp &= !FuncName.compare("strncasecmp"); + isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp + * function prototype */ + FunctionType *FT = Callee->getFunctionType(); + + isStrcmp &= FT->getNumParams() == 2 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()); + isStrcasecmp &= FT->getNumParams() == 2 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()); + isMemcmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy() && + FT->getParamType(2)->isIntegerTy(); + isStrncmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + isStrncasecmp &= FT->getNumParams() == 3 && + FT->getReturnType()->isIntegerTy(32) && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0) == + IntegerType::getInt8PtrTy(M.getContext()) && + FT->getParamType(2)->isIntegerTy(); + + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && + !isStrncasecmp && !isIntMemcpy) + continue; + + /* is a str{n,}{case,}cmp/memcmp, check if we have + * str{case,}cmp(x, "const") or str{case,}cmp("const", x) + * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, + * ..) memcmp(x, "const", ..) or memcmp("const", x, ..) */ + Value *Str1P = callInst->getArgOperand(0), + *Str2P = callInst->getArgOperand(1); + std::string Str1, Str2; + StringRef TmpStr; + bool HasStr1 = getConstantStringInfo(Str1P, TmpStr); + if (TmpStr.empty()) + HasStr1 = false; + else + Str1 = TmpStr.str(); + bool HasStr2 = getConstantStringInfo(Str2P, TmpStr); + if (TmpStr.empty()) + HasStr2 = false; + else + Str2 = TmpStr.str(); + + if (debug) + fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", + FuncName.c_str(), Str1P, Str1P->getName().str().c_str(), + Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P, + Str2P->getName().str().c_str(), Str2.c_str(), + HasStr2 == true ? "true" : "false"); + + // we handle the 2nd parameter first because of llvm memcpy + if (!HasStr2) { + + auto *Ptr = dyn_cast(Str2P); + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = + dyn_cast(Ptr->getOperand(0))) { + + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr2 = true; + Str2 = Array->getAsString().str(); + + } + + } + + } + + } + + // for the internal memcpy routine we only care for the second + // parameter and are not reporting anything. + if (isIntMemcpy == true) { + + if (HasStr2 == true) { + + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast(op2); + if (ilen) { + + uint64_t literalLength = Str2.size(); + uint64_t optLength = ilen->getZExtValue(); + if (literalLength + 1 == optLength) { + + Str2.append("\0", 1); // add null byte + addedNull = true; + + } + + } + + valueMap[Str1P] = new std::string(Str2); + + if (debug) + fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P); + continue; + + } + + continue; + + } + + // Neither a literal nor a global variable? + // maybe it is a local variable that we saved + if (!HasStr2) { + + std::string *strng = valueMap[Str2P]; + if (strng && !strng->empty()) { + + Str2 = *strng; + HasStr2 = true; + if (debug) + fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), + Str2P); + + } + + } + + if (!HasStr1) { + + auto Ptr = dyn_cast(Str1P); + + if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { + + if (auto *Var = + dyn_cast(Ptr->getOperand(0))) { + + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr1 = true; + Str1 = Array->getAsString().str(); + + } + + } + + } + + } + + // Neither a literal nor a global variable? + // maybe it is a local variable that we saved + if (!HasStr1) { + + std::string *strng = valueMap[Str1P]; + if (strng && !strng->empty()) { + + Str1 = *strng; + HasStr1 = true; + if (debug) + fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), + Str1P); + + } + + } + + /* handle cases of one string is const, one string is variable */ + if (!(HasStr1 ^ HasStr2)) continue; + + std::string thestring; + + if (HasStr1) + thestring = Str1; + else + thestring = Str2; + + optLen = thestring.length(); + + if (isMemcmp || isStrncmp || isStrncasecmp) { + + Value * op2 = callInst->getArgOperand(2); + ConstantInt *ilen = dyn_cast(op2); + if (ilen) { + + uint64_t literalLength = optLen; + optLen = ilen->getZExtValue(); + if (literalLength + 1 == optLen) { // add null byte + thestring.append("\0", 1); + addedNull = true; + + } + + } + + } + + // add null byte if this is a string compare function and a null + // was not already added + if (addedNull == false && !isMemcmp) { + + thestring.append("\0", 1); // add null byte + optLen++; + + } + + if (!be_quiet) { + + std::string outstring; + fprintf(stderr, "%s: length %u/%u \"", FuncName.c_str(), optLen, + (unsigned int)thestring.length()); + for (uint8_t i = 0; i < thestring.length(); i++) { + + uint8_t c = thestring[i]; + if (c <= 32 || c >= 127) + fprintf(stderr, "\\x%02x", c); + else + fprintf(stderr, "%c", c); + + } + + fprintf(stderr, "\"\n"); + + } + + // we take the longer string, even if the compare was to a + // shorter part. Note that depending on the optimizer of the + // compiler this can be wrong, but it is more likely that this + // is helping the fuzzer + if (optLen != thestring.length()) optLen = thestring.length(); + if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA; + if (optLen < MIN_AUTO_EXTRA) // too short? skip + continue; + + dictionary.push_back(thestring.substr(0, optLen)); + + } + + } + + } + + } + + } + + /* InsTrim instrumentation starts here */ + + u64 total_rs = 0; + u64 total_hs = 0; + + for (Function &F : M) { + + if (debug) { + + uint32_t bb_cnt = 0; + + for (auto &BB : F) + if (BB.size() > 0) ++bb_cnt; + SAYF(cMGN "[D] " cRST "Function %s size %zu %u\n", + F.getName().str().c_str(), F.size(), bb_cnt); + + } + + // if the function below our minimum size skip it (1 or 2) + if (F.size() < function_minimum_size) continue; + if (isBlacklisted(&F)) continue; + + std::unordered_set MS; + if (!MarkSetOpt) { + + for (auto &BB : F) { + + MS.insert(&BB); + + } + + total_rs += F.size(); + + } else { + + auto Result = markNodes(&F); + auto RS = Result.first; + auto HS = Result.second; + + MS.insert(RS.begin(), RS.end()); + if (!LoopHeadOpt) { + + MS.insert(HS.begin(), HS.end()); + total_rs += MS.size(); + + } else { + + DenseSet> EdgeSet; + DominatorTreeWrapperPass * DTWP = + &getAnalysis(F); + auto DT = &DTWP->getDomTree(); + + total_rs += RS.size(); + total_hs += HS.size(); + + for (BasicBlock *BB : HS) { + + bool Inserted = false; + for (auto BI = pred_begin(BB), BE = pred_end(BB); BI != BE; ++BI) { + + auto Edge = BasicBlockEdge(*BI, BB); + if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) { + + EdgeSet.insert({*BI, BB}); + Inserted = true; + break; + + } + + } + + if (!Inserted) { + + MS.insert(BB); + total_rs += 1; + total_hs -= 1; + + } + + } + + for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) { + + auto PredBB = I->first; + auto SuccBB = I->second; + auto NewBB = SplitBlockPredecessors(SuccBB, {PredBB}, ".split", DT, + nullptr, nullptr, false); + MS.insert(NewBB); + + } + + } + + } + + for (BasicBlock &BB : F) { + + auto PI = pred_begin(&BB); + auto PE = pred_end(&BB); + IRBuilder<> IRB(&*BB.getFirstInsertionPt()); + Value * L = NULL; + + if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; } + + if (PI == PE) { + + L = ConstantInt::get(Int32Ty, afl_global_id++); + + } else { + + auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin()); + DenseMap PredMap; + for (auto PI = pred_begin(&BB), PE = pred_end(&BB); PI != PE; ++PI) { + + BasicBlock *PBB = *PI; + auto It = PredMap.insert({PBB, afl_global_id++}); + unsigned Label = It.first->second; + PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB); + + } + + L = PN; + + } + + /* Load SHM pointer */ + Value *MapPtrIdx; + + if (map_addr) { + + MapPtrIdx = IRB.CreateGEP(MapPtrFixed, L); + + } else { + + LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); + MapPtr->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + MapPtrIdx = IRB.CreateGEP(MapPtr, L); + + } + + /* Update bitmap */ + LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); + Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + Value *Incr = IRB.CreateAdd(Counter, One); + + if (skip_nozero) { + + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Ty); + Incr = IRB.CreateAdd(Incr, carry); + + } + + IRB.CreateStore(Incr, MapPtrIdx) + ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + // done :) + + inst_blocks++; + + } + + } + + // save highest location ID to global variable + // do this after each function to fail faster + if (!be_quiet && afl_global_id > MAP_SIZE && + afl_global_id > FS_OPT_MAX_MAPSIZE) { + + uint32_t pow2map = 1, map = afl_global_id; + while ((map = map >> 1)) + pow2map++; + WARNF( + "We have %u blocks to instrument but the map size is only %u. Either " + "edit config.h and set MAP_SIZE_POW2 from %u to %u, then recompile " + "afl-fuzz and llvm_mode and then make this target - or set " + "AFL_MAP_SIZE with at least size %u when running afl-fuzz with this " + "target.", + afl_global_id, MAP_SIZE, MAP_SIZE_POW2, pow2map, afl_global_id); + + } + + if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) { + + // yes we could create our own function, insert it into ctors ... + // but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o + + Function *f = M.getFunction("__afl_auto_init_globals"); + + if (!f) { + + fprintf(stderr, + "Error: init function could not be found (this should not " + "happen)\n"); + exit(-1); + + } + + BasicBlock *bb = &f->getEntryBlock(); + if (!bb) { + + fprintf(stderr, + "Error: init function does not have an EntryBlock (this should " + "not happen)\n"); + exit(-1); + + } + + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + + if (map_addr) { + + GlobalVariable *AFLMapAddrFixed = + new GlobalVariable(M, Int64Ty, true, GlobalValue::ExternalLinkage, + 0, "__afl_map_addr"); + ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr); + StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed); + StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) { + + uint32_t write_loc = afl_global_id; + + if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3); + + GlobalVariable *AFLFinalLoc = + new GlobalVariable(M, Int32Ty, true, GlobalValue::ExternalLinkage, + 0, "__afl_final_loc"); + ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc); + StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc); + StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + if (dictionary.size()) { + + size_t memlen = 0, count = 0, offset = 0; + char * ptr; + + for (auto token : dictionary) { + + memlen += token.length(); + count++; + + } + + if (!be_quiet) + printf("AUTODICTIONARY: %lu string%s found\n", count, + count == 1 ? "" : "s"); + + if (count) { + + if ((ptr = (char *)malloc(memlen + count)) == NULL) { + + fprintf(stderr, "Error: malloc for %lu bytes failed!\n", + memlen + count); + exit(-1); + + } + + count = 0; + + for (auto token : dictionary) { + + if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) { + + ptr[offset++] = (uint8_t)token.length(); + memcpy(ptr + offset, token.c_str(), token.length()); + offset += token.length(); + count++; + + } + + } + + GlobalVariable *AFLDictionaryLen = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, + "__afl_dictionary_len"); + ConstantInt *const_len = ConstantInt::get(Int32Ty, offset); + StoreInst * StoreDictLen = + IRB.CreateStore(const_len, AFLDictionaryLen); + StoreDictLen->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + ArrayType *ArrayTy = ArrayType::get(IntegerType::get(C, 8), offset); + GlobalVariable *AFLInternalDictionary = new GlobalVariable( + M, ArrayTy, true, GlobalValue::ExternalLinkage, + ConstantDataArray::get( + C, *(new ArrayRef((char *)ptr, offset))), + "__afl_internal_dictionary"); + AFLInternalDictionary->setInitializer(ConstantDataArray::get( + C, *(new ArrayRef((char *)ptr, offset)))); + AFLInternalDictionary->setConstant(true); + + GlobalVariable *AFLDictionary = new GlobalVariable( + M, PointerType::get(Int8Ty, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_dictionary"); + + Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero); + Value *AFLDictPtr = + IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Ty, 0)); + StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary); + StoreDict->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } + + } + + } + + /* Say something nice. */ + + if (!be_quiet) { + + if (!inst_blocks) + WARNF("No instrumentation targets found."); + else { + + char modeline[100]; + snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); + OKF("Instrumented %u locations (%llu, %llu) with no collisions (on " + "average %llu " + "collisions would be in afl-gcc/afl-clang-fast) (%s mode).", + inst_blocks, total_rs, total_hs, calculateCollisions(inst_blocks), + modeline); + + } + + } + + return true; + + } + +}; // end of struct InsTrim + +} // end of anonymous namespace + +char InsTrimLTO::ID = 0; + +static void registerInsTrimLTO(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + PM.add(new InsTrimLTO()); + +} + +static RegisterPass X("afl-lto-instrim", + "afl++ InsTrim LTO instrumentation pass", + false, false); + +static RegisterStandardPasses RegisterInsTrimLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerInsTrimLTO); + -- cgit 1.4.1 From b7e574607c1b153a1c64fa1108a3b1b157cbd77c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 6 May 2020 13:27:12 +0200 Subject: rename pass --- llvm_mode/afl-llvm-lto-instrim.so.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'llvm_mode/afl-llvm-lto-instrim.so.cc') diff --git a/llvm_mode/afl-llvm-lto-instrim.so.cc b/llvm_mode/afl-llvm-lto-instrim.so.cc index a5bc337f..9fd3e3ec 100644 --- a/llvm_mode/afl-llvm-lto-instrim.so.cc +++ b/llvm_mode/afl-llvm-lto-instrim.so.cc @@ -114,7 +114,7 @@ struct InsTrimLTO : public ModulePass { if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { - SAYF(cCYA "LLVMInsTrimLTO" VERSION cRST + SAYF(cCYA "InsTrimLTO" VERSION cRST " by csienslab and Marc \"vanHauser\" Heuse\n"); } else -- cgit 1.4.1 From 140053502bd5ce162ab7e6bfbb151494381d704c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 7 May 2020 08:08:20 +0200 Subject: import transform fix into autodict, code-format --- examples/afl_untracer/afl-untracer.c | 40 +++++++++++++++------------- llvm_mode/afl-clang-fast.c | 6 +++-- llvm_mode/afl-llvm-lto-instrim.so.cc | 24 +++++++++++------ llvm_mode/afl-llvm-lto-instrumentation.so.cc | 24 +++++++++++------ 4 files changed, 58 insertions(+), 36 deletions(-) (limited to 'llvm_mode/afl-llvm-lto-instrim.so.cc') diff --git a/examples/afl_untracer/afl-untracer.c b/examples/afl_untracer/afl-untracer.c index 99f06f36..5dbc71bf 100644 --- a/examples/afl_untracer/afl-untracer.c +++ b/examples/afl_untracer/afl-untracer.c @@ -279,12 +279,13 @@ library_list_t *find_library(char *name) { /* for having an easy breakpoint after load the shared library */ // this seems to work for clang too. nice :) requires gcc 4.4+ #pragma GCC push_options -#pragma GCC optimize ("O0") -void breakpoint() { +#pragma GCC optimize("O0") +void breakpoint() { if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n"); } + #pragma GCC pop_options /* Error reporting to forkserver controller */ @@ -470,7 +471,7 @@ void setup_trap_instrumentation() { FILE *patches = fopen(filename, "r"); if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename); - // Index into the coverage bitmap for the current trap instruction. + // Index into the coverage bitmap for the current trap instruction. #ifdef __aarch64__ uint64_t bitmap_index = 0; #else @@ -507,11 +508,13 @@ void setup_trap_instrumentation() { PROT_READ | PROT_WRITE | PROT_EXEC) != 0) FATAL("Failed to mprotect library %s writable", line); - // Create shadow memory. + // Create shadow memory. #ifdef __aarch64__ for (int i = 0; i < 8; i++) { + #else for (int i = 0; i < 4; i++) { + #endif void *shadow_addr = SHADOW(lib_addr + i); @@ -540,16 +543,17 @@ void setup_trap_instrumentation() { FATAL("Too many basic blocks to instrument"); #ifdef __arch64__ - uint64_t + uint64_t #else - uint32_t + uint32_t #endif - *shadow = SHADOW(lib_addr + offset); + *shadow = SHADOW(lib_addr + offset); if (*shadow != 0) continue; // skip duplicates // Make lookup entry in shadow memory. -#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || defined(__i386__)) +#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || \ + defined(__i386__)) // this is for Intel x64 @@ -566,10 +570,10 @@ void setup_trap_instrumentation() { // this is for aarch64 - uint32_t *patch_bytes = (uint32_t*)(lib_addr + offset); - uint32_t orig_bytes = *patch_bytes; + uint32_t *patch_bytes = (uint32_t *)(lib_addr + offset); + uint32_t orig_bytes = *patch_bytes; *shadow = (bitmap_index << 32) | orig_bytes; - *patch_bytes = 0xd4200000; // replace instruction with debug trap + *patch_bytes = 0xd4200000; // replace instruction with debug trap if (debug) fprintf(stderr, "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %016x\n", @@ -577,14 +581,14 @@ void setup_trap_instrumentation() { bitmap_index, *shadow); #else - // this will be ARM and AARCH64 - // for ARM we will need to identify if the code is in thumb or ARM + // this will be ARM and AARCH64 + // for ARM we will need to identify if the code is in thumb or ARM #error "non x86_64/aarch64 not supported yet" - //__arm__: - // linux thumb: 0xde01 - // linux arm: 0xe7f001f0 - //__aarch64__: - // linux aarch64: 0xd4200000 + //__arm__: + // linux thumb: 0xde01 + // linux arm: 0xe7f001f0 + //__aarch64__: + // linux aarch64: 0xd4200000 #endif bitmap_index++; diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 1f3463eb..42b02bdd 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -716,9 +716,11 @@ int main(int argc, char **argv, char **envp) { } } - + if (instrument_opt_mode && lto_mode) - FATAL("CTX and NGRAM can not be used in LTO mode (and would make LTO useless)"); + FATAL( + "CTX and NGRAM can not be used in LTO mode (and would make LTO " + "useless)"); if (!instrument_opt_mode) { diff --git a/llvm_mode/afl-llvm-lto-instrim.so.cc b/llvm_mode/afl-llvm-lto-instrim.so.cc index 9fd3e3ec..f862e091 100644 --- a/llvm_mode/afl-llvm-lto-instrim.so.cc +++ b/llvm_mode/afl-llvm-lto-instrim.so.cc @@ -346,11 +346,15 @@ struct InsTrimLTO : public ModulePass { if (auto *Var = dyn_cast(Ptr->getOperand(0))) { - if (auto *Array = dyn_cast( - Var->getInitializer())) { + if (Var->hasInitializer()) { - HasStr2 = true; - Str2 = Array->getAsString().str(); + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr2 = true; + Str2 = Array->getAsString().str(); + + } } @@ -419,11 +423,15 @@ struct InsTrimLTO : public ModulePass { if (auto *Var = dyn_cast(Ptr->getOperand(0))) { - if (auto *Array = dyn_cast( - Var->getInitializer())) { + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr1 = true; + Str1 = Array->getAsString().str(); - HasStr1 = true; - Str1 = Array->getAsString().str(); + } } diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 79081d37..0e353fdf 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -326,11 +326,15 @@ bool AFLLTOPass::runOnModule(Module &M) { if (auto *Var = dyn_cast(Ptr->getOperand(0))) { - if (auto *Array = - dyn_cast(Var->getInitializer())) { + if (Var->hasInitializer()) { - HasStr2 = true; - Str2 = Array->getAsString().str(); + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr2 = true; + Str2 = Array->getAsString().str(); + + } } @@ -398,11 +402,15 @@ bool AFLLTOPass::runOnModule(Module &M) { if (auto *Var = dyn_cast(Ptr->getOperand(0))) { - if (auto *Array = - dyn_cast(Var->getInitializer())) { + if (Var->hasInitializer()) { + + if (auto *Array = dyn_cast( + Var->getInitializer())) { + + HasStr1 = true; + Str1 = Array->getAsString().str(); - HasStr1 = true; - Str1 = Array->getAsString().str(); + } } -- cgit 1.4.1 From d048af11cd43caf9fc9a8dc2e39a41b33600448f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 7 May 2020 11:57:12 +0200 Subject: calculate correct collisions for classic in InsTrimLTO --- GNUmakefile | 2 +- llvm_mode/afl-llvm-lto-instrim.so.cc | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) (limited to 'llvm_mode/afl-llvm-lto-instrim.so.cc') diff --git a/GNUmakefile b/GNUmakefile index df1434a0..de89c836 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -204,7 +204,7 @@ ifdef ASAN_BUILD endif ifdef PROFILING - $(info Compiling profiling version of binaries) + $(info Compiling with profiling information, for analysis: gprof ./afl-fuzz gmon.out > prof.txt) CFLAGS+=-pg LDFLAGS+=-pg endif diff --git a/llvm_mode/afl-llvm-lto-instrim.so.cc b/llvm_mode/afl-llvm-lto-instrim.so.cc index f862e091..6371a3cc 100644 --- a/llvm_mode/afl-llvm-lto-instrim.so.cc +++ b/llvm_mode/afl-llvm-lto-instrim.so.cc @@ -855,6 +855,33 @@ struct InsTrimLTO : public ModulePass { } + // count basic blocks for comparison with classic instrumentation + + u32 edges = 0; + for (auto &F : M) { + + if (F.size() < function_minimum_size) continue; + + for (auto &BB : F) { + + bool would_instrument = false; + + for (BasicBlock *Pred : predecessors(&BB)) { + + int count = 0; + for (BasicBlock *Succ : successors(Pred)) + if (Succ != NULL) count++; + + if (count > 1) return true; + + } + + if (would_instrument == true) edges++; + + } + + } + /* Say something nice. */ if (!be_quiet) { @@ -871,9 +898,9 @@ struct InsTrimLTO : public ModulePass { getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); OKF("Instrumented %u locations (%llu, %llu) with no collisions (on " - "average %llu " - "collisions would be in afl-gcc/afl-clang-fast) (%s mode).", - inst_blocks, total_rs, total_hs, calculateCollisions(inst_blocks), + "average %llu collisions would be in afl-gcc/afl-clang-fast for %u " + "edges) (%s mode).", + inst_blocks, total_rs, total_hs, calculateCollisions(edges), edges, modeline); } -- cgit 1.4.1 From ef2ccc8117bb899616472e2d95525ae0ca1a2098 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 7 May 2020 14:59:12 +0200 Subject: added AFL_LLVM_SKIPSINGLEBLOCK and changed default behaviour to instrument single block functions --- docs/Changelog.md | 2 ++ docs/env_variables.md | 8 ++++---- llvm_mode/LLVMInsTrim.so.cc | 5 +++-- llvm_mode/afl-llvm-lto-instrim.so.cc | 3 ++- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 7 ++++++- llvm_mode/afl-llvm-pass.so.cc | 17 ++++++++++++----- src/afl-common.c | 18 +++++++++--------- 7 files changed, 38 insertions(+), 22 deletions(-) (limited to 'llvm_mode/afl-llvm-lto-instrim.so.cc') diff --git a/docs/Changelog.md b/docs/Changelog.md index 35240021..8bcc8949 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,6 +16,8 @@ sending a mail to . - an old, old bug in afl that would show negative stability in rare circumstances is now hopefully fixed - llvm_mode: + - afl-clang-fast/lto now do not skip single block functions. This + behaviour can be reactivated with AFL_LLVM_SKIPSINGLEBLOCK - if LLVM 11 is installed the posix shm_open+mmap is used and a fixed address for the shared memory map is used as this increases the fuzzing speed diff --git a/docs/env_variables.md b/docs/env_variables.md index f5d28981..36e5a432 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -83,6 +83,10 @@ tools make fairly broad use of environmental variables: The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset of the settings discussed in section #1, with the exception of: + - Setting AFL_LLVM_SKIPSINGLEBLOCK=1 will skip instrumenting + functions with a single basic block. This is useful for most C and + some C++ targets. This works for all instrumentation modes. + - AFL_AS, since this toolchain does not directly invoke GNU as. - TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are @@ -156,10 +160,6 @@ 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 many times it was called (unless it is a complex loop). - - Setting AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK=1 will skip instrumenting - functions with a single basic block. This is useful for most C and - some C++ targets. - See llvm_mode/README.instrim.md ### NGRAM diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index ad046a8b..ed6c79e8 100644 --- a/llvm_mode/LLVMInsTrim.so.cc +++ b/llvm_mode/LLVMInsTrim.so.cc @@ -134,7 +134,8 @@ struct InsTrim : public ModulePass { } - if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") != NULL) + if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || + getenv("AFL_LLVM_SKIPSINGLEBLOCK")) function_minimum_size = 2; unsigned PrevLocSize = 0; @@ -394,7 +395,7 @@ struct InsTrim : public ModulePass { if ((callInst = dyn_cast(&IN))) { Function *Callee = callInst->getCalledFunction(); - if (!Callee || Callee->size() < 2) + if (!Callee || Callee->size() < function_minimum_size) continue; else { diff --git a/llvm_mode/afl-llvm-lto-instrim.so.cc b/llvm_mode/afl-llvm-lto-instrim.so.cc index 6371a3cc..a686bb81 100644 --- a/llvm_mode/afl-llvm-lto-instrim.so.cc +++ b/llvm_mode/afl-llvm-lto-instrim.so.cc @@ -172,7 +172,8 @@ struct InsTrimLTO : public ModulePass { } - if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") != NULL) + if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || + getenv("AFL_LLVM_SKIPSINGLEBLOCK")) function_minimum_size = 2; // this is our default diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 0e353fdf..f44b336e 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -87,6 +87,7 @@ class AFLLTOPass : public ModulePass { protected: int afl_global_id = 1, debug = 0, autodictionary = 0; + uint32_t function_minimum_size = 1; uint32_t be_quiet = 0, inst_blocks = 0, inst_funcs = 0, total_instr = 0; uint64_t map_addr = 0x10000; char * skip_nozero = NULL; @@ -124,6 +125,10 @@ bool AFLLTOPass::runOnModule(Module &M) { if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; + if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || + getenv("AFL_LLVM_SKIPSINGLEBLOCK")) + function_minimum_size = 2; + if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { uint64_t val; @@ -189,7 +194,7 @@ bool AFLLTOPass::runOnModule(Module &M) { // fprintf(stderr, "DEBUG: Function %s\n", F.getName().str().c_str()); - if (F.size() < 2) continue; + if (F.size() < function_minimum_size) continue; if (isBlacklisted(&F)) continue; std::vector InsBlocks; diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 0d9e0aba..2d23ad21 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -84,6 +84,7 @@ class AFLCoverage : public ModulePass { uint32_t ngram_size = 0; uint32_t debug = 0; uint32_t map_size = MAP_SIZE; + uint32_t function_minimum_size = 1; char * ctx_str = NULL, *skip_nozero = NULL; }; @@ -182,6 +183,10 @@ bool AFLCoverage::runOnModule(Module &M) { #endif skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || + getenv("AFL_LLVM_SKIPSINGLEBLOCK")) + function_minimum_size = 2; + unsigned PrevLocSize = 0; char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); @@ -294,13 +299,15 @@ bool AFLCoverage::runOnModule(Module &M) { if (!isInWhitelist(&F)) continue; + if (F.size() < function_minimum_size) continue; + for (auto &BB : F) { BasicBlock::iterator IP = BB.getFirstInsertionPt(); IRBuilder<> IRB(&(*IP)); // Context sensitive coverage - if (ctx_str && &BB == &F.getEntryBlock() && F.size() > 1) { + if (ctx_str && &BB == &F.getEntryBlock()) { // load the context ID of the previous function and write to to a local // variable on the stack @@ -318,7 +325,7 @@ bool AFLCoverage::runOnModule(Module &M) { if ((callInst = dyn_cast(&IN))) { Function *Callee = callInst->getCalledFunction(); - if (!Callee || Callee->size() < 2) + if (!Callee || Callee->size() < function_minimum_size) continue; else { @@ -389,11 +396,11 @@ bool AFLCoverage::runOnModule(Module &M) { } // fprintf(stderr, " == %d\n", more_than_one); - if (more_than_one != 1) { + if (F.size() > 1 && more_than_one != 1) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX - if (ctx_str && has_calls && F.size() > 1) { + if (ctx_str && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { @@ -526,7 +533,7 @@ bool AFLCoverage::runOnModule(Module &M) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX. // Currently this is only needed for the Ubuntu clang-6.0 bug - if (ctx_str && has_calls && F.size() > 1) { + if (ctx_str && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { diff --git a/src/afl-common.c b/src/afl-common.c index 54b2e790..d9d57863 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -64,15 +64,15 @@ char *afl_environment_variables[] = { "AFL_LD_PRELOAD", "AFL_LD_VERBOSE", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LLVM_CTX", "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD", "AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY", - "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK", "AFL_LLVM_LAF_SPLIT_COMPARES", - "AFL_LLVM_LAF_SPLIT_COMPARES_BITW", "AFL_LLVM_LAF_SPLIT_FLOATS", - "AFL_LLVM_LAF_SPLIT_SWITCHES", "AFL_LLVM_LAF_TRANSFORM_COMPARES", - "AFL_LLVM_MAP_ADDR", "AFL_LLVM_MAP_DYNAMIC", "AFL_LLVM_NGRAM_SIZE", - "AFL_NGRAM_SIZE", "AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST", - "AFL_LLVM_SKIP_NEVERZERO", "AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID", - "AFL_LLVM_LTO_DONTWRITEID", "AFL_NO_ARITH", "AFL_NO_BUILTIN", - "AFL_NO_CPU_RED", "AFL_NO_FORKSRV", "AFL_NO_UI", "AFL_NO_PYTHON", - "AFL_UNTRACER_FILE", + "AFL_LLVM_SKIPSINGLEBLOCK", "AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK", + "AFL_LLVM_LAF_SPLIT_COMPARES", "AFL_LLVM_LAF_SPLIT_COMPARES_BITW", + "AFL_LLVM_LAF_SPLIT_FLOATS", "AFL_LLVM_LAF_SPLIT_SWITCHES", + "AFL_LLVM_LAF_TRANSFORM_COMPARES", "AFL_LLVM_MAP_ADDR", + "AFL_LLVM_MAP_DYNAMIC", "AFL_LLVM_NGRAM_SIZE", "AFL_NGRAM_SIZE", + "AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST", "AFL_LLVM_SKIP_NEVERZERO", + "AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID", "AFL_LLVM_LTO_DONTWRITEID", + "AFL_NO_ARITH", "AFL_NO_BUILTIN", "AFL_NO_CPU_RED", "AFL_NO_FORKSRV", + "AFL_NO_UI", "AFL_NO_PYTHON", "AFL_UNTRACER_FILE", "AFL_NO_X86", // not really an env but we dont want to warn on it "AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_PATH", "AFL_PERFORMANCE_FILE", //"AFL_PERSISTENT", // not implemented anymore, so warn additionally -- cgit 1.4.1