From fc437b55e672f200a66b2d54db1834a9036ca82d Mon Sep 17 00:00:00 2001 From: "Dongjia \"toka\" Zhang" Date: Fri, 12 Apr 2024 20:28:04 +0200 Subject: [PATCH 1/2] LLVM 18 update (#167) Update to support LLVM 18 --- .github/workflows/run_tests.yml | 2 +- CMakeLists.txt | 4 ++-- README.md | 2 +- compiler/Runtime.cpp | 2 +- compiler/Symbolizer.cpp | 21 ++++++++++++--------- compiler/Symbolizer.h | 4 ++-- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 8eb6533b..aedc742b 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - llvm_version: [16, 17] + llvm_version: [16, 17, 18] steps: - uses: actions/checkout@v3 with: diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d98a9cb..3db2ba8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,8 +80,8 @@ find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake from ${LLVM_DIR}") -if (${LLVM_VERSION_MAJOR} LESS 8 OR ${LLVM_VERSION_MAJOR} GREATER 17) - message(WARNING "The software has been developed for LLVM 8 through 17; \ +if (${LLVM_VERSION_MAJOR} LESS 8 OR ${LLVM_VERSION_MAJOR} GREATER 18) + message(WARNING "The software has been developed for LLVM 8 through 18; \ it is unlikely to work with other versions!") endif() diff --git a/README.md b/README.md index 5a6afede..7fd36cd4 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ program. The actual computation happens through calls to the support library at run time. To build the pass and the support library, install LLVM (any version between 8 -and 17) and Z3 (version 4.5 or later), as well as a C++ compiler with support +and 18) and Z3 (version 4.5 or later), as well as a C++ compiler with support for C++17. LLVM lit is only needed to run the tests; if it's not packaged with your LLVM, you can get it with `pip install lit`. diff --git a/compiler/Runtime.cpp b/compiler/Runtime.cpp index a97b30f6..454819c7 100644 --- a/compiler/Runtime.cpp +++ b/compiler/Runtime.cpp @@ -37,7 +37,7 @@ SymFnT import(llvm::Module &M, llvm::StringRef name, llvm::Type *ret, Runtime::Runtime(Module &M) { IRBuilder<> IRB(M.getContext()); auto *intPtrType = M.getDataLayout().getIntPtrType(M.getContext()); - auto *ptrT = IRB.getInt8PtrTy(); + auto *ptrT = IRB.getInt8Ty()->getPointerTo(); auto *int8T = IRB.getInt8Ty(); auto *int1T = IRB.getInt1Ty(); auto *voidT = IRB.getVoidTy(); diff --git a/compiler/Symbolizer.cpp b/compiler/Symbolizer.cpp index d111c52d..41ad7ba8 100644 --- a/compiler/Symbolizer.cpp +++ b/compiler/Symbolizer.cpp @@ -89,7 +89,8 @@ void Symbolizer::shortCircuitExpressionUses() { // Build the check whether any input expression is non-null (i.e., there // is a symbolic input). - auto *nullExpression = ConstantPointerNull::get(IRB.getInt8PtrTy()); + auto *nullExpression = + ConstantPointerNull::get(IRB.getInt8Ty()->getPointerTo()); std::vector nullChecks; for (const auto &input : symbolicComputation.inputs) { nullChecks.push_back( @@ -149,7 +150,7 @@ void Symbolizer::shortCircuitExpressionUses() { Value *finalArgExpression; if (needRuntimeCheck) { IRB.SetInsertPoint(symbolicComputation.firstInstruction); - auto *argPHI = IRB.CreatePHI(IRB.getInt8PtrTy(), 2); + auto *argPHI = IRB.CreatePHI(IRB.getInt8Ty()->getPointerTo(), 2); argPHI->addIncoming(originalArgExpression, argCheckBlock); argPHI->addIncoming(newArgExpression, newArgExpression->getParent()); finalArgExpression = argPHI; @@ -165,10 +166,10 @@ void Symbolizer::shortCircuitExpressionUses() { // if short-circuiting wasn't possible. if (!symbolicComputation.lastInstruction->use_empty()) { IRB.SetInsertPoint(&tail->front()); - auto *finalExpression = IRB.CreatePHI(IRB.getInt8PtrTy(), 2); + auto *finalExpression = IRB.CreatePHI(IRB.getInt8Ty()->getPointerTo(), 2); symbolicComputation.lastInstruction->replaceAllUsesWith(finalExpression); - finalExpression->addIncoming(ConstantPointerNull::get(IRB.getInt8PtrTy()), - head); + finalExpression->addIncoming( + ConstantPointerNull::get(IRB.getInt8Ty()->getPointerTo()), head); finalExpression->addIncoming( symbolicComputation.lastInstruction, symbolicComputation.lastInstruction->getParent()); @@ -384,7 +385,7 @@ void Symbolizer::handleFunctionCall(CallBase &I, Instruction *returnPoint) { // previous function call. (If the function is instrumented, it will just // override our null with the real expression.) IRB.CreateCall(runtime.setReturnExpression, - ConstantPointerNull::get(IRB.getInt8PtrTy())); + ConstantPointerNull::get(IRB.getInt8Ty()->getPointerTo())); IRB.SetInsertPoint(returnPoint); symbolicExpressions[&I] = IRB.CreateCall(runtime.getReturnExpression); } @@ -826,11 +827,13 @@ void Symbolizer::visitPHINode(PHINode &I) { IRBuilder<> IRB(&I); unsigned numIncomingValues = I.getNumIncomingValues(); - auto *exprPHI = IRB.CreatePHI(IRB.getInt8PtrTy(), numIncomingValues); + auto *exprPHI = + IRB.CreatePHI(IRB.getInt8Ty()->getPointerTo(), numIncomingValues); for (unsigned incoming = 0; incoming < numIncomingValues; incoming++) { exprPHI->addIncoming( // The null pointer will be replaced in finalizePHINodes. - ConstantPointerNull::get(cast(IRB.getInt8PtrTy())), + ConstantPointerNull::get( + cast(IRB.getInt8Ty()->getPointerTo())), I.getIncomingBlock(incoming)); } @@ -909,7 +912,7 @@ void Symbolizer::visitSwitchInst(SwitchInst &I) { // Build a check whether we have a symbolic condition, to be used later. auto *haveSymbolicCondition = IRB.CreateICmpNE( - conditionExpr, ConstantPointerNull::get(IRB.getInt8PtrTy())); + conditionExpr, ConstantPointerNull::get(IRB.getInt8Ty()->getPointerTo())); auto *constraintBlock = SplitBlockAndInsertIfThen(haveSymbolicCondition, &I, /* unreachable */ false); diff --git a/compiler/Symbolizer.h b/compiler/Symbolizer.h index 808712ee..11a7b1a8 100644 --- a/compiler/Symbolizer.h +++ b/compiler/Symbolizer.h @@ -148,7 +148,7 @@ class Symbolizer : public llvm::InstVisitor { Input(llvm::Value *concrete, unsigned idx, llvm::Instruction *user) : concreteValue(concrete), operandIndex(idx), user(user) { assert(getSymbolicOperand()->getType() == - llvm::Type::getInt8PtrTy(user->getContext())); + llvm::Type::getInt8Ty(user->getContext())->getPointerTo()); } llvm::Value *getSymbolicOperand() const { @@ -215,7 +215,7 @@ class Symbolizer : public llvm::InstVisitor { auto *expr = getSymbolicExpression(V); if (expr == nullptr) return llvm::ConstantPointerNull::get( - llvm::IntegerType::getInt8PtrTy(V->getContext())); + llvm::IntegerType::getInt8Ty(V->getContext())->getPointerTo()); return expr; } From c89b6cceff8fb93e7187ea6e899f1ee3979a01ba Mon Sep 17 00:00:00 2001 From: mephi42 Date: Fri, 22 Oct 2021 03:44:56 +0200 Subject: [PATCH 2/2] Support AFL++ coverage maps AFL++ generates coverage maps with different sizes. --- util/symcc_fuzzing_helper/src/main.rs | 2 +- util/symcc_fuzzing_helper/src/symcc.rs | 44 ++++++++++++++++---------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/util/symcc_fuzzing_helper/src/main.rs b/util/symcc_fuzzing_helper/src/main.rs index 378e79d1..9af01363 100644 --- a/util/symcc_fuzzing_helper/src/main.rs +++ b/util/symcc_fuzzing_helper/src/main.rs @@ -353,7 +353,7 @@ fn process_new_testcase( ) })? { AflShowmapResult::Success(testcase_bitmap) => { - let interesting = state.current_bitmap.merge(&testcase_bitmap); + let interesting = state.current_bitmap.merge(*testcase_bitmap)?; if interesting { symcc::copy_testcase(&testcase, &mut state.queue, parent).with_context(|| { format!( diff --git a/util/symcc_fuzzing_helper/src/symcc.rs b/util/symcc_fuzzing_helper/src/symcc.rs index 69066039..745ce9e7 100644 --- a/util/symcc_fuzzing_helper/src/symcc.rs +++ b/util/symcc_fuzzing_helper/src/symcc.rs @@ -42,13 +42,13 @@ fn insert_input_file, P: AsRef>( /// A coverage map as used by AFL. pub struct AflMap { - data: [u8; 65536], + data: Option>, } impl AflMap { /// Create an empty map. pub fn new() -> AflMap { - AflMap { data: [0; 65536] } + AflMap { data: None } } /// Load a map from disk. @@ -60,31 +60,41 @@ impl AflMap { path.as_ref().display() ) })?; + Ok(AflMap { data: Some(data) }) + } + + /// Merge two coverage maps in place. + fn merge_vec(data: &mut Vec, new_data: Vec) -> Result { + let mut interesting = false; ensure!( - data.len() == 65536, - "The file to load the coverage map from has the wrong size ({})", - data.len() + data.len() == new_data.len(), + "Coverage maps must have the same size ({} and {})", + data.len(), + new_data.len(), ); - - let mut result = AflMap::new(); - result.data.copy_from_slice(&data); - Ok(result) + for (known, new) in data.iter_mut().zip(new_data.iter()) { + if *known != (*known | new) { + *known |= new; + interesting = true; + } + } + Ok(interesting) } /// Merge with another coverage map in place. /// /// Return true if the map has changed, i.e., if the other map yielded new /// coverage. - pub fn merge(&mut self, other: &AflMap) -> bool { - let mut interesting = false; - for (known, new) in self.data.iter_mut().zip(other.data.iter()) { - if *known != (*known | new) { - *known |= new; - interesting = true; + pub fn merge(&mut self, other: AflMap) -> Result { + match (&mut self.data, other.data) { + (Some(data), Some(new_data)) => AflMap::merge_vec(data, new_data), + (Some(_), None) => Ok(false), + (None, Some(new_data)) => { + self.data = Some(new_data); + Ok(true) } + (None, None) => Ok(false), } - - interesting } }