Compare commits

...

16 Commits

Author SHA1 Message Date
91320d95ae showmap 2024-06-02 16:56:46 +02:00
c00d01bc7b score values 2024-06-02 16:37:53 +02:00
ce6ff9ff27 fixes 2024-05-27 14:43:15 +02:00
3d15e1efb4 add cmplog stats to experiment 2024-05-27 13:13:51 +02:00
59d546f39a minor enhancement 2024-05-23 21:42:17 +02:00
35156eb917 update 2024-05-17 19:04:25 +02:00
4265d70d70 fix 2024-05-17 18:40:14 +02:00
e36185cbd5 debug 2024-05-16 14:46:24 +02:00
2dc967fee3 debug 2024-05-16 14:43:35 +02:00
d4071b0fe4 debug 2024-05-16 14:40:53 +02:00
5a0a33e52a debug 2024-05-16 13:30:25 +02:00
c510ba6863 fixes and debug 2024-05-16 12:30:53 +02:00
bd4c9a5eab use score for weighting in exploit mode 2024-05-16 11:43:17 +02:00
f9e85817ad write score to map 2024-05-16 11:24:15 +02:00
8758be3630 add vuln complexity score 2024-05-15 18:03:07 +02:00
31a7ff2ba2 add loop analysis to CC 2024-05-15 15:28:03 +02:00
15 changed files with 622 additions and 62 deletions

View File

@ -200,6 +200,8 @@ struct queue_entry {
u8 *fname; /* File name for the test case */
u32 len; /* Input length */
u32 id; /* entry number in queue_buf */
u32 found;
s32 cmp, fcmp, rtn;
u8 colorized, /* Do not run redqueen stage again */
cal_failed; /* Calibration failed? */
@ -251,6 +253,9 @@ struct queue_entry {
struct skipdet_entry *skipdet_e;
u32 score; /* complexity/vulnerability score */
u64 total_execs; /* total executes of this item */
};
struct extra_data {
@ -832,6 +837,9 @@ typedef struct afl_state {
/* How often did we evict from the cache (for statistics only) */
u32 q_testcase_evictions;
/* current complexity/vulnerability score received */
u32 current_score;
/* Refs to each queue entry with cached testcase (for eviction, if cache_count
* is too large) */
struct queue_entry **q_testcase_cache;

View File

@ -21,18 +21,20 @@ static char *afl_environment_variables[] = {
"AFL_BENCH_UNTIL_CRASH", "AFL_CAL_FAST", "AFL_CC", "AFL_CC_COMPILER",
"AFL_CMIN_ALLOW_ANY", "AFL_CMIN_CRASHES_ONLY", "AFL_CMPLOG_ONLY_NEW",
"AFL_CODE_END", "AFL_CODE_START", "AFL_COMPCOV_BINNAME",
"AFL_DUMP_CYCLOMATIC_COMPLEXITY", "AFL_CMPLOG_MAX_LEN", "AFL_COMPCOV_LEVEL",
"AFL_CRASH_EXITCODE", "AFL_CRASHING_SEEDS_AS_NEW_CRASH",
"AFL_CUSTOM_MUTATOR_LIBRARY", "AFL_CUSTOM_MUTATOR_ONLY",
"AFL_CUSTOM_INFO_PROGRAM", "AFL_CUSTOM_INFO_PROGRAM_ARGV",
"AFL_CUSTOM_INFO_PROGRAM_INPUT", "AFL_CUSTOM_INFO_OUT", "AFL_CXX",
"AFL_CYCLE_SCHEDULES", "AFL_DEBUG", "AFL_DEBUG_CHILD", "AFL_DEBUG_GDB",
"AFL_DEBUG_UNICORN", "AFL_DISABLE_REDUNDANT", "AFL_NO_REDUNDANT",
"AFL_DISABLE_TRIM", "AFL_NO_TRIM", "AFL_DISABLE_LLVM_INSTRUMENTATION",
"AFL_DONT_OPTIMIZE", "AFL_DRIVER_STDERR_DUPLICATE_FILENAME",
"AFL_DUMB_FORKSRV", "AFL_EARLY_FORKSERVER", "AFL_ENTRYPOINT",
"AFL_EXIT_WHEN_DONE", "AFL_EXIT_ON_TIME", "AFL_EXIT_ON_SEED_ISSUES",
"AFL_FAST_CAL", "AFL_FINAL_SYNC", "AFL_FORCE_UI", "AFL_FRIDA_DEBUG_MAPS",
"AFL_DUMP_QUEUE_ON_EXIT", "AFL_DUMP_CYCLOMATIC_COMPLEXITY",
"AFL_DUMP_VULNERABILITY_COMPLEXITY", "AFL_CMPLOG_MAX_LEN",
"AFL_COMPCOV_LEVEL", "AFL_CRASH_EXITCODE",
"AFL_CRASHING_SEEDS_AS_NEW_CRASH", "AFL_CUSTOM_MUTATOR_LIBRARY",
"AFL_CUSTOM_MUTATOR_ONLY", "AFL_CUSTOM_INFO_PROGRAM",
"AFL_CUSTOM_INFO_PROGRAM_ARGV", "AFL_CUSTOM_INFO_PROGRAM_INPUT",
"AFL_CUSTOM_INFO_OUT", "AFL_CXX", "AFL_CYCLE_SCHEDULES", "AFL_DEBUG",
"AFL_DEBUG_CHILD", "AFL_DEBUG_GDB", "AFL_DEBUG_UNICORN",
"AFL_DISABLE_REDUNDANT", "AFL_NO_REDUNDANT", "AFL_DISABLE_TRIM",
"AFL_NO_TRIM", "AFL_DISABLE_LLVM_INSTRUMENTATION", "AFL_DONT_OPTIMIZE",
"AFL_DRIVER_STDERR_DUPLICATE_FILENAME", "AFL_DUMB_FORKSRV",
"AFL_EARLY_FORKSERVER", "AFL_ENTRYPOINT", "AFL_EXIT_WHEN_DONE",
"AFL_EXIT_ON_TIME", "AFL_EXIT_ON_SEED_ISSUES", "AFL_FAST_CAL",
"AFL_FINAL_SYNC", "AFL_FORCE_UI", "AFL_FRIDA_DEBUG_MAPS",
"AFL_FRIDA_DRIVER_NO_HOOK", "AFL_FRIDA_EXCLUDE_RANGES",
"AFL_FRIDA_INST_CACHE_SIZE", "AFL_FRIDA_INST_COVERAGE_ABSOLUTE",
"AFL_FRIDA_INST_COVERAGE_FILE", "AFL_FRIDA_INST_DEBUG_FILE",

View File

@ -60,6 +60,8 @@
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "config.h"
#include "debug.h"
@ -172,6 +174,7 @@ SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) {
}
using LoopInfoCallback = function_ref<const LoopInfo *(Function &F)>;
using DomTreeCallback = function_ref<const DominatorTree *(Function &F)>;
using PostDomTreeCallback =
function_ref<const PostDominatorTree *(Function &F)>;
@ -187,13 +190,15 @@ class ModuleSanitizerCoverageLTO
}
bool instrumentModule(Module &M, DomTreeCallback DTCallback,
PostDomTreeCallback PDTCallback);
PostDomTreeCallback PDTCallback,
LoopInfoCallback LCallback);
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
private:
void instrumentFunction(Function &F, DomTreeCallback DTCallback,
PostDomTreeCallback PDTCallback);
PostDomTreeCallback PDTCallback,
LoopInfoCallback LCallback);
/* void InjectCoverageForIndirectCalls(Function &F,
ArrayRef<Instruction *>
IndirCalls);*/
@ -250,6 +255,7 @@ class ModuleSanitizerCoverageLTO
uint32_t afl_global_id = 0;
uint32_t unhandled = 0;
uint32_t select_cnt = 0;
uint32_t dump_cc = 0, dump_vc = 0;
uint32_t instrument_ctx = 0;
uint32_t instrument_ctx_max_depth = 0;
uint32_t extra_ctx_inst = 0;
@ -291,6 +297,7 @@ class ModuleSanitizerCoverageLTOLegacyPass : public ModulePass {
AU.addRequired<DominatorTreeWrapperPass>();
AU.addRequired<PostDominatorTreeWrapperPass>();
AU.addRequired<LoopInfoWrapperPass>();
}
@ -319,7 +326,15 @@ class ModuleSanitizerCoverageLTOLegacyPass : public ModulePass {
};
return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback);
auto LoopCallback = [this](Function &F) -> const LoopInfo * {
return &this->getAnalysis<LoopInfoWrapperPass>(F).getLoopInfo();
};
ModuleSancov.instrumentModule(M, DTCallback, PDTCallback, LoopCallback);
return 1;
}
@ -372,15 +387,21 @@ PreservedAnalyses ModuleSanitizerCoverageLTO::run(Module &M,
};
if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback))
return PreservedAnalyses::none();
auto LoopCallback = [&FAM](Function &F) -> const LoopInfo * {
return PreservedAnalyses::all();
return &FAM.getResult<LoopAnalysis>(F);
};
ModuleSancov.instrumentModule(M, DTCallback, PDTCallback, LoopCallback);
return PreservedAnalyses::none();
}
bool ModuleSanitizerCoverageLTO::instrumentModule(
Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback,
LoopInfoCallback LCallback) {
if (Options.CoverageType == SanitizerCoverageOptions::SCK_None) return false;
/*
@ -474,6 +495,10 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
}
if (getenv("AFL_DUMP_CYCLOMATIC_COMPLEXITY")) { dump_cc = 1; }
if (getenv("AFL_DUMP_VULNERABILITY_COMPLEXITY")) { dump_vc = 1; }
skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO");
use_threadsafe_counters = getenv("AFL_LLVM_THREADSAFE_INST");
@ -1057,7 +1082,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule(
// M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, Int32PtrTy);
for (auto &F : M)
instrumentFunction(F, DTCallback, PDTCallback);
instrumentFunction(F, DTCallback, PDTCallback, LCallback);
// AFL++ START
if (dFile.is_open()) dFile.close();
@ -1347,7 +1372,8 @@ Function *returnOnlyCaller(Function *F) {
}
void ModuleSanitizerCoverageLTO::instrumentFunction(
Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback,
LoopInfoCallback LCallback) {
if (F.empty()) return;
if (F.getName().find(".module_ctor") != std::string::npos)
@ -1421,6 +1447,7 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
const DominatorTree *DT = DTCallback(F);
const PostDominatorTree *PDT = PDTCallback(F);
const LoopInfo *LI = LCallback(F);
bool IsLeafFunc = true;
uint32_t skip_next = 0;
uint32_t call_counter = 0, call_depth = 0;
@ -1955,6 +1982,51 @@ void ModuleSanitizerCoverageLTO::instrumentFunction(
}
unsigned int score = 0;
if (dump_cc) { score += calcCyclomaticComplexity(&F, LI); }
if (dump_vc) { score += calcVulnerabilityScore(&F, LI, DT, PDT); }
if (score) {
BasicBlock::iterator IP = F.getEntryBlock().getFirstInsertionPt();
IRBuilder<> builder(&*IP);
// Access the int32 value at u8 offset 1 (unaligned access)
LoadInst *MapPtr =
builder.CreateLoad(PointerType::get(Int8Ty, 0), AFLMapPtr);
llvm::Value *CastToInt8Ptr =
builder.CreateBitCast(MapPtr, llvm::PointerType::get(Int8Ty, 0));
llvm::Value *Int32Ptr = builder.CreateGEP(
Int8Ty, CastToInt8Ptr, llvm::ConstantInt::get(Int32Ty, 1));
llvm::Value *CastToInt32Ptr =
builder.CreateBitCast(Int32Ptr, llvm::PointerType::get(Int32Ty, 0));
// Load the unaligned int32 value
llvm::LoadInst *Load = builder.CreateLoad(Int32Ty, CastToInt32Ptr);
Load->setAlignment(llvm::Align(1));
// Value to add
llvm::Value *ValueToAdd = llvm::ConstantInt::get(Int32Ty, score);
// Perform addition and check for wrap around
llvm::Value *Add =
builder.CreateAdd(Load, ValueToAdd, "addValue", true, true);
// Check if addition wrapped (unsigned)
llvm::Value *DidWrap = builder.CreateICmpULT(Add, Load, "didWrap");
// Select the maximum value if there was a wrap, otherwise use the result
llvm::Value *MaxInt32 = llvm::ConstantInt::get(Int32Ty, UINT32_MAX);
llvm::Value *Result =
builder.CreateSelect(DidWrap, MaxInt32, Add, "selectMaxOrResult");
// Store the result back at the same unaligned offset
llvm::StoreInst *Store = builder.CreateStore(Result, CastToInt32Ptr);
Store->setAlignment(llvm::Align(1));
}
InjectCoverage(F, BlocksToInstrument, IsLeafFunc);
// InjectCoverageForIndirectCalls(F, IndirCalls);

View File

@ -70,6 +70,8 @@
#endif
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "config.h"
#include "debug.h"
@ -119,6 +121,7 @@ SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) {
}
using LoopInfoCallback = function_ref<const LoopInfo *(Function &F)>;
using DomTreeCallback = function_ref<const DominatorTree *(Function &F)>;
using PostDomTreeCallback =
function_ref<const PostDominatorTree *(Function &F)>;
@ -135,11 +138,13 @@ class ModuleSanitizerCoverageAFL
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
bool instrumentModule(Module &M, DomTreeCallback DTCallback,
PostDomTreeCallback PDTCallback);
PostDomTreeCallback PDTCallback,
LoopInfoCallback LCallback);
private:
void instrumentFunction(Function &F, DomTreeCallback DTCallback,
PostDomTreeCallback PDTCallback);
PostDomTreeCallback PDTCallback,
LoopInfoCallback LCallback);
void InjectTraceForCmp(Function &F, ArrayRef<Instruction *> CmpTraceTargets);
void InjectTraceForSwitch(Function &F,
ArrayRef<Instruction *> SwitchTraceTargets);
@ -195,7 +200,7 @@ class ModuleSanitizerCoverageAFL
SanitizerCoverageOptions Options;
uint32_t instr = 0, selects = 0, unhandled = 0, dump_cc = 0;
uint32_t instr = 0, selects = 0, unhandled = 0, dump_cc = 0, dump_vc = 0;
GlobalVariable *AFLMapPtr = NULL;
ConstantInt *One = NULL;
ConstantInt *Zero = NULL;
@ -233,8 +238,10 @@ PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M,
ModuleAnalysisManager &MAM) {
ModuleSanitizerCoverageAFL ModuleSancov(Options);
auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
auto DTCallback = [&FAM](Function &F) -> const DominatorTree *{
auto DTCallback = [&FAM](Function &F) -> const DominatorTree * {
return &FAM.getResult<DominatorTreeAnalysis>(F);
@ -246,9 +253,21 @@ PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M,
};
if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback))
auto LoopCallback = [&FAM](Function &F) -> const LoopInfo * {
return &FAM.getResult<LoopAnalysis>(F);
};
if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback, LoopCallback)) {
return PreservedAnalyses::none();
return PreservedAnalyses::all();
} else {
return PreservedAnalyses::all();
}
}
@ -324,7 +343,8 @@ Function *ModuleSanitizerCoverageAFL::CreateInitCallsForSections(
}
bool ModuleSanitizerCoverageAFL::instrumentModule(
Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback,
LoopInfoCallback LCallback) {
setvbuf(stdout, NULL, _IONBF, 0);
@ -332,6 +352,8 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
if (getenv("AFL_DUMP_CYCLOMATIC_COMPLEXITY")) { dump_cc = 1; }
if (getenv("AFL_DUMP_VULNERABILITY_COMPLEXITY")) { dump_vc = 1; }
if ((isatty(2) && !getenv("AFL_QUIET")) || debug) {
SAYF(cCYA "SanitizerCoveragePCGUARD" VERSION cRST "\n");
@ -429,7 +451,7 @@ bool ModuleSanitizerCoverageAFL::instrumentModule(
M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, Int32PtrTy);
for (auto &F : M)
instrumentFunction(F, DTCallback, PDTCallback);
instrumentFunction(F, DTCallback, PDTCallback, LCallback);
Function *Ctor = nullptr;
@ -568,7 +590,8 @@ static bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT,
#endif
void ModuleSanitizerCoverageAFL::instrumentFunction(
Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) {
Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback,
LoopInfoCallback LCallback) {
if (F.empty()) return;
if (!isInInstrumentList(&F, FMNAME)) return;
@ -604,6 +627,7 @@ void ModuleSanitizerCoverageAFL::instrumentFunction(
const DominatorTree *DT = DTCallback(F);
const PostDominatorTree *PDT = PDTCallback(F);
const LoopInfo *LI = LCallback(F);
bool IsLeafFunc = true;
for (auto &BB : F) {
@ -636,12 +660,55 @@ void ModuleSanitizerCoverageAFL::instrumentFunction(
}
unsigned int score = 0;
if (dump_cc) { score += calcCyclomaticComplexity(&F, LI); }
if (dump_vc) { score += calcVulnerabilityScore(&F, LI, DT, PDT); }
if (score) {
BasicBlock::iterator IP = F.getEntryBlock().getFirstInsertionPt();
IRBuilder<> builder(&*IP);
// Access the int32 value at u8 offset 1 (unaligned access)
LoadInst *MapPtr =
builder.CreateLoad(PointerType::get(Int8Ty, 0), AFLMapPtr);
llvm::Value *CastToInt8Ptr =
builder.CreateBitCast(MapPtr, llvm::PointerType::get(Int8Ty, 0));
llvm::Value *Int32Ptr = builder.CreateGEP(
Int8Ty, CastToInt8Ptr, llvm::ConstantInt::get(Int32Ty, 1));
llvm::Value *CastToInt32Ptr =
builder.CreateBitCast(Int32Ptr, llvm::PointerType::get(Int32Ty, 0));
// Load the unaligned int32 value
llvm::LoadInst *Load = builder.CreateLoad(Int32Ty, CastToInt32Ptr);
Load->setAlignment(llvm::Align(1));
// Value to add
llvm::Value *ValueToAdd = llvm::ConstantInt::get(Int32Ty, score);
// Perform addition and check for wrap around
llvm::Value *Add =
builder.CreateAdd(Load, ValueToAdd, "addValue", true, true);
// Check if addition wrapped (unsigned)
llvm::Value *DidWrap = builder.CreateICmpULT(Add, Load, "didWrap");
// Select the maximum value if there was a wrap, otherwise use the result
llvm::Value *MaxInt32 = llvm::ConstantInt::get(Int32Ty, UINT32_MAX);
llvm::Value *Result =
builder.CreateSelect(DidWrap, MaxInt32, Add, "selectMaxOrResult");
// Store the result back at the same unaligned offset
llvm::StoreInst *Store = builder.CreateStore(Result, CastToInt32Ptr);
Store->setAlignment(llvm::Align(1));
}
InjectCoverage(F, BlocksToInstrument, IsLeafFunc);
// InjectTraceForCmp(F, CmpTraceTargets);
// InjectTraceForSwitch(F, SwitchTraceTargets);
if (dump_cc) { calcCyclomaticComplexity(&F); }
}
GlobalVariable *ModuleSanitizerCoverageAFL::CreateFunctionLocalArrayInSection(

View File

@ -1849,7 +1849,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
to avoid duplicate calls (which can happen as an artifact of the underlying
implementation in LLVM). */
if (__afl_final_loc < 5) __afl_final_loc = 5; // we skip the first 5 entries
if (__afl_final_loc < 4) __afl_final_loc = 4; // we skip the first 5 entries
*(start++) = ++__afl_final_loc;

View File

@ -14,7 +14,21 @@
#include <fstream>
#include <cmath>
#include <llvm/Support/raw_ostream.h>
#if LLVM_VERSION_MAJOR >= 13
#include "llvm/Support/raw_ostream.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Operator.h"
#include "llvm/IR/Dominators.h"
#include "llvm/Analysis/PostDominators.h"
#endif
// #define LEOPARD_USE_WEIGHTS 1
#define IS_EXTERN extern
#include "afl-llvm-common.h"
@ -26,11 +40,79 @@ static std::list<std::string> allowListFunctions;
static std::list<std::string> denyListFiles;
static std::list<std::string> denyListFunctions;
unsigned int calcCyclomaticComplexity(llvm::Function *F) {
#if LLVM_VERSION_MAJOR >= 13
// Leopard complexity calculations
#ifndef LEOPARD_USE_WEIGHTS
#define C1_WEIGHT 1.0
#define C2_WEIGHT 1.0
#define C3_WEIGHT 1.0
#define C4_WEIGHT 1.0
#define V1_WEIGHT 1.0
#define V2_WEIGHT 1.0
#define V3_WEIGHT 1.0
#define V4_WEIGHT 1.0
#define V5_WEIGHT 1.0
#define V6_WEIGHT 1.0
#define V7_WEIGHT 1.0
#define V8_WEIGHT 1.0
#define V9_WEIGHT 1.0
#define V10_WEIGHT 1.0
#define V11_WEIGHT 1.0
#else
// Cyclomatic weights
#define C1_WEIGHT 1.0
#define C2_WEIGHT 1.0
#define C3_WEIGHT 1.0
#define C4_WEIGHT 1.0
// Vulnerability weights
#define V1_WEIGHT 1.5
#define V2_WEIGHT 3.25
#define V3_WEIGHT 4.25
#define V4_WEIGHT 3.0
#define V5_WEIGHT 4.25
#define V6_WEIGHT 7.75
#define V7_WEIGHT 2.5
#define V8_WEIGHT 2.5
#define V9_WEIGHT 4.0
#define V10_WEIGHT 5.25
#define V11_WEIGHT 3.5
#endif
static void countNestedLoops(Loop *L, int depth, unsigned int &loopCount,
unsigned int &nestedLoopCount,
unsigned int &maxNestingLevel) {
loopCount++;
if (!L->getSubLoops().empty()) {
// Increment nested loop count by the number of sub-loops
nestedLoopCount += L->getSubLoops().size();
// Update maximum nesting level
if (depth > maxNestingLevel) { maxNestingLevel = depth; }
// Recursively count sub-loops
for (Loop *SubLoop : L->getSubLoops()) {
countNestedLoops(SubLoop, depth + 1, loopCount, nestedLoopCount,
maxNestingLevel);
}
}
}
unsigned int calcCyclomaticComplexity(llvm::Function *F,
const llvm::LoopInfo *LI) {
unsigned int numBlocks = 0;
unsigned int numEdges = 0;
unsigned int numCalls = 0;
unsigned int numLoops = 0;
unsigned int numNestedLoops = 0;
unsigned int maxLoopNesting = 0;
// Iterate through each basic block in the function
for (BasicBlock &BB : *F) {
@ -55,22 +137,197 @@ unsigned int calcCyclomaticComplexity(llvm::Function *F) {
}
for (Loop *L : *LI) {
countNestedLoops(L, 1, numLoops, numNestedLoops, maxLoopNesting);
}
// Cyclomatic Complexity V(G) = E - N + 2P
// For a single function, P (number of connected components) is 1
// Calls are considered to be an edge
unsigned int CC = 2 + numCalls + numEdges - numBlocks;
unsigned int cc =
(unsigned int)(C1_WEIGHT * (double)(2 + numCalls + numEdges - numBlocks) +
C2_WEIGHT * (double)numLoops +
C3_WEIGHT * (double)numNestedLoops +
C4_WEIGHT * (double)maxLoopNesting);
// if (debug) {
fprintf(stderr, "CyclomaticComplexity for %s: %u\n",
F->getName().str().c_str(), CC);
fprintf(stderr,
"CyclomaticComplexity for %s: %u (calls=%u edges=%u blocks=%u "
"loops=%u nested_loops=%u max_loop_nesting_level=%u)\n",
F->getName().str().c_str(), cc, numCalls, numEdges, numBlocks,
numLoops, numNestedLoops, maxLoopNesting);
//}
return CC;
return cc;
}
unsigned int calcVulnerabilityScore(llvm::Function *F, const llvm::LoopInfo *LI,
const llvm::DominatorTree *DT,
const llvm::PostDominatorTree *PDT) {
unsigned int score = 0;
// V1 and V2
unsigned paramCount = F->arg_size();
unsigned calledParamCount = 0;
// V3, V4 and V5
unsigned pointerArithCount = 0;
unsigned totalPointerArithParams = 0;
unsigned maxPointerArithVars = 0;
// V6 to V11
unsigned nestedControlStructCount = 0;
unsigned maxNestingLevel = 0;
unsigned maxControlDependentControls = 0;
unsigned maxDataDependentControls = 0;
unsigned ifWithoutElseCount = 0;
unsigned controlPredicateVarCount = 0;
std::function<void(Loop *, unsigned)> countNestedLoops = [&](Loop *L,
unsigned depth) {
nestedControlStructCount++;
if (depth > maxNestingLevel) { maxNestingLevel = depth; }
for (Loop *SubLoop : L->getSubLoops()) {
countNestedLoops(SubLoop, depth + 1);
}
};
for (Loop *TopLoop : *LI) {
countNestedLoops(TopLoop, 1);
}
for (inst_iterator I = inst_begin(*F), E = inst_end(*F); I != E; ++I) {
if (CallInst *CI = dyn_cast<CallInst>(&*I)) {
if (Function *CalledF = CI->getCalledFunction()) {
calledParamCount += CalledF->arg_size();
}
}
if (auto *GEP = dyn_cast<GetElementPtrInst>(&*I)) {
pointerArithCount++;
unsigned numPointerArithVars = GEP->getNumOperands();
totalPointerArithParams += numPointerArithVars;
if (numPointerArithVars > maxPointerArithVars) {
maxPointerArithVars = numPointerArithVars;
}
}
if (BranchInst *BI = dyn_cast<BranchInst>(&*I)) {
if (BI->isConditional()) {
unsigned controlDependentCount = 0;
unsigned dataDependentCount = 0;
for (Use &U : BI->operands()) {
if (Instruction *Op = dyn_cast<Instruction>(U.get())) {
if (DT->dominates(Op, &*I)) { controlDependentCount++; }
if (PDT->dominates(Op, &*I)) { dataDependentCount++; }
}
}
if (controlDependentCount > maxControlDependentControls) {
maxControlDependentControls = controlDependentCount;
}
if (dataDependentCount > maxDataDependentControls) {
maxDataDependentControls = dataDependentCount;
}
// Check for if() without else
BasicBlock *TrueBB = BI->getSuccessor(0);
BasicBlock *FalseBB = BI->getSuccessor(1);
if (TrueBB && FalseBB) {
if (TrueBB->getSinglePredecessor() == &*I->getParent() &&
FalseBB->empty()) {
ifWithoutElseCount++;
}
}
// Count variables involved in control predicates
if (ICmpInst *ICmp = dyn_cast<ICmpInst>(BI->getCondition())) {
controlPredicateVarCount += ICmp->getNumOperands();
} else if (BinaryOperator *BinOp =
dyn_cast<BinaryOperator>(BI->getCondition())) {
controlPredicateVarCount += BinOp->getNumOperands();
} else if (SelectInst *Select =
dyn_cast<SelectInst>(BI->getCondition())) {
controlPredicateVarCount += Select->getNumOperands();
}
}
}
}
score = (unsigned int)(V1_WEIGHT * (double)paramCount +
V2_WEIGHT * (double)calledParamCount +
V3_WEIGHT * (double)pointerArithCount +
V4_WEIGHT * (double)totalPointerArithParams +
V5_WEIGHT * (double)maxPointerArithVars +
V6_WEIGHT * (double)nestedControlStructCount +
V7_WEIGHT * (double)maxNestingLevel +
V8_WEIGHT * (double)maxControlDependentControls +
V9_WEIGHT * (double)maxDataDependentControls +
V10_WEIGHT * (double)ifWithoutElseCount +
V11_WEIGHT * (double)controlPredicateVarCount);
fprintf(stderr,
"VulnerabilityScore for %s: %u (paramCount=%u "
"calledParamCount=%u|pointerArithCount=%u totalPointerArithParams=%u "
"maxPointerArithVars=%u|maxNestingLevel=%u "
"maxControlDependentControls=%u maxDataDependentControls=%u "
"ifWithoutElseCount=%u controlPredicateVarCount=%u)\n",
F->getName().str().c_str(), score, paramCount, calledParamCount,
pointerArithCount, totalPointerArithParams, maxPointerArithVars,
maxNestingLevel, maxControlDependentControls,
maxDataDependentControls, ifWithoutElseCount,
controlPredicateVarCount);
return score;
}
#endif
char *getBBName(const llvm::BasicBlock *BB) {
static char *name;
@ -136,7 +393,11 @@ bool isIgnoreFunction(const llvm::Function *F) {
for (auto const &ignoreListFunc : ignoreList) {
#if LLVM_VERSION_MAJOR >= 19
if (F->getName().starts_with(ignoreListFunc)) { return true; }
#else
if (F->getName().startswith(ignoreListFunc)) { return true; }
#endif
}

View File

@ -12,6 +12,7 @@
#include <sys/time.h>
#include "llvm/Config/llvm-config.h"
#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 5
typedef long double max_align_t;
#endif
@ -26,6 +27,19 @@ typedef long double max_align_t;
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#endif
#if LLVM_VERSION_MAJOR > 12
#include "llvm/Support/raw_ostream.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Operator.h"
#include "llvm/IR/Dominators.h"
#include "llvm/Analysis/PostDominators.h"
#endif
#if LLVM_VERSION_MAJOR > 3 || \
(LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4)
#include "llvm/IR/DebugInfo.h"
@ -55,7 +69,11 @@ void initInstrumentList();
bool isInInstrumentList(llvm::Function *F, std::string Filename);
unsigned long long int calculateCollisions(uint32_t edges);
void scanForDangerousFunctions(llvm::Module *M);
unsigned int calcCyclomaticComplexity(llvm::Function *F);
unsigned int calcCyclomaticComplexity(llvm::Function *F,
const llvm::LoopInfo *LI);
unsigned int calcVulnerabilityScore(llvm::Function *F, const llvm::LoopInfo *LI,
const llvm::DominatorTree *DT,
const llvm::PostDominatorTree *PDT);
#ifndef IS_EXTERN
#define IS_EXTERN

View File

@ -57,6 +57,12 @@
#include <set>
#include "afl-llvm-common.h"
#if LLVM_MAJOR >= 19
#define STARTSWITH starts_with
#else
#define STARTSWITH startswith
#endif
using namespace llvm;
namespace {
@ -532,10 +538,10 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp,
}
if (!isSizedcmp) needs_null = true;
if (Callee->getName().startswith("g_") ||
Callee->getName().startswith("curl_") ||
Callee->getName().startswith("Curl_") ||
Callee->getName().startswith("xml"))
if (Callee->getName().STARTSWITH("g_") ||
Callee->getName().STARTSWITH("curl_") ||
Callee->getName().STARTSWITH("Curl_") ||
Callee->getName().STARTSWITH("xml"))
nullCheck = true;
Value *sizedValue = isSizedcmp ? callInst->getArgOperand(2) : NULL;

View File

@ -578,7 +578,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
void *nyx_config = fsrv->nyx_handlers->nyx_config_load(fsrv->target_path);
fsrv->nyx_handlers->nyx_config_set_workdir_path(nyx_config, workdir_path);
fsrv->nyx_handlers->nyx_config_set_input_buffer_size(nyx_config, fsrv->max_length);
fsrv->nyx_handlers->nyx_config_set_input_buffer_size(nyx_config,
fsrv->max_length);
fsrv->nyx_handlers->nyx_config_set_input_buffer_write_protection(nyx_config,
true);

View File

@ -481,6 +481,14 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
s32 fd;
u64 cksum = 0;
// will be classified away otherwise
if (unlikely((afl->current_score = *(u32 *)((u8 *)afl->fsrv.trace_bits + 1)) >
0)) {
memset(afl->fsrv.trace_bits + 1, 0, 4);
}
/* Update path frequency. */
/* Generating a hash on every input is super expensive. Bad idea and should

View File

@ -60,27 +60,61 @@ inline u32 select_next_queue_entry(afl_state_t *afl) {
}
// #define DEBUG_QUEUE 1
double compute_weight(afl_state_t *afl, struct queue_entry *q,
double avg_exec_us, double avg_bitmap_size,
double avg_top_size) {
double avg_top_size, double avg_score) {
double weight = 1.0;
/*
if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) {
if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) {
u32 hits = afl->n_fuzz[q->n_fuzz_entry];
if (likely(hits)) { weight /= (log10(hits) + 1); }
u32 hits = afl->n_fuzz[q->n_fuzz_entry];
if (likely(hits)) { weight /= (log10(hits) + 1); }
}
}
#ifdef DEBUG_QUEUE
fprintf(stderr, "WEIGHT id=%u fname=%s start_weight=1.0\n", q->id,
q->fname); fprintf(stderr, " after step 1: %.2f (log10(hits))\n", weight);
#endif
if (likely(afl->schedule < RARE)) { weight *= (avg_exec_us / q->exec_us); }
#ifdef DEBUG_QUEUE
fprintf(stderr, " after step 2: %.2f (exec_us)\n", weight);
#endif
weight *= (log(q->bitmap_size) / avg_bitmap_size);
#ifdef DEBUG_QUEUE
fprintf(stderr, " after step 3: %.2f (log(bitmap_size))\n", weight);
#endif
weight *= (1 + (q->tc_ref / avg_top_size));
#ifdef DEBUG_QUEUE
fprintf(stderr, " after step 4: %.2f (top_size)\n", weight);
#endif
if (unlikely(avg_score != 0.0)) { weight *= (q->score / avg_score); }
#ifdef DEBUG_QUEUE
fprintf(stderr, " after step 5: %.2f (score)\n", weight);
#endif
if (likely(afl->schedule < RARE)) { weight *= (avg_exec_us / q->exec_us); }
weight *= (log(q->bitmap_size) / avg_bitmap_size);
weight *= (1 + (q->tc_ref / avg_top_size));
if (unlikely(weight < 0.1)) { weight = 0.1; }
if (unlikely(q->favored)) {
if (unlikely(weight < 0.1)) { weight = 0.1; }
if (unlikely(q->favored)) { weight *= 5; }
if (unlikely(!q->was_fuzzed)) { weight *= 2; }
if (unlikely(q->fs_redundant)) { weight *= 0.8; }
weight += 1;
weight *= 5;
}
#ifdef DEBUG_QUEUE
fprintf(stderr, " after step 6: %.2f (favored)\n", weight);
#endif
*/
if (unlikely(!q->was_fuzzed)) { weight *= 5; }
#ifdef DEBUG_QUEUE
fprintf(stderr, " after step 7: %.2f (was_fuzzed)\n", weight);
#endif
if (unlikely(q->fs_redundant)) { weight = 0.0; }
#ifdef DEBUG_QUEUE
fprintf(stderr, " after final step: %.2f (fs_redundant)\n", weight);
#endif
return weight;
@ -90,7 +124,8 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q,
void create_alias_table(afl_state_t *afl) {
u32 n = afl->queued_items, i = 0, nSmall = 0, nLarge = n - 1;
u32 n = afl->queued_items, i = 0, nSmall = 0, nLarge = n - 1,
exploit = afl->fuzz_mode;
double sum = 0;
double *P = (double *)afl_realloc(AFL_BUF_PARAM(out), n * sizeof(double));
@ -118,6 +153,7 @@ void create_alias_table(afl_state_t *afl) {
double avg_exec_us = 0.0;
double avg_bitmap_size = 0.0;
double avg_top_size = 0.0;
double avg_score = 0.0;
u32 active = 0;
for (i = 0; i < n; i++) {
@ -130,6 +166,7 @@ void create_alias_table(afl_state_t *afl) {
avg_exec_us += q->exec_us;
avg_bitmap_size += log(q->bitmap_size);
avg_top_size += q->tc_ref;
if (exploit) { avg_score += /*log(*/ q->score /*)*/; }
++active;
}
@ -140,14 +177,16 @@ void create_alias_table(afl_state_t *afl) {
avg_bitmap_size /= active;
avg_top_size /= active;
if (exploit) { avg_score /= active; }
for (i = 0; i < n; i++) {
struct queue_entry *q = afl->queue_buf[i];
if (likely(!q->disabled)) {
q->weight =
compute_weight(afl, q, avg_exec_us, avg_bitmap_size, avg_top_size);
q->weight = compute_weight(afl, q, avg_exec_us, avg_bitmap_size,
avg_top_size, avg_score);
q->perf_score = calculate_score(afl, q);
sum += q->weight;
@ -596,6 +635,13 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
q->trace_mini = NULL;
q->testcase_buf = NULL;
q->mother = afl->queue_cur;
q->cmp = q->fcmp = q->rtn = -1;
if (afl->queue_cur) {
afl->queue_cur->found++;
}
q->score = afl->current_score;
if (unlikely(!q->score)) { q->score = 1; }
#ifdef INTROSPECTION
q->bitsmap_size = afl->bitsmap_size;
@ -914,6 +960,8 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) {
u32 avg_bitmap_size = afl->total_bitmap_size / bitmap_entries;
u32 perf_score = 100;
return perf_score;
/* Adjust score based on execution speed of this path, compared to the
global average. Multiplier ranges from 0.1x to 3x. Fast inputs are
less expensive to fuzz, so we're giving them more air time. */

View File

@ -3072,6 +3072,8 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) {
afl->stage_max = 0;
afl->stage_cur = 0;
afl->queue_cur->cmp = afl->queue_cur->fcmp = afl->queue_cur->rtn = 0;
u32 lvl = (afl->queue_cur->colorized ? 0 : LVL1) +
(afl->cmplog_lvl == CMPLOG_LVL_MAX ? LVL3 : 0);
@ -3089,6 +3091,13 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) {
if (!afl->shm.cmp_map->headers[k].hits) { continue; }
if (afl->shm.cmp_map->headers[k].type != CMP_TYPE_INS)
afl->queue_cur->rtn++;
else if (unlikely((afl->shm.cmp_map->headers[k].attribute & 8) == 8))
afl->queue_cur->fcmp++;
else
afl->queue_cur->cmp++;
if (afl->pass_stats[k].faileds >= CMPLOG_FAIL_MAX ||
afl->pass_stats[k].total >= CMPLOG_FAIL_MAX) {

View File

@ -606,6 +606,8 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
}
q->exec_us = diff_us / afl->stage_max;
if (unlikely(!q->exec_us)) { q->exec_us = 1; }
q->bitmap_size = count_bytes(afl, afl->fsrv.trace_bits);
q->handicap = handicap;
q->cal_failed = 0;

View File

@ -1806,7 +1806,7 @@ int main(int argc, char **argv_orig, char **envp) {
afl->fsrv.use_fauxsrv = afl->non_instrumented_mode == 1 || afl->no_forkserver;
afl->fsrv.max_length = afl->max_length;
#ifdef __linux__
if (!afl->fsrv.nyx_mode) {
@ -2868,7 +2868,9 @@ int main(int argc, char **argv_orig, char **envp) {
}
u64 execs_before = afl->fsrv.total_execs;
skipped_fuzz = fuzz_one(afl);
afl->queue_cur->total_execs += afl->fsrv.total_execs - execs_before;
#ifdef INTROSPECTION
++afl->queue_cur->stats_selected;
@ -3067,6 +3069,39 @@ stop_fuzzing:
}
if (getenv("AFL_DUMP_QUEUE_ON_EXIT")) {
for (u32 mode = 0; mode < 1; mode++) {
afl->fuzz_mode = mode;
create_alias_table(afl);
fprintf(stderr, "\nQUEUE DUMP MODE: %u\n", mode);
for (u32 k = 0; k < afl->queued_items; ++k) {
struct queue_entry *q = afl->queue_buf[k];
fprintf(stderr,
"item=%u fname=%s len=%u exec_us=%llu total_execs=%llu "
"has_new_cov=%u "
"var_behavior=%u favored=%u fs_redundant=%u disabled=%u "
"bitmap_size=%u tc_ref=%u fuzz_level=%u was_fuzzed=%u "
"cmp=%d fcmp=%d rtn=%d "
"mother=%d found=%u perf_score=%.2f weight=%.2f score=%u\n",
k, q->fname, q->len, q->exec_us, q->total_execs, q->has_new_cov,
q->var_behavior, q->favored, q->fs_redundant, q->disabled,
q->bitmap_size, q->tc_ref, q->fuzz_level, q->was_fuzzed,
q->cmp, q->fcmp, q->rtn,
q->mother == NULL ? -1 : (int)q->mother->id, q->found,
q->perf_score, q->weight, q->score);
}
fprintf(stderr, "\n");
}
}
if (frida_afl_preload) { ck_free(frida_afl_preload); }
fclose(afl->fsrv.plot_file);

View File

@ -83,6 +83,8 @@ static u32 tcnt, highest; /* tuple content information */
static u32 in_len; /* Input data length */
static u32 score;
static u32 map_size = MAP_SIZE, timed_out = 0;
static bool quiet_mode, /* Hide non-essential messages? */
@ -238,6 +240,13 @@ static void at_exit_handler(void) {
static void analyze_results(afl_forkserver_t *fsrv) {
u32 i;
if (unlikely((score = *(u32 *)((u8 *)fsrv->trace_bits + 1)) > 0)) {
memset(fsrv->trace_bits + 1, 0, 4);
}
for (i = 0; i < map_size; i++) {
if (fsrv->trace_bits[i]) {
@ -269,6 +278,12 @@ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) {
}
if (unlikely((score = *(u32 *)((u8 *)fsrv->trace_bits + 1)) > 0)) {
memset(fsrv->trace_bits + 1, 0, 4);
}
if (cmin_mode &&
(fsrv->last_run_timed_out || (!caa && child_crashed != cco))) {
@ -1766,12 +1781,20 @@ int main(int argc, char **argv_orig, char **envp) {
OKF("Captured %u tuples (map size %u, highest value %u, total values %llu) "
"in '%s'." cRST,
tcnt, fsrv->real_map_size, highest, total, out_file);
if (collect_coverage)
if (collect_coverage) {
OKF("A coverage of %u edges were achieved out of %u existing (%.02f%%) "
"with %llu input files.",
tcnt, map_size, ((float)tcnt * 100) / (float)map_size,
fsrv->total_execs);
} else if (score > 0) {
OKF("Path score is %u (cyclomatic and/or vulnerability scoring).\n",
score);
}
}
if (stdin_file) {