From ff98f56a8fde37b4100e8da34fbd1ab63d30d8f7 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Mon, 2 Mar 2026 14:20:38 +0100 Subject: [PATCH 01/16] feat(REDDI): port REDDI in ASPIS mainline --- aspis.sh | 7 + passes/ASPIS.h | 27 +- passes/CMakeLists.txt | 12 +- passes/EDDI.cpp | 1439 ++++++++++++++++++++++++++++++---------- passes/Utils/Utils.cpp | 82 ++- passes/Utils/Utils.h | 6 +- 6 files changed, 1187 insertions(+), 386 deletions(-) diff --git a/aspis.sh b/aspis.sh index d6a65c9..81a93d4 100755 --- a/aspis.sh +++ b/aspis.sh @@ -129,6 +129,7 @@ parse_commands() { Hardening mechanism: --eddi (Default) Enable EDDI. + --reddi Enable Recursive-EDDI. --seddi Enable Selective-EDDI. --fdsc Enable Full Duplication with Selective Checking. --no-dup Completely disable data duplication. @@ -201,6 +202,9 @@ EOF --eddi) dup=0 ;; + --reddi) + dup=3 + ;; --seddi) dup=1 ;; @@ -366,6 +370,9 @@ run_aspis() { 2) exe $OPT -load-pass-plugin=$DIR/build/passes/libFDSC.so --passes="eddi-verify" $build_dir/out.ll -o $build_dir/out.ll $eddi_options ;; + 3) + exe $OPT -load-pass-plugin=$DIR/build/passes/libREDDI.so --passes="eddi-verify" $build_dir/out.ll -o $build_dir/out.ll $eddi_options + ;; *) echo -e "\t--no-dup specified!" esac diff --git a/passes/ASPIS.h b/passes/ASPIS.h index 393e3fa..4f97b5d 100644 --- a/passes/ASPIS.h +++ b/passes/ASPIS.h @@ -5,6 +5,7 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/Pass.h" #include +#include "Utils/Utils.h" #include #include @@ -33,10 +34,20 @@ class EDDI : public PassInfoMixin { std::set CompiledFuncs; std::map FuncAnnotations; std::set OriginalFunctions; + + std::set InstructionsToRemove; + std::set toHardenConstructors; + std::set toHardenFunctions; + std::set toHardenVariables; + std::set DuplicatedCalls; - // Map of for which we need to always use the duplicate in place of the original - std::map ValuesToAlwaysDup; + LinkageMap linkageMap; + + bool duplicateAll; + void preprocess(Module &Md); + void fixDuplicatedConstructors(Module &Md); + std::set getVirtualMethodsFromConstructor(Function *Fn); int isUsedByStore(Instruction &I, Instruction &Use); Instruction* cloneInstr(Instruction &I, std::map &DuplicatedInstructionMap); void duplicateOperands (Instruction &I, std::map &DuplicatedInstructionMap, BasicBlock &ErrBB); @@ -44,17 +55,21 @@ class EDDI : public PassInfoMixin { Value* comparePtrs(Value &V1, Value &V2, IRBuilder<> &B); void addConsistencyChecks(Instruction &I, std::map &DuplicatedInstructionMap, BasicBlock &ErrBB); void fixFuncValsPassedByReference(Instruction &I, std::map &DuplicatedInstructionMap, IRBuilder<> &B); + int transformCallBaseInst(CallBase *CInstr, std::map &DuplicatedInstructionMap, IRBuilder<> &B, BasicBlock &ErrBB) ; Function *getFunctionDuplicate(Function *Fn); Function *getFunctionFromDuplicate(Function *Fn); - Constant *duplicateConstant(Constant *C, std::map &DuplicatedInstructionMap); - void duplicateGlobals(Module &Md, std::map &DuplicatedInstructionMap); + void duplicateGlobals (Module &Md, std::map &DuplicatedInstructionMap); bool isAllocaForExceptionHandling(AllocaInst &I); - int transformCallBaseInst(CallBase *CInstr, std::map &DuplicatedInstructionMap, IRBuilder<> &B, BasicBlock &ErrBB); - int duplicateInstruction(Instruction &I, std::map &DuplicatedInstructionMap, BasicBlock &ErrBB); + int duplicateInstruction (Instruction &I, std::map &DuplicatedInstructionMap, BasicBlock &ErrBB); bool isValueDuplicated(std::map &DuplicatedInstructionMap, Instruction &V); Function *duplicateFnArgs(Function &Fn, Module &Md, std::map &DuplicatedInstructionMap); + void CreateErrBB(Module &Md, Function &Fn, BasicBlock *ErrBB); + void fixGlobalCtors(Module &M); + void fixNonDuplicatedFunctions(Module &Md, std::map DuplicatedInstructionMap, std::set DuplicatedFns); public: + explicit EDDI(bool duplicateAll) : duplicateAll(duplicateAll) {} + PreservedAnalyses run(Module &M, ModuleAnalysisManager &); diff --git a/passes/CMakeLists.txt b/passes/CMakeLists.txt index b3c15f3..7fd0175 100644 --- a/passes/CMakeLists.txt +++ b/passes/CMakeLists.txt @@ -7,7 +7,17 @@ add_library(EDDI SHARED FuncRetToRef.cpp Utils/Utils.cpp ) -target_compile_definitions(EDDI PRIVATE SELECTIVE_CHECKING=0 CHECK_AT_STORES CHECK_AT_CALLS CHECK_AT_BRANCH) +target_compile_definitions(EDDI PRIVATE DUPLICATE_ALL SELECTIVE_CHECKING=0 CHECK_AT_STORES CHECK_AT_CALLS CHECK_AT_BRANCH) + +# REDDI +add_library(REDDI SHARED + DuplicateGlobals.cpp + EDDI.cpp + FuncRetToRef.cpp + Utils/Utils.cpp +) +target_compile_definitions(REDDI PRIVATE SELECTIVE_CHECKING=0 CHECK_AT_STORES CHECK_AT_CALLS CHECK_AT_BRANCH) + # FDSC add_library(FDSC SHARED diff --git a/passes/EDDI.cpp b/passes/EDDI.cpp index d8e5a35..9f840ba 100755 --- a/passes/EDDI.cpp +++ b/passes/EDDI.cpp @@ -8,6 +8,7 @@ * ************************************************************************************************ */ #include "ASPIS.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/ADT/Statistic.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Function.h" @@ -21,6 +22,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/Cloning.h" +#include #include #include #include @@ -35,6 +37,7 @@ #include #include #include +// #include "../TypeDeductionAnalysis/TypeDeductionAnalysis.hpp" #include "Utils/Utils.h" @@ -42,10 +45,6 @@ using namespace llvm; #define DEBUG_TYPE "eddi_verification" -#ifndef DC_HANDLER_INLINE -//#define DC_HANDLER_INLINE -#endif - /** * - 0: EDDI (Add checks at every basic block) * - 1: FDSC (Add checks only at basic blocks with more than one predecessor) @@ -56,6 +55,486 @@ using namespace llvm; // #define CHECK_AT_CALLS // #define CHECK_AT_BRANCH +// Regex to match constructors: the class name should be the same of the function name +std::regex ConstructorRegex(R"(.*([\w]+)::\1\((.*?)\))"); + +std::set toFixInvokes; + +/** + * @brief Check if the passed store is the one which saves the vtable in the object. + * In case it is, return the pointer to the GV of the vtable. + * + * @param SInst Reference to the store instruction to analyze. + * @return The pointer to the vtable global variable, if found; nullptr otherwise. + */ +GlobalVariable* isVTableStore(StoreInst &SInst) { + if(isa(SInst.getValueOperand())) { + // TODO: Should see the uses of the valueOperand to find this inst in case it happens + errs() << "this is a GEP instruction\n"; + auto *V = cast(SInst.getValueOperand())->getOperand(0); + if(isa(V)) { + auto *GV = cast(V); + auto vtableName = demangle(GV->getName().str()); + // Found "vtable" in name + if(vtableName.find("vtable") != vtableName.npos) { + // LLVM_DEBUG(dbgs() << "[REDDI] GEP Vtable name: " << vtableName << " of function " << Fn->getName() << "\n"); + return GV; + } + } + } else if(isa(SInst.getValueOperand())) { + auto *CE = cast(SInst.getValueOperand()); + if(CE->getOpcode() == Instruction::GetElementPtr && isa(CE->getOperand(0))) { + auto *GV = cast(CE->getOperand(0)); + auto vtableName = demangle(GV->getName().str()); + // Found "vtable" in name + if(vtableName.find("vtable") != vtableName.npos) { + return GV; + } + } + } + + return nullptr; +} + +/** + * @brief Retrieve all the virtual methods present in the vtable from the pointer to the constructor. + * + * @param Fn pointer to a function. + * @return A set containing the virtual functions referenced in the vtable (could be empty). + */ +std::set EDDI::getVirtualMethodsFromConstructor(Function *Fn) { + std::set virtualMethods; + + if(!Fn) { + errs() << "Fn is not a valid function.\n"; + return virtualMethods; + } + + // Find vtable + GlobalVariable *vtable = nullptr; + for(auto &BB : *Fn) { + for(auto &I : BB) { + if(isa(I)){ + auto &SInst = cast(I); + vtable = isVTableStore(SInst); + } + + if(vtable) + break; + } + + if(vtable) + break; + } + + // Get all the virtual methods + if(vtable) { + // Ensure the vtable global variable has an initializer + Constant *Initializer = vtable->getInitializer(); + if (!Initializer || !isa(Initializer)) { + errs() << "Vtable initializer is not a ConstantStruct.\n"; + return virtualMethods; + } + + // Extract the array field from the struct + ConstantStruct *VTableStruct = cast(Initializer); + + for(int i = 0; i < VTableStruct->getNumOperands(); i++) { + Constant *ArrayField = VTableStruct->getOperand(i); + if (!isa(ArrayField)) { + errs() << "Vtable field " << i << " is not a ConstantArray.\n"; + continue; + } + + // get virtual functions to harden from vtable + for (Value *Elem : cast(ArrayField)->operands()) { + if (isa(Elem)) { + virtualMethods.insert(cast(Elem)); + // LLVM_DEBUG(dbgs() << "[REDDI] Found virtual method " << cast(Elem)->getName() << " in " << Fn->getName() << "\n"); + } + } + } + } + + return virtualMethods; +} + +/** + * @brief For each toHardenConstructors, modifies the store for the vtable so that is used + * the `_dup` version of that vtable. + * + * identifies the store which saves the vtable in the object (if exists). Found it, + * duplicates the vtable (uses all the virtual `_dup` methods) and uses this new vtable + * (global variable) in the store. + * + * @param Md The module we are analyzing. + */ +void EDDI::fixDuplicatedConstructors(Module &Md) { + for(Function *Fn : toHardenConstructors) { + GlobalVariable *vtable = nullptr; + GlobalVariable *NewVtable = nullptr; + StoreInst *SInstVtable = nullptr; + Function *FnDup = getFunctionDuplicate(Fn); + + if(!FnDup) { + errs() << "Doesn't exist the dup version of " << Fn->getName() << "\n"; + continue; + } + + // Find vtable + LLVM_DEBUG(dbgs() << "[REDDI] Finding vtable for " << Fn->getName() << "\n"); + for(auto &BB : *Fn) { + for(auto &I : BB) { + if(isa(I)){ + auto &SInst = cast(I); + vtable = isVTableStore(SInst); + } + + if(vtable) + break; + } + + if(vtable) + break; + } + + // Duplicate vtable + if(vtable) { + // Ensure the vtable global variable has an initializer + Constant *Initializer = vtable->getInitializer(); + if (!Initializer || !isa(Initializer)) { + errs() << "Vtable initializer is not a ConstantStruct.\n"; + return; + } + + // Extract the array field from the struct + ConstantStruct *VTableStruct = cast(Initializer); + + std::vector NewArrayRef; + + for(int i = 0; i < VTableStruct->getNumOperands(); i++) { + Constant *ArrayField = VTableStruct->getOperand(i); + if (!isa(ArrayField)) { + errs() << "Vtable field " << i << " is not a ConstantArray.\n"; + continue; + } + + ConstantArray *FunctionArray = cast(ArrayField); + + // Iterate over elements of the array and modify function pointers + std::vector ModifiedElements; + for (Value *Elem : FunctionArray->operands()) { + if (isa(Elem)) { + Function *Func = cast(Elem); + // Replace with the _dup version of the function + std::string DupName = Func->getName().str() + "_dup"; + Function *DupFunction = Md.getFunction(DupName); + + if (DupFunction) { + // LLVM_DEBUG(dbgs() << "Getting _dup function: " << DupFunction->getName() << "\n"); + ModifiedElements.push_back(DupFunction); + } else { + errs() << "Missing _dup function for: " << Func->getName() << "\n"; + ModifiedElements.push_back(cast(Elem)); // Keep the original + } + } else { + // Retain non-function elements + ModifiedElements.push_back(cast(Elem)); + } + } + + // Create a new ConstantArray with the modified elements + ArrayType *ArrayType = FunctionArray->getType(); + Constant *NewArray = ConstantArray::get(ArrayType, ModifiedElements); + NewArrayRef.push_back(NewArray); + } + + // Create a new ConstantStruct for the vtable + Constant *NewVTableStruct = ConstantStruct::get(VTableStruct->getType(), NewArrayRef); + + // Create a new global variable for the modified vtable + NewVtable = new GlobalVariable( + Md, + NewVTableStruct->getType(), + vtable->isConstant(), + GlobalValue::ExternalLinkage, + NewVTableStruct, + vtable->getName() + "_dup" + ); + NewVtable->setSection(vtable->getSection()); + LLVM_DEBUG(dbgs() << "[REDDI] Created new vtable: " << NewVtable->getName() << "\n"); + } + + // In the dup constructor, change the relative store + if(NewVtable) { + for(auto &BB : *FnDup) { + for(auto &I : BB) { + if(isa(I)) { + auto &SInst = cast(I); + if(isVTableStore(SInst)) { + if(isa(SInst.getValueOperand())) { + // TODO: Should see the uses of the valueOperand to find this inst in case it happens + errs() << "this is a GEP instruction\n"; + } else if(isa(SInst.getValueOperand())) { + auto *CE = cast(SInst.getValueOperand()); + if (CE->getOpcode() == Instruction::GetElementPtr) { + // Extract the indices and base type + std::vector Indices = { + ConstantInt::get(Type::getInt32Ty(Md.getContext()), 0), + ConstantInt::get(Type::getInt32Ty(Md.getContext()), 0), + ConstantInt::get(Type::getInt32Ty(Md.getContext()), 2) + }; + + // Create a new GEP ConstantExpr with the new vtable + auto *NewGEP = ConstantExpr::getGetElementPtr( + cast(CE)->getSourceElementType(), + NewVtable, + Indices, + cast(CE)->isInBounds() + ); + + // Update the store instruction + SInst.setOperand(0, NewGEP); + } + LLVM_DEBUG(dbgs() << "[REDDI] Changed vtable_dup store with new vtable: " << NewVtable->getName() << "\n"); + } + } + } + } + } + } + } +} + +/** + * @brief Fill toHardenFunctions and toHardenVariables sets with all the functions and + * global variables that will need to be hardened. + * + * The rules to enter in toHardenFunctions set are: + * - Explicitely marked as `to_harden` + * - Called by a `to_harden` function and not an `exclude` or `to_duplicate` function + * - Used by a `to_harden` GlobalVariable + * - Present in a vtable of a `to_harden` object + * + * The rule to enter in toHardenVariables set is that it is a global variable explicitly + * marked as `to_harden` + * + * @param Md The module we are analyzing. + */ +void EDDI::preprocess(Module &Md) { + // Replace all uses of alias to aliasee + LLVM_DEBUG(dbgs() << "[REDDI] Replacing aliases\n"); + for (auto &alias : Md.aliases()) { + auto aliasee = alias.getAliaseeObject(); + if(isa(aliasee)){ + alias.replaceAllUsesWith(aliasee); + } + } + LLVM_DEBUG(dbgs() << "\n"); + + LLVM_DEBUG(dbgs() << "Getting annotations... "); + getFuncAnnotations(Md, FuncAnnotations); + LLVM_DEBUG(dbgs() << "[done]\n\n"); + + // Getting the explicit `to_harden` functions and Values + LLVM_DEBUG(dbgs() << "[REDDI] Getting all the functions and Global variables to harden\n"); + + // Choose between EDDI and REDDI + if(duplicateAll) { + outs() << "EDDI!\n"; + + // All the functions are to be hardened except the ones explicitly marked as `exclude` or `to_duplicate` + for(auto &F : Md) { + if((!F.hasName() || !isToDuplicateName(F.getName())) && (FuncAnnotations.find(&F) == FuncAnnotations.end() || + (!FuncAnnotations.find(&F)->second.starts_with("exclude") && !FuncAnnotations.find(&F)->second.starts_with("to_duplicate")))) { + toHardenFunctions.insert(&F); + } + } + + // All the Global Variables are to be hardened except the ones explicitly marked as `exclude` or `to_duplicate` + for(auto &GV : Md.globals()) { + if(GV.hasName() && !isToDuplicateName(GV.getName())) { + if(FuncAnnotations.find(&GV) == FuncAnnotations.end() || + (!FuncAnnotations.find(&GV)->second.starts_with("exclude") && !FuncAnnotations.find(&GV)->second.starts_with("to_duplicate"))) { + toHardenVariables.insert(&GV); + } + } + } + } else { + outs() << "REDDI!\n"; + for(auto x : FuncAnnotations) { + if(x.second.starts_with("to_harden")) { + if(isa(x.first) && getFunctionDuplicate(cast(x.first)) == NULL) { + // If is a function and it isn't/hasn't a duplicate version already + toHardenFunctions.insert(cast(x.first)); + } else if(isa(x.first)) { + toHardenVariables.insert(cast(x.first)); + } + } + } + LLVM_DEBUG(dbgs() << "\n"); + } + + // Getting the explicit `to_harden` functions and Values + LLVM_DEBUG(dbgs() << "[REDDI] Getting all the global variables to harden from explicitly to_harden functions\n"); + for(auto *Fn : toHardenFunctions) { + for(auto &BB : *Fn) { + for(auto &I : BB) { + for(auto &V : I.operands()) { + + if(isa(V) && cast(V)->hasInitializer() && toHardenVariables.find(V) == toHardenVariables.end()) { + toHardenVariables.insert(V); + outs() << "Inserting GV from explicit toHarden: " << *V << "\n"; + } + else if(isa(V)) { + for(auto &U : cast(V)->operands()) { + // if(isa(U) && U->hasName() && !isToDuplicateName(U->getName())) { + if(isa(U) && cast(U)->hasInitializer() && toHardenVariables.find(U) == toHardenVariables.end()) { + toHardenVariables.insert(U); + outs() << "Inserting GV from explicit toHarden from GEPOp: " << *U << "\n"; + } + } + } + + } + } + } + } + LLVM_DEBUG(dbgs() << "\n"); + + // Collecting all the functions called by a value to be hardened + LLVM_DEBUG(dbgs() << "[REDDI] Getting all the functions to harden called by a Global Variable\n"); + std::set toCheckVariables{toHardenVariables}; + while(!toCheckVariables.empty()){ + std::set toAddVariables; // support set to contain new to-be-checked values + for(Value *V : toCheckVariables) { + // outs() << "Instruction to check: " << *V << "\n"; + // Just protect the return value of the call, not the operands + if((isa(V) || isa(V)) && !isa(V)) { + auto Instr = cast(V); + + // Check parameters of function + for(int i = 0; i < Instr->getNumOperands(); i++) { + Value *operand = nullptr; + + // Get operand + if(isa(Instr)) { + auto PhiInst = cast(Instr); + operand = PhiInst->getIncomingValue(i); + // outs() << "phi operand: " << *operand << "\n"; + } else if(isa(Instr->getOperand(i)) || isa(Instr->getOperand(i)) || isa(Instr->getOperand(i))) { + operand = Instr->getOperand(i); + // outs() << "operand: " << *operand << "\n"; + } + + // Check if to add operand to toAddVariables + if(operand != NULL && operand != V && isa(operand) && + toHardenVariables.find(operand) == toHardenVariables.end() && + toCheckVariables.find(operand) == toCheckVariables.end() && + (FuncAnnotations.find(operand) == FuncAnnotations.end() || !FuncAnnotations.find(operand)->second.starts_with("exclude")) && + (!operand->hasName() || !isToDuplicateName(operand->getName())) && + (!isa(operand) || !isAllocaForExceptionHandling(*cast(operand)))) { + toAddVariables.insert(operand); + // outs() << "* To be hardened operand: " << *operand << "\n"; + } + } + } + + for(User *U : V->users()) { + if(isa(U) || isa(U)) { + // if(isa(U) || isa(U) || isa(U) || isa(U) || isa(U) || isa(U) || isa(U)) { + if(U != NULL && U != V && + toHardenVariables.find(U) == toHardenVariables.end() && + toCheckVariables.find(U) == toCheckVariables.end() && + (FuncAnnotations.find(U) == FuncAnnotations.end() || !FuncAnnotations.find(U)->second.starts_with("exclude")) && + (!U->hasName() || !isToDuplicateName(U->getName())) && + (!isa(U) || !isAllocaForExceptionHandling(*cast(U)))) { + // outs() << "* To be hardened user: " << *U << "\n"; + // If it is a call, add also the called function in the toHardenFunction set + if(isa(U)) { + CallBase *CallI = cast(U); + Function *Fn = CallI->getCalledFunction(); + if (Fn != NULL && getFunctionDuplicate(Fn) == NULL && + (FuncAnnotations.find(Fn) == FuncAnnotations.end() || + (!FuncAnnotations.find(Fn)->second.starts_with("exclude") && !FuncAnnotations.find(Fn)->second.starts_with("to_duplicate"))) && + !isToDuplicateName(Fn->getName()) && !Fn->getName().starts_with("__clang_call_terminate")) { + // If it isn't/hasn't a duplicate version already + toHardenFunctions.insert(Fn); + toAddVariables.insert(U); + } else { + LLVM_DEBUG(errs() << "[REDDI] Indirect Function to harden (called by " << V->getName() << ")\n"); + // continue; + } + } else { + toAddVariables.insert(U); + } + } + } + } + } + toHardenVariables.merge(toCheckVariables); + toCheckVariables = toAddVariables; + } + LLVM_DEBUG(dbgs() << "\n"); + + // Recursively retrieve functions to harden + LLVM_DEBUG(dbgs() << "[REDDI] Getting all the functions to harden recursively\n"); + std::set JustAddedFns{toHardenFunctions}; + while(!JustAddedFns.empty()) { + // New discovered functions + std::set toAddFns; + for(Function *Fn : JustAddedFns) { + // Check if it is a constructor + std::string DemangledName = demangle(Fn->getName().str()); + if(std::regex_match(DemangledName, ConstructorRegex)) { + // Add it to the toHardenConstructors set and retrieve all its virtualMethods + // if it isn't/hasn't a duplicate version already + toHardenConstructors.insert(Fn); + toAddFns.merge(getVirtualMethodsFromConstructor(Fn)); + } + + // Retrieve all the other called functions + for(BasicBlock &BB : *Fn) { + for(Instruction &I : BB) { + if(isa(I)) { + if(Function *CalledFn = cast(I).getCalledFunction()) { + auto CalledFnEntry = FuncAnnotations.find(CalledFn); + bool to_harden = (CalledFnEntry == FuncAnnotations.end()) || + !(CalledFnEntry->second.starts_with("exclude") || CalledFnEntry->second.starts_with("to_duplicate")); + LLVM_DEBUG(dbgs() << "[REDDI] " << Fn->getName() << " called " << CalledFn->getName() << + ((CalledFnEntry == FuncAnnotations.end()) ? " (not annotated)" : "") << + ((CalledFnEntry != FuncAnnotations.end() && CalledFnEntry->second.starts_with("exclude")) ? " (exclude)" : "") << + (toHardenFunctions.find(CalledFn) != toHardenFunctions.end() ? " (already in toHardenFunctions)" : "") << + (JustAddedFns.find(CalledFn) != JustAddedFns.end() ? " (already in JustAddedFns)" : "") << + "\n"); + if(to_harden && toHardenFunctions.find(CalledFn) == toHardenFunctions.end() && + JustAddedFns.find(CalledFn) == JustAddedFns.end() && + getFunctionDuplicate(CalledFn) == NULL && + (FuncAnnotations.find(CalledFn) == FuncAnnotations.end() || !FuncAnnotations.find(CalledFn)->second.starts_with("exclude")) && + !CalledFn->getName().starts_with("__clang_call_terminate")) { + // If is a new function to and it isn't/hasn't a duplicate version + toAddFns.insert(CalledFn); + // LLVM_DEBUG(dbgs() << "[REDDI] Added: " << CalledFn->getName() << "\n"); + } + } else { + // LLVM_DEBUG(errs() << "[REDDI] Indirect Function to harden (called by " << Fn->getName() << ")\n"); + // I.print(errs()); + // errs() << "\n"; + } + } + } + } + } + + // Add the just analyzed functions to the `toHardenFunctions` set + toHardenFunctions.merge(JustAddedFns); + // Now analyze the just discovered functions + JustAddedFns = toAddFns; + } + + LLVM_DEBUG(dbgs() << "[REDDI] preprocess done\n\n"); +} + /** * Determines whether a instruction &I is used by store instructions different * than &Use @@ -149,8 +628,13 @@ void EDDI::duplicateOperands( // if the operand has not been duplicated we need to duplicate it if (isa(V)) { Instruction *Operand = cast(V); - if (!isValueDuplicated(DuplicatedInstructionMap, *Operand)) - duplicateInstruction(*Operand, DuplicatedInstructionMap, ErrBB); + if (!isValueDuplicated(DuplicatedInstructionMap, *Operand)) { + if(duplicateInstruction(*Operand, DuplicatedInstructionMap, ErrBB)) { + if(InstructionsToRemove.find(Operand) == InstructionsToRemove.end()) { + InstructionsToRemove.insert(Operand); + } + } + } } // It may happen that we have a GEP as inline operand of a instruction. The // operands of the GEP are not duplicated leading to errors, so we manually @@ -182,17 +666,10 @@ void EDDI::duplicateOperands( if (IClone != NULL) { // use the duplicated instruction as operand of IClone auto Duplicate = DuplicatedInstructionMap.find(V); - if (Duplicate != DuplicatedInstructionMap.end()) { - IClone->setOperand(J, Duplicate->second); // set the J-th operand with the duplicate value - } - - // let us see whether we need to use always the dup for this operand - Duplicate = ValuesToAlwaysDup.find(V); - if (Duplicate != ValuesToAlwaysDup.end()) { // in this case, we want to use the dup also for the original instruction - //errs() << "Overriding operand for instructions: \n" << I << "\n" << *IClone << "\n"; - I.setOperand(J, Duplicate->second); - IClone->setOperand(J, Duplicate->second); - } + if (Duplicate != DuplicatedInstructionMap.end()) + IClone->setOperand( + J, + Duplicate->second); // set the J-th operand with the duplicate value } J++; } @@ -243,20 +720,18 @@ Value *EDDI::comparePtrs(Value &V1, Value &V2, IRBuilder<> &B) { Value *F1 = getPtrFinalValue(V1); Value *F2 = getPtrFinalValue(V2); - if (F1 != NULL && F2 != NULL && !F1->getType()->isPointerTy() && !F2->getType()->isPointerTy()) { + if (F1 != NULL && F2 != NULL && !F1->getType()->isPointerTy()) { Instruction *L1 = B.CreateLoad(F1->getType(), F1); Instruction *L2 = B.CreateLoad(F2->getType(), F2); if (L1->getType()->isFloatingPointTy()) { return B.CreateCmp(CmpInst::FCMP_UEQ, L1, L2); - } else if (L1->getType()->isIntegerTy()) { + } else { return B.CreateCmp(CmpInst::ICMP_EQ, L1, L2); } } return NULL; } -int syncpt_id = 0; - /** * Adds a consistency check on the instruction I */ @@ -324,9 +799,13 @@ void EDDI::addConsistencyChecks( if (OriginalElem->getType()->isFloatingPointTy()) { CmpInstructions.push_back( B.CreateCmp(CmpInst::FCMP_UEQ, OriginalElem, CopyElem)); - } else if (OriginalElem->getType()->isIntegerTy()){ + } else if (OriginalElem->getType()->isIntOrIntVectorTy() || OriginalElem->getType()->isPtrOrPtrVectorTy()) { CmpInstructions.push_back( B.CreateCmp(CmpInst::ICMP_EQ, OriginalElem, CopyElem)); + } else { + errs() << "Didn't create a comparison for "; + OriginalElem->getType()->print(errs()); + errs() << " type\n"; } } } @@ -337,9 +816,11 @@ void EDDI::addConsistencyChecks( if (Original->getType()->isFloatingPointTy()) { CmpInstructions.push_back( B.CreateCmp(CmpInst::FCMP_UEQ, Original, Copy)); - } else if (Original->getType()->isIntegerTy()) { + } else if (Original->getType()->isIntOrIntVectorTy() || Original->getType()->isPtrOrPtrVectorTy()) { CmpInstructions.push_back( B.CreateCmp(CmpInst::ICMP_EQ, Original, Copy)); + } else { + errs() << "Didn't create a comparison for " << Original->getType() << " type\n"; } } } @@ -350,13 +831,6 @@ void EDDI::addConsistencyChecks( // them are true if (!CmpInstructions.empty()) { // all comparisons must be true - if (ProfilingEnabled) { - IRBuilder<> BProfiler(cast(CmpInstructions.front())); - BProfiler.CreateCall(I.getParent()->getParent()->getParent()->getFunction("aspis.datacheck.begin")); - auto EndCall = BProfiler.CreateCall(I.getParent()->getParent()->getParent()->getFunction("aspis.datacheck.end")); - EndCall->removeFromParent(); - EndCall->insertAfter(cast(CmpInstructions.back())); - } Value *AndInstr = B.CreateAnd(CmpInstructions); auto CondBrInst = B.CreateCondBr(AndInstr, I.getParent(), &ErrBB); if (DebugEnabled) { @@ -370,11 +844,6 @@ void EDDI::addConsistencyChecks( BrInst->setDebugLoc(I.getDebugLoc()); } } - - if (ProfilingEnabled) { - I.setMetadata("aspis.syncpt", MDNode::get(I.getContext(), MDString::get(I.getContext(), std::to_string(syncpt_id)))); - syncpt_id++; - } } // Given an instruction, loads and stores the pointers passed to the @@ -389,20 +858,21 @@ void EDDI::fixFuncValsPassedByReference( int numOps = I.getNumOperands(); for (int i = 0; i < numOps; i++) { Value *V = I.getOperand(i); - if (isa(V) && V->getType()->isPointerTy()) { + if (isa(V)) { Instruction *Operand = cast(V); auto Duplicate = DuplicatedInstructionMap.find(Operand); if (Duplicate != DuplicatedInstructionMap.end()) { Value *Original = Duplicate->first; Value *Copy = Duplicate->second; - - Type *OriginalType = Original->getType(); - Instruction *TmpLoad = B.CreateLoad(OriginalType, Original); - Instruction *TmpStore = B.CreateStore(TmpLoad, Copy); - DuplicatedInstructionMap.insert( - std::pair(TmpLoad, TmpLoad)); - DuplicatedInstructionMap.insert( - std::pair(TmpStore, TmpStore)); + if(Original->getType()->isPointerTy() && Copy->getType()->isPointerTy()) { + Type *OriginalType = Original->getType(); + Instruction *TmpLoad = B.CreateLoad(OriginalType, Original); + Instruction *TmpStore = B.CreateStore(TmpLoad, Copy); + DuplicatedInstructionMap.insert( + std::pair(TmpLoad, TmpLoad)); + DuplicatedInstructionMap.insert( + std::pair(TmpStore, TmpStore)); + } } } } @@ -439,55 +909,30 @@ Function *EDDI::getFunctionFromDuplicate(Function *Fn) { // Otherwise, we try to get the non-"_dup" version Function *FnDup = Fn->getParent()->getFunction( Fn->getName().str().substr(0, Fn->getName().str().length() - 8)); - if (FnDup == NULL) { + if (FnDup == NULL || FnDup == Fn) { FnDup = Fn->getParent()->getFunction( Fn->getName().str().substr(0, Fn->getName().str().length() - 4)); } return FnDup; } -Constant *EDDI::duplicateConstant(Constant *C, std::map &DuplicatedInstructionMap) { - Constant *ret = C; - - if(isa(C)) { - return getFunctionDuplicate(cast(C)); - } - else if (isa(C) && DuplicatedInstructionMap.find(C) != DuplicatedInstructionMap.end()) { - return cast(DuplicatedInstructionMap.find(C)->second); - } - else if (isa(C)) { - ConstantArray *CArr = cast(C); - std::vector ConstArray; - - for (auto &Elem : C->operands()) { - assert(isa(Elem) && "Trying to duplicate a constant that has nonconstant operands!"); - Constant *NewElem = duplicateConstant(cast(Elem), DuplicatedInstructionMap); - ConstArray.push_back(NewElem); - } - return ConstantArray::get(CArr->getType(), ConstArray); - } - else if (isa(C)) { - errs() << "WARNING - Constant struct duplication is not supported! Possible undefined behaviour...\n"; - // TODO create the constant struct - } - return ret; -} - -int globcnt = 0; - void EDDI::duplicateGlobals( Module &Md, std::map &DuplicatedInstructionMap) { Value *RuntimeSig; Value *RetSig; std::list GVars; - for (GlobalVariable &GV : Md.globals()) { - GVars.push_back(&GV); + for (auto *V : toHardenVariables) { + if(isa(V)) { + GVars.push_back(cast(V)); + } } for (auto GV : GVars) { + auto GVAnnotation = FuncAnnotations.find(GV); if (!isa(GV) && - FuncAnnotations.find(GV) != FuncAnnotations.end()) { - if ((FuncAnnotations.find(GV))->second.starts_with("runtime_sig") || - (FuncAnnotations.find(GV))->second.starts_with("run_adj_sig")) { + GVAnnotation != FuncAnnotations.end()) { + // What does these annotations do? + if (GVAnnotation->second.starts_with("runtime_sig") || + GVAnnotation->second.starts_with("run_adj_sig")) { continue; } } @@ -506,36 +951,21 @@ void EDDI::duplicateGlobals( bool isStruct = GV->getValueType()->isStructTy(); bool isArray = GV->getValueType()->isArrayTy(); bool isPointer = GV->getValueType()->isPointerTy(); - bool endsWithDup = GV->getName().ends_with("_dup"); - bool hasExternalLinkage = GV->isExternallyInitialized() || GV->hasExternalLinkage(); + bool ends_withDup = GV->getName().ends_with("_dup"); + bool hasInternalLinkage = GV->hasInternalLinkage(); bool isMetadataInfo = GV->getSection() == "llvm.metadata"; + bool isReservedName = GV->getName().starts_with("llvm."); bool toExclude = !isa(GV) && - FuncAnnotations.find(GV) != FuncAnnotations.end() && - (FuncAnnotations.find(GV))->second.starts_with("exclude"); - bool isConstStruct = GV->getSection() != "llvm.metadata" && GV->hasInitializer() && isa(GV->getInitializer()); - bool isStructOfGlobals = false; // is true if and only if the global variable that we are duplicating contains at least a global pointer - bool isStructOfFunctions = false; // is true if the global variable that we are duplicating contains at least a global pointer, and such global pointer is a function pointer - if (isConstStruct) { - for (Value *Op : cast(GV->getInitializer())->operands()) { - if (isa(Op)) { - isStructOfGlobals = true; - if(isa(Op)) isStructOfFunctions = true; - break; - } - } - } - if (isStructOfFunctions || ! (isFunction || isConstant || endsWithDup || isMetadataInfo || toExclude || hasExternalLinkage || hasExternalLinkage) // is not function, constant, struct and does not end with _dup + GVAnnotation != FuncAnnotations.end() && + GVAnnotation->second.starts_with("exclude"); + + if (! (isFunction || isConstant || ends_withDup || isMetadataInfo || isReservedName || toExclude) // is not function, constant, struct and does not end with _dup /* && ((hasInternalLinkage && (!isArray || (isArray && !cast(GV.getValueType())->getArrayElementType()->isAggregateType() ))) // has internal linkage and is not an array, or is an array but the element type is not aggregate || !isArray) */ // if it does not have internal linkage, it is not an array or a pointer ) { Constant *Initializer = nullptr; if (GV->hasInitializer()) { - Initializer = duplicateConstant(GV->getInitializer(), DuplicatedInstructionMap); - } - // set a placeholder for the name of the global variable - if (!GV->hasName()) { - GV->setName("glob_"+std::to_string(globcnt)); - globcnt++; + Initializer = GV->getInitializer(); } GlobalVariable *InsertBefore; @@ -559,41 +989,37 @@ void EDDI::duplicateGlobals( GVCopy->setAlignment(GV->getAlign()); GVCopy->setDSOLocal(GV->isDSOLocal()); - // Save the duplicated global so that the duplicate can be used as operand // of other duplicated instructions DuplicatedInstructionMap.insert(std::pair(GV, GVCopy)); DuplicatedInstructionMap.insert(std::pair(GVCopy, GV)); - - if (isStructOfFunctions) - ValuesToAlwaysDup.insert(std::pair(GV, GVCopy)); } } } - bool EDDI::isAllocaForExceptionHandling(AllocaInst &I){ - for (auto e : I.users()) - { - if (isa(e)){ - StoreInst *storeInst=cast(e); - auto *valueOperand =storeInst->getValueOperand(); - if(isa(valueOperand)){ - CallBase *callInst = cast(valueOperand); - if (callInst->getCalledFunction() && callInst->getCalledFunction()->getName() == "__cxa_begin_catch") - {return true;} - } - +bool EDDI::isAllocaForExceptionHandling(AllocaInst &I){ + for (auto e : I.users()) + { + if (isa(e)){ + StoreInst *storeInst=cast(e); + auto *valueOperand =storeInst->getValueOperand(); + if(isa(valueOperand)){ + CallBase *callInst = cast(valueOperand); + if (callInst->getCalledFunction() != NULL && callInst->getCalledFunction()->getName() == "__cxa_begin_catch") + {return true;} } + } - return false; + } + return false; } int EDDI::transformCallBaseInst(CallBase *CInstr, std::map &DuplicatedInstructionMap, - IRBuilder<> &B, BasicBlock &ErrBB) { + IRBuilder<> &B, BasicBlock &ErrBB) { int res = 0; SmallVector args; SmallVector ParamTypes; - + Function *Callee = CInstr->getCalledFunction(); Function *Fn = getFunctionDuplicate(Callee); @@ -611,27 +1037,6 @@ int EDDI::transformCallBaseInst(CallBase *CInstr, std::map &Du if(DuplicatedInstructionMap.find(Arg) != DuplicatedInstructionMap.end()) { Copy = DuplicatedInstructionMap.find(Arg)->second; } - else if (isa(Arg) && isa(Arg)) { - GEPOperator *GEPOperand = cast(Arg); - Value *PtrOperand = GEPOperand->getPointerOperand(); - // update the duplicate GEP operator using the duplicate of the pointer - // operand - if (DuplicatedInstructionMap.find(PtrOperand) != - DuplicatedInstructionMap.end()) { - std::vector indices; - for (auto &Idx : GEPOperand->indices()) { - indices.push_back(Idx); - } - Constant *CloneGEPOperand = - cast(GEPOperand) - ->getInBoundsGetElementPtr( - GEPOperand->getSourceElementType(), - cast( - DuplicatedInstructionMap.find(PtrOperand)->second), - ArrayRef(indices)); - Copy = CloneGEPOperand; - } - } // Duplicating only fixed parameters, passing just one time the variadic arguments if(Callee != NULL && Callee->getFunctionType() != NULL && i >= Callee->getFunctionType()->getNumParams()) { @@ -660,15 +1065,8 @@ int EDDI::transformCallBaseInst(CallBase *CInstr, std::map &Du // In case of duplication of an indirect call, call the function with doubled parameters if (Callee == NULL) { - if (CInstr->isInlineAsm()) { - return 0; - } // Create the new function type Type *ReturnType = CInstr->getType(); - if (ReturnType->isAggregateType() || ReturnType->isPointerTy()) { - errs() << "WARNING - Indirect calls to function returning aggregate data or pointers are not supported!\n Compilation output may have unpredictable behaviour.\n"; - errs() << "\tIn instruction: " << *CInstr << "\n"; - } FunctionType *FuncType = FunctionType::get(ReturnType, ParamTypes, false); // Create a dummy function pointer (Fn) for the new call @@ -710,7 +1108,6 @@ int EDDI::transformCallBaseInst(CallBase *CInstr, std::map &Du // Replace the old instruction with the new one CInstr->replaceNonMetadataUsesWith(NewCInstr); - DuplicatedInstructionMap.insert(std::pair(NewCInstr, NewCInstr)); // Remove original instruction since we created the duplicated version res = 1; } else { @@ -726,9 +1123,8 @@ int EDDI::transformCallBaseInst(CallBase *CInstr, std::map &Du if (DebugEnabled) { NewCInstr->setDebugLoc(CInstr->getDebugLoc()); } - CInstr->replaceNonMetadataUsesWith(NewCInstr); - DuplicatedInstructionMap.insert(std::pair(NewCInstr, NewCInstr)); res = 1; + CInstr->replaceNonMetadataUsesWith(NewCInstr); } return res; @@ -786,7 +1182,11 @@ int EDDI::duplicateInstruction( #ifdef CHECK_AT_STORES #if (SELECTIVE_CHECKING == 1) - if (I.getParent()->getTerminator()->getNumSuccessors() > 1) + if(I.getParent()->getTerminator() == NULL) { + errs() << "Malformed block!\n"; + I.getParent()->print(errs()); + errs() << "\n"; + } else if (I.getParent()->getTerminator()->getNumSuccessors() > 1) #endif addConsistencyChecks(I, DuplicatedInstructionMap, ErrBB); #endif @@ -794,7 +1194,9 @@ int EDDI::duplicateInstruction( // that happens I just remove the duplicate if (IClone->isIdenticalTo(&I)) { IClone->eraseFromParent(); - DuplicatedInstructionMap.erase(DuplicatedInstructionMap.find(&I)); + if(DuplicatedInstructionMap.find(&I) != DuplicatedInstructionMap.end()) { + DuplicatedInstructionMap.erase(DuplicatedInstructionMap.find(&I)); + } } } @@ -807,33 +1209,48 @@ int EDDI::duplicateInstruction( // add consistency checks on I #ifdef CHECK_AT_BRANCH - if (I.getParent()->getTerminator()->getNumSuccessors() > 1) + if(I.getParent()->getTerminator() == NULL) { + errs() << "Malformed block!\n"; + I.getParent()->print(errs()); + errs() << "\n"; + } else if (I.getParent()->getTerminator()->getNumSuccessors() > 1) addConsistencyChecks(I, DuplicatedInstructionMap, ErrBB); #endif } - // if the istruction is a call, we duplicate the operands and add consistency + // if the istruction is a non-already-duplicated call, we duplicate the operands and add consistency // checks - else if (isa(I)) { + else if (isa(I) && DuplicatedCalls.find(&I) == DuplicatedCalls.end()) { + DuplicatedCalls.insert(&I); CallBase *CInstr = cast(&I); // there are some instructions that can be annotated with "to_duplicate" in // order to tell the pass to duplicate the function call. Function *Callee = CInstr->getCalledFunction(); Callee = getFunctionFromDuplicate(Callee); // check if the function call has to be duplicated - if ((FuncAnnotations.find(Callee) != FuncAnnotations.end() && - (*FuncAnnotations.find(Callee)).second.starts_with("to_duplicate")) || - isIntrinsicToDuplicate(CInstr)) { + if ((FuncAnnotations.find(Callee) != FuncAnnotations.end() && FuncAnnotations.find(Callee)->second.starts_with("to_duplicate")) || + isToDuplicate(CInstr)) { // duplicate the instruction cloneInstr(*CInstr, DuplicatedInstructionMap); // duplicate the operands duplicateOperands(I, DuplicatedInstructionMap, ErrBB); + if(isa(I)) { + // In case of an invoke instruction, we have to fix the first invoke since + // it would jump to the next BB and not to the duplicated invoke instruction + auto *IInstr = &cast(I); + toFixInvokes.insert(IInstr); + } + // add consistency checks on I #ifdef CHECK_AT_CALLS #if (SELECTIVE_CHECKING == 1) - if (I.getParent()->getTerminator()->getNumSuccessors() > 1) + if(I.getParent()->getTerminator() == NULL) { + errs() << "Malformed block!\n"; + I.getParent()->print(errs()); + errs() << "\n"; + } else if (I.getParent()->getTerminator()->getNumSuccessors() > 1) #endif addConsistencyChecks(I, DuplicatedInstructionMap, ErrBB); #endif @@ -846,22 +1263,29 @@ int EDDI::duplicateInstruction( // add consistency checks on I #ifdef CHECK_AT_CALLS #if (SELECTIVE_CHECKING == 1) - if (I.getParent()->getTerminator()->getNumSuccessors() > 1) + if(I.getParent()->getTerminator() == NULL) { + errs() << "Malformed block!\n"; + I.getParent()->print(errs()); + errs() << "\n"; + } else if (I.getParent()->getTerminator()->getNumSuccessors() > 1) #endif addConsistencyChecks(I, DuplicatedInstructionMap, ErrBB); #endif IRBuilder<> B(CInstr); - if (!isa(CInstr)) { + if (!isa(CInstr) && I.getNextNonDebugInstruction()) { B.SetInsertPoint(I.getNextNonDebugInstruction()); - } else { + } else if(isa(CInstr) && cast(CInstr)->getNormalDest()) { B.SetInsertPoint( &*cast(CInstr)->getNormalDest()->getFirstInsertionPt()); + } else { + errs() << "Can't set insert point! " << I << "\n"; + abort(); } // get the function with the duplicated signature, if it exists Function *Fn = getFunctionDuplicate(CInstr->getCalledFunction()); - // if the _dup function exists, we substitute the call instruction with a - // call to the function with duplicated arguments + // if the _dup function exists (and it is not itself the dup version) or is an indirect call, + // we substitute the call instruction with a call to the function with duplicated arguments if (CInstr->getCalledFunction() == NULL || (Fn != NULL && Fn != CInstr->getCalledFunction())) { res = transformCallBaseInst(CInstr, DuplicatedInstructionMap, B, ErrBB); } else { @@ -887,15 +1311,6 @@ bool EDDI::isValueDuplicated( return false; } -Instruction *getSingleReturnInst(Function &F) { - for (BasicBlock &BB : F) { - if (auto *retInst = llvm::dyn_cast(BB.getTerminator())) { - return retInst; - } - } - return nullptr; -} - Function * EDDI::duplicateFnArgs(Function &Fn, Module &Md, std::map &DuplicatedInstructionMap) { @@ -906,7 +1321,11 @@ EDDI::duplicateFnArgs(Function &Fn, Module &Md, std::vector paramTypeVec; for (int i = 0; i < Fn.arg_size(); i++) { Type *ParamType = FnType->params()[i]; - if (AlternateMemMapEnabled == false) { // sequential + + // Passing just one time the variadic arguments while passing two times the fixed ones + if(i >= FnType->getNumParams()) { + paramTypeVec.push_back(ParamType); + } else if (!AlternateMemMapEnabled) { // sequential paramTypeVec.insert(paramTypeVec.begin() + i, ParamType); paramTypeVec.push_back(ParamType); } else { @@ -925,7 +1344,11 @@ EDDI::duplicateFnArgs(Function &Fn, Module &Md, Fn.getName() + "_dup", Fn.getParent()); ValueToValueMapTy Params; for (int i = 0; i < Fn.arg_size(); i++) { - if (AlternateMemMapEnabled == false) { + if (Fn.getArg(i)->hasStructRetAttr()) { + Fn.getArg(i)->removeAttr(Attribute::AttrKind::StructRet); + } + + if (!AlternateMemMapEnabled) { Params[Fn.getArg(i)] = ClonedFunc->getArg(Fn.arg_size() + i); } else { Params[Fn.getArg(i)] = ClonedFunc->getArg(i * 2); @@ -935,34 +1358,109 @@ EDDI::duplicateFnArgs(Function &Fn, Module &Md, CloneFunctionInto(ClonedFunc, &Fn, Params, CloneFunctionChangeType::GlobalChanges, returns); - - // we may have a noundef ret attribute and we remove it as we have no return - if(ClonedFunc->hasRetAttribute(Attribute::NoUndef)){ - ClonedFunc->removeRetAttr(Attribute::NoUndef); - } - for (int i=0; i < ClonedFunc->arg_size(); i++) { - if(ClonedFunc->hasParamAttribute(i, Attribute::StructRet)){ - ClonedFunc->removeParamAttr(i, Attribute::StructRet); + return ClonedFunc; +} + +/** + * @brief Recursively searches for the value type, returning its type and alignment + * @param Arg [In] Pointer to the value we want to analyze + * @param ArgAlign [Out] The found alignment + * @return The Type of Arg, if found. VoidTy otherwise + */ +Type *getValueType(Value *Arg, Align *ArgAlign) { + // https://llvm.org/docs/OpaquePointers.html + while(true) { + if(isa(Arg) && !cast(Arg)->isIndirectCall() && demangle(cast(Arg)->getCalledFunction()->getName().str()).find("operator new") == 0) { + Value *Size = cast(Arg)->getArgOperand(0); + if(isa(Size)) { + // Use the size to create a type + LLVMContext &Ctx = Arg->getContext(); + + // Assume the allocated memory is for an array of bytes + Type *ElementType = Type::getInt8Ty(Ctx); // Byte type + return ArrayType::get(ElementType, cast(Size)->getZExtValue()); + } + errs() << "Call not supported" << *Arg << "\n"; + return Type::getVoidTy(Arg->getContext()); + } else if(isa(Arg)) { + Type *ArgType = cast(Arg)->getValueType(); + if(ArgType->isPointerTy()) { + bool foundNewValue = false; + for(Value *ArgUsers : cast(Arg)->users()) { + if (isa(ArgUsers) && cast(ArgUsers)->getPointerOperand() == Arg) { + Arg = cast(ArgUsers)->getValueOperand(); + *ArgAlign = cast(ArgUsers)->getAlign(); + errs() << "Store found: " << *ArgUsers << " with align " << ArgAlign->value() << "\n"; + foundNewValue = true; + break; + } + } + + if(!foundNewValue) { + errs() << "Global Type not supported" << *Arg << "\n"; + return Type::getVoidTy(Arg->getContext()); + } + } else { + return ArgType; + } + } else if(isa(Arg)) { + Arg = cast(Arg)->getIncomingValue(0); + } else if(isa(Arg)) { + *ArgAlign = cast(Arg)->getAlign(); + return cast(Arg)->getAllocatedType(); + } else if(isa(Arg)) { + *ArgAlign = cast(Arg)->getPointerAlignment(cast(Arg)->getModule()->getDataLayout()); + return cast(Arg)->getSourceElementType(); + } else if(isa(Arg)) { + return cast(Arg)->getFunctionType(); + } else if(isa(Arg)) { + *ArgAlign = cast(Arg)->getAlign(); + Arg = cast(Arg)->getPointerOperand(); + } else if(isa(Arg)) { + *ArgAlign = cast(Arg)->getAlign(); + Arg = cast(Arg)->getValueOperand(); + } else { + errs() << "Type not supported" << *Arg << "\n"; + return Type::getVoidTy(Arg->getContext()); } } - - return ClonedFunc; } /** - * I have to duplicate all instructions except function calls and branches + * @brief I have to duplicate all instructions except function calls and branches + * + * 0. Replacing aliases to aliasees + * 1. getting function annotations + * 2. Creating fault tolerance functions + * 3. Create map of subprogram and linkage names + * 4. Duplicate globals + * 4.1. + * 5. For each function in module, if it should NOT compile (the function is neither null nor empty, + * it does not have to be marked as excluded or to_duplicate nor it is one of the original functions) skip + * 6. If the function is a duplicated one, we need to iterate over the function arguments and duplicate them in order to access them during the instruction duplication phase + * 6.1. Call duplicateInstruction on all uses of each argument + * 7. For each Instruction, duplicate the instruction and then save for delete after if the duplicated instruction is the same as the original + * 8. Generate error branches + * 9. Delete the marked duplicated instructions + * + * + * + * 1. Duplicate Globals + * 2. Duplicate functions + * 3. Duplicate Constructors + * + * * @param Md * @return */ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { LLVM_DEBUG(dbgs() << "Initializing EDDI...\n"); - LLVM_DEBUG(dbgs() << "Getting annotations... "); - getFuncAnnotations(Md, FuncAnnotations); - LLVM_DEBUG(dbgs() << "[done]\n"); + preprocess(Md); + LLVM_DEBUG(dbgs() << "[REDDI] Preprocess finished\n"); createFtFuncs(Md); - LinkageMap linkageMap = mapFunctionLinkageNames(Md); + linkageMap = mapFunctionLinkageNames(Md); // fix debug information in the first BB of each function if(DebugEnabled) { @@ -973,7 +1471,7 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { auto NextI = I; // iterate over the next instructions finding the first debug loc - while ((NextI = NextI->getNextNode())) { + while (NextI = NextI->getNextNode()) { if (NextI->getDebugLoc()) { I->setDebugLoc(NextI->getDebugLoc()); break; @@ -987,132 +1485,365 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { DuplicatedInstructionMap; // is a map containing the instructions // and their duplicates - // store the functions that are currently in the module - std::list FnList; + LLVM_DEBUG(dbgs() << "Duplicating globals... "); + duplicateGlobals(Md, DuplicatedInstructionMap); + LLVM_DEBUG(dbgs() << "[done]\n"); + + // store the duplicated functions that are currently in the module std::set DuplicatedFns; - LLVM_DEBUG(dbgs() << "Retrieving functions to compile... "); - // first store the instructions to compile in the current module - int cnt = 0; - for (Function &Fn : Md) { - if (shouldCompile(Fn, FuncAnnotations, OriginalFunctions)) { - cnt++; - // LLVM_DEBUG(dbgs() << "Found: " << cnt << "\r"); - FnList.push_back(&Fn); - ValueToValueMapTy Params; - Function *OriginalFn = CloneFunction(&Fn, Params); - OriginalFunctions.insert(OriginalFn); - OriginalFn->setName(Fn.getName().str() + "_original"); + // then duplicate the function arguments using toHardenFunctions + LLVM_DEBUG(dbgs() << "Creating _dup functions\n"); + for (Function *Fn : toHardenFunctions) { + // Create dup functions only if the function is declared in this module + // and isn't just to be duplicated + if(!Fn->isDeclaration() && !isToDuplicateName(Fn->getName())) { + Function *newFn = duplicateFnArgs(*Fn, Md, DuplicatedInstructionMap); + DuplicatedFns.insert(newFn); } } + LLVM_DEBUG(dbgs() << "Creating _dup functions [done]\n"); + + // Fixing the duplicated constructors + fixDuplicatedConstructors(Md); + + // list of duplicated instructions to remove since they are equal to the original + std::set GrayAreaCallsToFix; + int iFn = 1; + LLVM_DEBUG(dbgs() << "Iterating over the functions...\n"); + + for (Function *Fn : DuplicatedFns) { + LLVM_DEBUG(dbgs() << "Compiling " << iFn++ << "/" << DuplicatedFns.size() << ": " + << Fn->getName() << "\n"); + CompiledFuncs.insert(Fn); + + BasicBlock *ErrBB = BasicBlock::Create(Fn->getContext(), "ErrBB", Fn); - LLVM_DEBUG(dbgs() << "Found: " << FnList.size() << "\n"); - - // then duplicate the function arguments using FnList populated earlier - for (Function *Fn : FnList) { - Function *newFn = duplicateFnArgs(*Fn, Md, DuplicatedInstructionMap); - DuplicatedFns.insert(newFn); - auto FAnns = FuncAnnotations; - auto OFunc = OriginalFunctions; - Fn->replaceUsesWithIf(newFn, [FAnns, OFunc] (Use &U) { - auto res = false; - if (isa(U.getUser()) && shouldCompile(*cast(U.getUser())->getParent()->getParent(), FAnns, OFunc)) { - res = true; + LLVM_DEBUG(dbgs() << "function arguments"); + // save the function arguments and their duplicates + for (int i = 0; i < Fn->arg_size(); i++) { + Value *Arg, *ArgClone; + if (!AlternateMemMapEnabled) { + if (i >= Fn->arg_size() / 2) { + break; + } + Arg = Fn->getArg(i); + ArgClone = Fn->getArg(i + Fn->arg_size() / 2); + } else { + if (i % 2 == 1) + continue; + Arg = Fn->getArg(i); + ArgClone = Fn->getArg(i + 1); } - return res; - }); - } + DuplicatedInstructionMap.insert( + std::pair(Arg, ArgClone)); + DuplicatedInstructionMap.insert( + std::pair(ArgClone, Arg)); + for (User *U : Arg->users()) { + if (isa(U)) { + Instruction *I = cast(U); + // duplicate the uses of each argument + if (duplicateInstruction(*I, DuplicatedInstructionMap, *ErrBB)) { + if(InstructionsToRemove.find(I) == InstructionsToRemove.end()) { + InstructionsToRemove.insert(I); + errs() << "Remove instr ( " << *I << " ) from " << *I->getParent()->getParent() << " while duplicating fn args\n"; + } else { + errs() << "Duplicated to remove instr ( " << *I << " ) from " << *I->getParent()->getParent() << " while duplicating fn args\n"; + } + } + } + } + } + LLVM_DEBUG(dbgs() << " [done]\n"); - LLVM_DEBUG(dbgs() << "Duplicating globals... "); - duplicateGlobals(Md, DuplicatedInstructionMap); - LLVM_DEBUG(dbgs() << "[done]\n"); + LLVM_DEBUG(dbgs() << "Duplicate instructions"); - // list of duplicated instructions to remove since they are equal to the - // original - std::list InstructionsToRemove; - int i = -1; - int tot_funcs = 0; - for (Function &Fn : Md) { - if (shouldCompile(Fn, FuncAnnotations, OriginalFunctions)) { - tot_funcs++; + std::set InstToDuplicate; + for (BasicBlock &BB : *Fn) { + for (Instruction &I : BB) { + InstToDuplicate.insert(&I); + } } + + for (Instruction *I : InstToDuplicate) { + if (!isValueDuplicated(DuplicatedInstructionMap, *I)) { + // perform the duplication + int shouldDelete = + duplicateInstruction(*I, DuplicatedInstructionMap, *ErrBB); + + // the instruction duplicated may be equal to the original, so we + // return shouldDelete in order to drop the duplicates + + // TODO: Why to be done in another phase and not in duplciateInstruction? + if (shouldDelete) { + if(InstructionsToRemove.find(I) == InstructionsToRemove.end()) { + InstructionsToRemove.insert(I); + } + } + } + } + LLVM_DEBUG(dbgs() << "[done]\n"); + + // insert the code for calling the error basic block in case of a mismatch + CreateErrBB(Md, *Fn, ErrBB); } - LLVM_DEBUG(dbgs() << "Iterating over the module functions...\n"); - for (Function &Fn : Md) { - if (shouldCompile(Fn, FuncAnnotations, OriginalFunctions)) { - i++; - LLVM_DEBUG(dbgs() << "Compiling " << i << "/" << tot_funcs << ": " - << Fn.getName() << "\n"); - CompiledFuncs.insert(&Fn); - BasicBlock *ErrBB = BasicBlock::Create(Fn.getContext(), "ErrBB", &Fn); - - // If the function is a duplicated one, we need to - // iterate over the function arguments and duplicate - // them in order to access them during the instruction - // duplication phase - if (DuplicatedFns.find(&Fn) != DuplicatedFns.end()) { - // save the function arguments and their duplicates - for (int i = 0; i < Fn.arg_size(); i++) { - Value *Arg, *ArgClone; - if (AlternateMemMapEnabled == false) { - if (i >= Fn.arg_size() / 2) { - break; + + LLVM_DEBUG(dbgs() << "Iterating over variables...\n"); + // Duplicate usages of global variables to harden only if not in a _dup function + // (already handled in a duplicated function) + for (Value *V : toHardenVariables) { + if(V == NULL) { + errs() << "To harden a null var\n"; + continue; + } + + for(User *U : V->users()) { + if(!isa(U)) { + // If User is not an instruction continue to next user + continue; + } + + Instruction *I = cast(U); + Function *Fn = I->getFunction(); + + // Duplicate instruction only if this isn't an already duplicated function + if(!Fn->getName().ends_with("_dup")) { + BasicBlock *ErrBB = nullptr; + bool newErrBB = true; + + // Search pre-existant ErrBB + for(BasicBlock &BB : *Fn) { + if(BB.getName().starts_with("ErrBB")) { + ErrBB = &BB; + newErrBB = false; // ErrBB already present + } + } + + if(newErrBB) { + ErrBB = BasicBlock::Create(Fn->getContext(), "ErrBB", Fn); + } + + if(!isa(I)) { + if(duplicateInstruction(*I, DuplicatedInstructionMap, *ErrBB)) { + if(InstructionsToRemove.find(I) == InstructionsToRemove.end()) { + InstructionsToRemove.insert(I); } - Arg = Fn.getArg(i); - ArgClone = Fn.getArg(i + Fn.arg_size() / 2); - } else { - if (i % 2 == 1) - continue; - Arg = Fn.getArg(i); - ArgClone = Fn.getArg(i + 1); } - DuplicatedInstructionMap.insert( - std::pair(Arg, ArgClone)); - DuplicatedInstructionMap.insert( - std::pair(ArgClone, Arg)); - for (User *U : Arg->users()) { - if (isa(U)) { - auto *I = cast(U); - if (!isValueDuplicated(DuplicatedInstructionMap, *I)) { - int shouldDelete = - duplicateInstruction(*I, DuplicatedInstructionMap, *ErrBB); - // the instruction duplicated may be equal to the original, so we - // return shouldDelete in order to drop the duplicates - if (shouldDelete) { - InstructionsToRemove.push_back(&*I); - } - } + } else { + GrayAreaCallsToFix.insert(cast(I)); + } + + if(newErrBB) { + // insert the code for calling the error basic block in case of a mismatch + CreateErrBB(Md, *Fn, ErrBB); + } + } + } + } + + // Protect only the explicitly marked `to_harden` functions + LLVM_DEBUG(dbgs() << "Getting all GrayAreaCallsToFix...\n"); + for(auto annot : FuncAnnotations) { + if(annot.second.starts_with("to_harden")) { + if(isa(annot.first)) { + auto Fn = cast(annot.first); + outs() << "Adding to GrayAreaCallsToFix all calls of " << Fn->getName() << "\n"; + // Get function calls in gray area + for(auto U : getFunctionFromDuplicate(Fn)->users()) { + if(isa(U)) { + auto caller = cast(U)->getFunction(); + // Protect this call if it's not in toHardenFunction and is not marked as `exclude` + if(toHardenFunctions.find(caller) == toHardenFunctions.end() && + (FuncAnnotations.find(caller) == FuncAnnotations.end() || !FuncAnnotations.find(caller)->second.starts_with("exclude"))) { + outs() << "GrayAreaCallsToFix added: " << *U << "\n"; + GrayAreaCallsToFix.insert(cast(U)); } } } } - for (Instruction *I2rm : InstructionsToRemove) { - I2rm->eraseFromParent(); + } + } + + LLVM_DEBUG(dbgs() << "Fixing gray area calls\n"); + // Add alloca and memcpy of non duplicated instructions and use that as duplciated instr + for(CallBase *CInstr : GrayAreaCallsToFix) { + if(FuncAnnotations.find(CInstr->getCalledFunction()) != FuncAnnotations.end() && + FuncAnnotations.find(CInstr->getCalledFunction())->second.starts_with("exclude")) { + // Maybe check if have to fix operands and return after the call + errs() << "About to duplicate a call not to duplciate: " << *CInstr << "\n"; + continue; + } + + + // Map with the duplicated instructions, including the temporary load ones + std::map TmpDuplicatedInstructionMap{DuplicatedInstructionMap}; + Function *Fn = CInstr->getFunction(); + BasicBlock *ErrBB = nullptr; + bool newErrBB = true; + + // Search pre-existant ErrBB + for(BasicBlock &BB : *Fn) { + if(BB.getName().starts_with("ErrBB")) { + ErrBB = &BB; + newErrBB = false; // ErrBB already present } - InstructionsToRemove.clear(); - std::list instructionsToDuplicate; - for (BasicBlock &BB : Fn) { - for (Instruction &I : BB) { - if (!isValueDuplicated(DuplicatedInstructionMap, I)) { - instructionsToDuplicate.push_back(&I); - } + } + + if(newErrBB) { + ErrBB = BasicBlock::Create(Fn->getContext(), "ErrBB", Fn); + } + + // Set insertion point for the load instructions + IRBuilder<> B(CInstr); + B.SetInsertPoint(CInstr); + + for (unsigned i = 0; i < CInstr->arg_size(); i++) { + // Populate args and ParamTypes from the original instruction + Value *Arg = CInstr->getArgOperand(i); + + // If argument has already a duplicate, nothing to do + if(TmpDuplicatedInstructionMap.find(Arg) != TmpDuplicatedInstructionMap.end() || !isa(Arg)) { + // If Argument already duplicated continue to next argument + continue; + } + + // Create alloca and memcpy only if ptr since if it is a value, we can just pass two times the same value + if(Arg->getType()->isPointerTy() && !CInstr->isByValArgument(i) && isa(Arg) && !isa(Arg)) + { + const llvm::DataLayout &DL = Md.getDataLayout(); + Type *ArgType; + + Align ArgAlign; + ArgType = getValueType(Arg, &ArgAlign); + + // If can't find type, do not duplicate argument + if(ArgType->isVoidTy()) { + continue; } + + uint64_t SizeInBytes = DL.getTypeAllocSize(ArgType); + Value *Size = llvm::ConstantInt::get(B.getInt64Ty(), SizeInBytes); + + // Alignment (assuming alignment of 1 here; adjust as necessary) + llvm::ConstantInt *Align = B.getInt32(ArgAlign.value()); + + // Volatility (non-volatile in this example) + llvm::ConstantInt *IsVolatile = B.getInt1(false); + + // Create the memcpy call + auto CopyArg = B.CreateAlloca(ArgType); + + llvm::CallInst *memcpy_call = B.CreateMemCpy(CopyArg, Arg->getPointerAlignment(DL), Arg, Arg->getPointerAlignment(DL), Size); + + TmpDuplicatedInstructionMap.insert(std::pair(CopyArg, Arg)); + TmpDuplicatedInstructionMap.insert(std::pair(Arg, CopyArg)); + } else { + // Otherwise pass two times the same arg + TmpDuplicatedInstructionMap.insert(std::pair(Arg, Arg)); } + } - for (auto &I : instructionsToDuplicate) { - // perform the duplication - if (!isValueDuplicated(DuplicatedInstructionMap, *I)) { - int shouldDelete = - duplicateInstruction(*I, DuplicatedInstructionMap, *ErrBB); - // the instruction duplicated may be equal to the original, so we - // return shouldDelete in order to drop the duplicates - if (shouldDelete) { - InstructionsToRemove.push_back(&*I); + // Finally, duplicate the call with the temporary DuplicatedInstructionMap + if(duplicateInstruction(*CInstr, TmpDuplicatedInstructionMap, *ErrBB)) { + if(InstructionsToRemove.find(CInstr) == InstructionsToRemove.end()) { + InstructionsToRemove.insert(CInstr); + } + } + + + if(newErrBB) { + // insert the code for calling the error basic block in case of a mismatch + CreateErrBB(Md, *Fn, ErrBB); + } + } + + LLVM_DEBUG(dbgs() << "Fixing invokes\n"); + for(InvokeInst *IInstr : toFixInvokes) { + if(IInstr == NULL) { + errs() << "To fix a null invoke\n"; + continue; + } + + // Split every toFixInvoke in two different BBs, with the first having the normal continuation + // to the next invoke and both having the same landingpad + auto *NewBB = IInstr->getParent()->splitBasicBlockBefore(IInstr->getNextNonDebugInstruction()); + auto *BrI = NewBB->getTerminator(); + BrI->removeFromParent(); + BrI->deleteValue(); + + // Update the first invoke's normal destination + IInstr->setNormalDest(NewBB->getNextNode()); + } + + LLVM_DEBUG(dbgs() << "Remove instructions\n"); + // Drop the instructions that have been marked for removal earlier + for (Instruction *I2rm : InstructionsToRemove) { + if(I2rm == NULL) { + errs() << "To remove a null instruction\n"; + continue; + } + + I2rm->eraseFromParent(); + } + + LLVM_DEBUG(dbgs() << "Fixing global ctors\n"); + fixGlobalCtors(Md); + + // Fixing calls to default handlers + LLVM_DEBUG(dbgs() << "Fixing DataCorruptionHandlers\n"); + auto *DataCorruptionH = Md.getFunction(getLinkageName(linkageMap, "DataCorruption_Handler")); + for(User *U : DataCorruptionH->users()) { + if(isa(U)) { + CallBase *CallI = cast(U); + auto dbgLoc = findNearestDebugLoc(*CallI); + if(dbgLoc) + CallI->setDebugLoc(dbgLoc); + } + } + + LLVM_DEBUG(dbgs() << "Persisting Compiled Functions...\n"); + persistCompiledFunctions(CompiledFuncs, "compiled_eddi_functions.csv"); + +/* if (Function *mainFunc = Md.getFunction("main")) { + errs() << *mainFunc; + } else { + errs() << "Function 'main' not found!\n"; + } + */ + return PreservedAnalyses::none(); +} + +/** + * @brief Fix all calls to duplicated funcitons or original functions, calling the relative _dup or _original version + */ +void EDDI::fixNonDuplicatedFunctions(Module &Md, std::map DuplicatedInstructionMap, std::set DuplicatedFns){ + for(auto &Fn : Md){ + for(auto &B : Fn){ + for(auto &I : B){ + if(isa(I) ){ + CallBase &ICall = cast(I); + Function *calledFn = ICall.getCalledFunction(); + + if(DuplicatedFns.find(calledFn) != DuplicatedFns.end()){ + // If duplicated function call the _dup variant + BasicBlock *ErrBB = BasicBlock::Create(Fn.getContext(), "ErrBB", &Fn); + duplicateInstruction(I, DuplicatedInstructionMap, *ErrBB); + CreateErrBB(Md, Fn, ErrBB); + } else { + if (calledFn != NULL && calledFn->hasName()) { + Function *OriginalFn = Md.getFunction(calledFn->getName().str() + "_original"); + if (OriginalFn != NULL) { + ICall.setCalledFunction(OriginalFn); + } + } } } } + } + } +} - // insert the code for calling the error basic block in case of a mismatch +void EDDI::CreateErrBB(Module &Md, Function &Fn, BasicBlock *ErrBB){ IRBuilder<> ErrB(ErrBB); assert(!getLinkageName(linkageMap, "DataCorruption_Handler").empty() && @@ -1124,7 +1855,6 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { auto *CallI = ErrB.CreateCall(CalleeF); ErrB.CreateUnreachable(); - #ifdef DC_HANDLER_INLINE std::list errBranches; for (User *U : ErrBB->users()) { Instruction *I = cast(U); @@ -1138,7 +1868,7 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { if (DebugEnabled) { for (Instruction &ErrI : *ErrBBCopy) { if (!I->getDebugLoc()) { - ErrI.setDebugLoc(findNearestDebugLoc(Fn.back().getTerminator())); + ErrI.setDebugLoc(findNearestDebugLoc(*Fn.back().getTerminator())); } else { ErrI.setDebugLoc(I->getDebugLoc()); } @@ -1147,62 +1877,74 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { I->replaceSuccessorWith(ErrBB, ErrBBCopy); } ErrBB->eraseFromParent(); - #else - if (DebugEnabled) { - // TODO fix possible null derereference below - for (Instruction &ErrI : *ErrBB) { - auto DL = findNearestDebugLoc(getSingleReturnInst(Fn)); - if (!DL) { - DL = findNearestDebugLoc(Fn.back().getTerminator()); - } - if (!DL) { - DL = findNearestDebugLoc(Fn.back().getTerminator()); - } - ErrI.setDebugLoc(DL); - } - } - #endif } - } - // Drop the instructions that have been marked for removal earlier - for (Instruction *I2rm : InstructionsToRemove) { - I2rm->eraseFromParent(); - } +void EDDI::fixGlobalCtors(Module &M) { + LLVMContext &Context = M.getContext(); - persistCompiledFunctions(CompiledFuncs, "compiled_eddi_functions.csv"); + // Retrieve the existing @llvm.global_ctors. + GlobalVariable *GlobalCtors = M.getGlobalVariable("llvm.global_ctors"); + if (!GlobalCtors) { + errs() << "Error: @llvm.global_ctors not found in the module.\n"; + return; + } - std::list FnsToRemove; - int removed; - do { - FnsToRemove.clear(); - removed = 0; - for (Function &Fn : Md) { - if ((Fn.getName().ends_with("_dup") || Fn.getName().ends_with("_ret") || Fn.getName().ends_with("original") )) { - bool shouldRemove = true; - for (auto U : Fn.users()) { - if (isa(U) || isa(U)) { - shouldRemove = false; - break; - } + // Get the constantness and the section name of the existing global variable. + bool isConstant = GlobalCtors->isConstant(); + StringRef Section = GlobalCtors->getSection(); + + // Get the type of the annotations array and struct. + ArrayType *CtorsArrayType = cast(GlobalCtors->getValueType()); + StructType *CtorStructType = cast(CtorsArrayType->getElementType()); + + // Create the new Ctor struct fields. + PointerType *Int8PtrType = Type::getInt8Ty(Context)->getPointerTo(); + Constant *IntegerConstant = ConstantInt::get(Type::getInt32Ty(Context), 65535); + Constant *NullPtr = ConstantPointerNull::get(Int8PtrType); // Null pointer for other fields. + + // Retrieve existing annotations and append the new one. + std::vector Ctors; + if (ConstantArray *ExistingArray = dyn_cast(GlobalCtors->getInitializer())) { + for (unsigned i = 0; i < ExistingArray->getNumOperands(); ++i) { + auto *ctorStr = ExistingArray->getOperand(i); + + auto *ctor = ctorStr->getOperand(1); + if(isa(ctor)){ + Function *dupCtor = getFunctionDuplicate(cast(ctor)); + // If there isn't the duplicated constructor, use the original one + if(dupCtor == NULL) { + dupCtor = cast(ctor); } - if (shouldRemove) FnsToRemove.push_back(&Fn); - } - else { - //auto FnDup = getFunctionDuplicate(&Fn); - /* if (!Fn.getName().equals("main") && !Fn.getName().equals("DataCorruption_Handler") && !Fn.getName().equals("SigMismatch_Handler") && !Fn.isDeclaration() && Fn.getNumUses() == 0) { - FnsToRemove.push_back(&Fn); - } */ + + Constant *CtorAsConstant = ConstantExpr::getBitCast(dupCtor, Int8PtrType);; + // Create the new Ctor struct. + Constant *NewCtor = ConstantStruct::get( + CtorStructType, + {IntegerConstant, CtorAsConstant, NullPtr}); + Ctors.push_back(NewCtor); } } + } - for (auto Fn : FnsToRemove) { - //errs() << "Erasing: " << Fn->getName() << "\n"; - Fn->eraseFromParent(); - removed++; - } - } while (removed > 0); - return PreservedAnalyses::none(); + // Create a new array with the correct type and size. + ArrayType *NewCtorArrayType = ArrayType::get(CtorStructType, Ctors.size()); + Constant *NewCtorArray = ConstantArray::get(NewCtorArrayType, Ctors); + + // Remove the old global variable from the module's symbol table. + GlobalCtors->removeFromParent(); + delete GlobalCtors; + + // Create a new global variable with the exact name "llvm.global_ctors". + GlobalVariable *NewGlobalCtors = new GlobalVariable( + M, + NewCtorArray->getType(), + isConstant, + GlobalValue::AppendingLinkage, // Must use appending linkage for @llvm.global_ctors. + NewCtorArray, + "llvm.global_ctors"); + + // Set the section to match the original. + NewGlobalCtors->setSection(Section); } //----------------------------------------------------------------------------- @@ -1224,9 +1966,14 @@ llvm::PassPluginLibraryInfo getEDDIPluginInfo() { [](StringRef Name, ModulePassManager &FPM, ArrayRef) { if (Name == "eddi-verify") { - FPM.addPass(EDDI()); +#ifdef DUPLICATE_ALL + FPM.addPass(EDDI(true)); +#else + FPM.addPass(EDDI(false)); +#endif return true; } + return false; }); PB.registerPipelineParsingCallback( diff --git a/passes/Utils/Utils.cpp b/passes/Utils/Utils.cpp index 7fcbb15..dc64512 100644 --- a/passes/Utils/Utils.cpp +++ b/passes/Utils/Utils.cpp @@ -1,5 +1,6 @@ #include "Utils.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/ADT/Statistic.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Function.h" @@ -121,44 +122,46 @@ bool shouldCompile(Function &Fn, && OriginalFunctions.find(&Fn) == OriginalFunctions.end(); } -DebugLoc findNearestDebugLoc(Instruction *I) { +DebugLoc findNearestDebugLoc(Instruction &I) { std::list candidates; - if (I == nullptr) { - return nullptr; - } - - if (I->getDebugLoc()) return I->getDebugLoc(); - auto *PrevI = I->getPrevNonDebugInstruction(); + Instruction *PrevI = &I; - while (PrevI && (PrevI = PrevI->getPrevNonDebugInstruction())) { + while (PrevI != NULL && (PrevI = PrevI->getPrevNonDebugInstruction())) { if (auto DL = PrevI->getDebugLoc()) { return DL; } } - for (auto *U : I->getParent()->users()) { - candidates.push_back(cast(U)->getParent()); + for (auto *U : I.getParent()->users()) { + if(isa(U)) { + candidates.push_back(cast(U)->getParent()); + } } - for (auto *BB : candidates) { - PrevI = BB->getTerminator(); - while ((PrevI = PrevI->getPrevNonDebugInstruction())) { - if (auto DL = PrevI->getDebugLoc()) { - return DL; + std::list newCandidates{candidates}; + while(!newCandidates.empty()) { + candidates = newCandidates; + newCandidates.clear(); + for (auto *BB : candidates) { + PrevI = BB->getTerminator(); + while (PrevI != NULL && (PrevI = PrevI->getPrevNonDebugInstruction(true))) { + if (auto DL = PrevI->getDebugLoc()) { + return DL; + } } - } - for (auto *U : BB->users()) { - if(std::find(candidates.begin(), candidates.end(), cast(U)->getParent()) == candidates.end()) { - candidates.push_back(cast(U)->getParent()); + for (auto *U : BB->users()) { + if(isa(U)) { + if(std::find(newCandidates.begin(), newCandidates.end(), cast(U)->getParent()) == newCandidates.end()) { + newCandidates.push_back(cast(U)->getParent()); + } + } } } } - errs() << "Could not find nearest debug location!\n"; - errs() << "Instruction: " << *I << "\n"; - errs() << "In function: \n"; - errs() << *I->getParent()->getParent() << "\n"; + errs() << "Could not find nearest debug location! Aborting compilation.\n"; + errs() << *I.getParent()->getParent() << "\n"; return nullptr; } @@ -176,8 +179,6 @@ LinkageMap mapFunctionLinkageNames(const Module &M) { return linkageMap; } -#include "llvm/Support/raw_ostream.h" - void printLinkageMap(const LinkageMap &linkageMap) { for (const auto &entry : linkageMap) { errs() << "Function Name: " << entry.first << "\n"; @@ -205,13 +206,32 @@ StringRef getLinkageName(const LinkageMap &linkageMap, const std::string &functi } } -bool isIntrinsicToDuplicate(CallBase *CInstr) { - Intrinsic::ID intrinsicID = CInstr->getIntrinsicID(); - if (intrinsicID != Intrinsic::not_intrinsic /* intrinsicID == Intrinsic::memcpy || intrinsicID == Intrinsic::memset */) { - return true; - } +bool isToDuplicate(CallBase *CInstr) { + Intrinsic::ID intrinsicID = CInstr->getIntrinsicID(); + if (intrinsicID != Intrinsic::not_intrinsic) { + return true; + } else if(CInstr->getCalledFunction() != NULL && isToDuplicateName(CInstr->getCalledFunction()->getName())) { + return true; + } + + return false; +} + +bool isToDuplicateName(StringRef FnMangledName) { + auto FnName = demangle(FnMangledName.str()); + // outs() << FnName << " " << FnName.find("std::") << "\n"; + if(FnName.find("operator new") == 0 || FnName.find("std::") != FnName.npos || FnName.find("fmt::") != FnName.npos || FnName.find("Eigen::") != FnName.npos) { + // outs() << "duplicated\n"; + + if(FnName.find("std::ostream") != FnName.npos || FnName.find("std::basic_ostream") != FnName.npos || FnName.find("std::basic_ios") != FnName.npos || FnName.find("std::basic_ios") != FnName.npos) { + // outs() << "not duplicated\n"; + return false; + } + + return true; + } - return false; + return false; } void createFtFunc(Module &Md, StringRef name) { diff --git a/passes/Utils/Utils.h b/passes/Utils/Utils.h index f0c5d37..a381676 100644 --- a/passes/Utils/Utils.h +++ b/passes/Utils/Utils.h @@ -39,12 +39,14 @@ bool shouldCompile(Function &Fn, const std::map &FuncAnnotations, const std::set &OriginalFunctions = std::set()); -DebugLoc findNearestDebugLoc(Instruction *I); +DebugLoc findNearestDebugLoc(Instruction &I); LinkageMap mapFunctionLinkageNames(const Module &M); void printLinkageMap(const LinkageMap &linkageMap); StringRef getLinkageName(const LinkageMap &linkageMap, const std::string &functionName); -bool isIntrinsicToDuplicate(CallBase *CInstr); +bool isToDuplicateName(StringRef FnMangledName); +bool isToDuplicate(CallBase *CInstr); +// bool isIntrinsicToDuplicate(CallBase *CInstr); void createFtFuncs(Module &Md); From 07357a7b447a0402633b182a3561eb62271cc361 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Tue, 31 Mar 2026 18:46:28 +0200 Subject: [PATCH 02/16] feat(REDDI): Duplicate entrypoint in-place and bugfix - Now the entrypoint of the program (usually the "main" function) is duplicated in-place when using EDDI, so that the program directly enters the Sphere of Replication - Avoided the search for debug locations when debug is not enabled --- passes/ASPIS.h | 4 +++- passes/EDDI.cpp | 46 ++++++++++++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/passes/ASPIS.h b/passes/ASPIS.h index 4f97b5d..b59c4ba 100644 --- a/passes/ASPIS.h +++ b/passes/ASPIS.h @@ -40,6 +40,8 @@ class EDDI : public PassInfoMixin { std::set toHardenFunctions; std::set toHardenVariables; std::set DuplicatedCalls; + + std::string entryPoint; LinkageMap linkageMap; @@ -68,7 +70,7 @@ class EDDI : public PassInfoMixin { void fixGlobalCtors(Module &M); void fixNonDuplicatedFunctions(Module &Md, std::map DuplicatedInstructionMap, std::set DuplicatedFns); public: - explicit EDDI(bool duplicateAll) : duplicateAll(duplicateAll) {} + explicit EDDI(bool duplicateAll, std::string entryPoint = "main") : duplicateAll(duplicateAll), entryPoint(entryPoint) {} PreservedAnalyses run(Module &M, ModuleAnalysisManager &); diff --git a/passes/EDDI.cpp b/passes/EDDI.cpp index 9f840ba..138b49b 100755 --- a/passes/EDDI.cpp +++ b/passes/EDDI.cpp @@ -462,7 +462,7 @@ void EDDI::preprocess(Module &Md) { toHardenFunctions.insert(Fn); toAddVariables.insert(U); } else { - LLVM_DEBUG(errs() << "[REDDI] Indirect Function to harden (called by " << V->getName() << ")\n"); + errs() << "[REDDI] Indirect Function to harden (called by " << V->getName() << ")\n"; // continue; } } else { @@ -517,7 +517,7 @@ void EDDI::preprocess(Module &Md) { // LLVM_DEBUG(dbgs() << "[REDDI] Added: " << CalledFn->getName() << "\n"); } } else { - // LLVM_DEBUG(errs() << "[REDDI] Indirect Function to harden (called by " << Fn->getName() << ")\n"); + // errs() << "[REDDI] Indirect Function to harden (called by " << Fn->getName() << ")\n"; // I.print(errs()); // errs() << "\n"; } @@ -1492,6 +1492,17 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { // store the duplicated functions that are currently in the module std::set DuplicatedFns; +#ifdef DUPLICATE_ALL + // Insert in the set of "duplicated functions" the original "entrypoint" + // function, to protect it "in place" and staring all the execution from + // the Sphere of Replication. + if(Function *entryPointFn = Md.getFunction(entryPoint)) { + DuplicatedFns.insert(entryPointFn); + } else { + errs() << "[EDDI] Entry point function not found: " << entryPoint << "\n"; + } +#endif + // then duplicate the function arguments using toHardenFunctions LLVM_DEBUG(dbgs() << "Creating _dup functions\n"); for (Function *Fn : toHardenFunctions) { @@ -1582,7 +1593,7 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { } } } - LLVM_DEBUG(dbgs() << "[done]\n"); + LLVM_DEBUG(dbgs() << " [done]\n"); // insert the code for calling the error basic block in case of a mismatch CreateErrBB(Md, *Fn, ErrBB); @@ -1790,14 +1801,16 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { fixGlobalCtors(Md); // Fixing calls to default handlers - LLVM_DEBUG(dbgs() << "Fixing DataCorruptionHandlers\n"); - auto *DataCorruptionH = Md.getFunction(getLinkageName(linkageMap, "DataCorruption_Handler")); - for(User *U : DataCorruptionH->users()) { - if(isa(U)) { - CallBase *CallI = cast(U); - auto dbgLoc = findNearestDebugLoc(*CallI); - if(dbgLoc) - CallI->setDebugLoc(dbgLoc); + if(DebugEnabled){ + LLVM_DEBUG(dbgs() << "Fixing DataCorruptionHandlers\n"); + auto *DataCorruptionH = Md.getFunction(getLinkageName(linkageMap, "DataCorruption_Handler")); + for(User *U : DataCorruptionH->users()) { + if(isa(U)) { + CallBase *CallI = cast(U); + auto dbgLoc = findNearestDebugLoc(*CallI); + if(dbgLoc) + CallI->setDebugLoc(dbgLoc); + } } } @@ -1860,17 +1873,18 @@ void EDDI::CreateErrBB(Module &Md, Function &Fn, BasicBlock *ErrBB){ Instruction *I = cast(U); errBranches.push_back(I); } + for (Instruction *I : errBranches) { ValueToValueMapTy VMap; BasicBlock *ErrBBCopy = CloneBasicBlock(ErrBB, VMap); ErrBBCopy->insertInto(ErrBB->getParent(), I->getParent()); // set the debug location to the instruction the ErrBB is related to if (DebugEnabled) { - for (Instruction &ErrI : *ErrBBCopy) { - if (!I->getDebugLoc()) { - ErrI.setDebugLoc(findNearestDebugLoc(*Fn.back().getTerminator())); - } else { - ErrI.setDebugLoc(I->getDebugLoc()); + for (Instruction &ErrI : *ErrBBCopy) { + if (!I->getDebugLoc()) { + ErrI.setDebugLoc(findNearestDebugLoc(*Fn.back().getTerminator())); + } else { + ErrI.setDebugLoc(I->getDebugLoc()); } } } From de1d74570c2c8dac4064edc70edc5ea114ecd360 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Wed, 15 Apr 2026 20:40:48 +0200 Subject: [PATCH 03/16] fix(build_system): Now also FDSC and SEDDI have the DUPLICATE_ALL flag --- passes/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/passes/CMakeLists.txt b/passes/CMakeLists.txt index 7fd0175..2d8ef3b 100644 --- a/passes/CMakeLists.txt +++ b/passes/CMakeLists.txt @@ -26,7 +26,7 @@ add_library(FDSC SHARED FuncRetToRef.cpp Utils/Utils.cpp ) -target_compile_definitions(FDSC PRIVATE SELECTIVE_CHECKING=1 CHECK_AT_STORES CHECK_AT_CALLS CHECK_AT_BRANCH) +target_compile_definitions(FDSC PRIVATE DUPLICATE_ALL SELECTIVE_CHECKING=1 CHECK_AT_STORES CHECK_AT_CALLS CHECK_AT_BRANCH) # sEDDI add_library(SEDDI SHARED @@ -35,7 +35,7 @@ add_library(SEDDI SHARED FuncRetToRef.cpp Utils/Utils.cpp ) -target_compile_definitions(SEDDI PRIVATE SELECTIVE_CHECKING=0 CHECK_AT_CALLS CHECK_AT_BRANCH) +target_compile_definitions(SEDDI PRIVATE DUPLICATE_ALL SELECTIVE_CHECKING=0 CHECK_AT_CALLS CHECK_AT_BRANCH) # CFCSS From 6553b9d8a1d8824aaac4dad498fe0fddc96cb745 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Thu, 16 Apr 2026 09:02:59 +0200 Subject: [PATCH 04/16] feat(EDDI): Restored possibility to use private error blocks for each different consistency check - Now in EDDI we can generate an ErrBB block for each consistency check which, after the call to "DataCorruption_Handler()", jumps to the normal continuation basic block - CFCSS has been fixed to support multiple ErrBB --- passes/ASPIS.h | 6 +- passes/CFCSS.cpp | 169 +++++++++++++++++++++++----------------- passes/EDDI.cpp | 197 +++++++++++++++++++++++++++++------------------ 3 files changed, 226 insertions(+), 146 deletions(-) diff --git a/passes/ASPIS.h b/passes/ASPIS.h index b59c4ba..6626f28 100644 --- a/passes/ASPIS.h +++ b/passes/ASPIS.h @@ -46,6 +46,7 @@ class EDDI : public PassInfoMixin { LinkageMap linkageMap; bool duplicateAll; + bool MultipleErrBBEnabled; void preprocess(Module &Md); void fixDuplicatedConstructors(Module &Md); @@ -68,9 +69,8 @@ class EDDI : public PassInfoMixin { void CreateErrBB(Module &Md, Function &Fn, BasicBlock *ErrBB); void fixGlobalCtors(Module &M); - void fixNonDuplicatedFunctions(Module &Md, std::map DuplicatedInstructionMap, std::set DuplicatedFns); public: - explicit EDDI(bool duplicateAll, std::string entryPoint = "main") : duplicateAll(duplicateAll), entryPoint(entryPoint) {} + explicit EDDI(bool duplicateAll, bool MultipleErrBBEnabled = false, std::string entryPoint = "main") : duplicateAll(duplicateAll), MultipleErrBBEnabled(MultipleErrBBEnabled), entryPoint(entryPoint) {} PreservedAnalyses run(Module &M, ModuleAnalysisManager &); @@ -135,7 +135,7 @@ class CFCSS : public PassInfoMixin { const std::map &BBSigs, std::map *NewBBs, BasicBlock &ErrBB, - Value *G, Value *D); + Value *G, Value *D, int NeighborSig); public: PreservedAnalyses run(Module &M, diff --git a/passes/CFCSS.cpp b/passes/CFCSS.cpp index b286818..1ddca73 100755 --- a/passes/CFCSS.cpp +++ b/passes/CFCSS.cpp @@ -38,7 +38,7 @@ void CFCSS::initializeBlocksSignatures(Module &Md, std::map &B for (Function &Fn : Md) { if (shouldCompile(Fn, FuncAnnotations)) { for (BasicBlock &BB : Fn) { - if (!BB.getName().equals_insensitive("errbb")) // we skip this since "errbb" Basic Blocks are generated by EDDI + if (!BB.hasName() || !BB.getName().equals_insensitive("CFGErrBB")) // we skip this since "CFGErrBB" Basic Blocks are generated by EDDI BBSigs.insert(std::pair(&BB, Counter)); Counter++; } @@ -54,12 +54,19 @@ void CFCSS::initializeBlocksSignatures(Module &Md, std::map &B */ BasicBlock* CFCSS::getFirstPredecessor(BasicBlock &BB, const std::map &BBSigs) { + std::set Predecessors; + // check between the basic block actual predecessors for (auto *Pred : predecessors(&BB)) { if (BBSigs.find(Pred) != BBSigs.end()) { - return Pred; + Predecessors.insert(Pred); + } else { + errs() << "Error: predecessor " << Pred->getName() << " of basic block " << BB.getName() << " not found in the original set of basic blocks\n"; } } + if (!Predecessors.empty()) { + return *Predecessors.begin(); + } return NULL; } @@ -79,11 +86,15 @@ int CFCSS::getNeighborSig(BasicBlock &BB, const std::map &BBSi while (Todo.size() != 0) { BasicBlock *Elem = *Todo.begin(); // get the first element for (auto *Succ : successors(Elem)) { - if (BBSigs.find(Succ) != BBSigs.end()) { - for (auto *Pred : predecessors(Succ)) { - if (BBSigs.find(Pred) != BBSigs.end() && Candidates.find(Pred) == Candidates.end()){ - Todo.insert(Pred); - Candidates.insert(Pred); + if(!Succ->getName().contains_insensitive("CFGErrBB")) { + if (BBSigs.find(Succ) != BBSigs.end()) { + for (auto *Pred : predecessors(Succ)) { + if (BBSigs.find(Pred) != BBSigs.end() && Candidates.find(Pred) == Candidates.end()){ + if(!Pred->getName().contains_insensitive("CFGErrBB")) { + Todo.insert(Pred); + Candidates.insert(Pred); + } + } } } } @@ -102,7 +113,9 @@ bool CFCSS::hasNPredecessorsOrMore(BasicBlock &BB, int N, const std::map &BBSigs, const std // errs()<<"cond for "<getName()<<": "<< // Cond->getValueName() <<"\n"; - if (!isa(BB->getTerminator())) { + // if (!isa(BB->getTerminator())) { Value *Cond = &CFGVerificationBB->back(); B.CreateCondBr(Cond, BB, FuncErrBBs.find(BB->getParent())->second); - } - else { - //if the BB has an invoke at the end branch unconditionally - B.CreateBr(BB); - } + // } + // else { + // //if the BB has an invoke at the end branch unconditionally // Emilio: Why? + // B.CreateBr(BB); + // } // move all the phi instructions from the next BB into the CFGVerificationBB while (isa(BB->front())) { Instruction &PHIInst = BB->front(); @@ -153,8 +166,8 @@ void CFCSS::sortBasicBlocks(const std::map &BBSigs, const std I->replaceSuccessorWith(BB, CFGVerificationBB); } } - } } +} /** * Creates a new basic block for the CFG verification of basic block BB. @@ -162,15 +175,15 @@ void CFCSS::sortBasicBlocks(const std::map &BBSigs, const std * @param BB * @param BBSigs * @param NewBBs - * @param ErrBB + * @param CFGErrBB * @param G * @param D */ void CFCSS::createCFGVerificationBB (BasicBlock &BB, const std::map &BBSigs, std::map *NewBBs, - BasicBlock &ErrBB, - Value *G, Value *D) { + BasicBlock &CFGErrBB, + Value *G, Value *D, int NeighborSig) { // local variables int CurSig = BBSigs.find(&BB)->second; // current signature LLVMContext &C = BB.getModule()->getContext(); // the module context @@ -180,55 +193,66 @@ void CFCSS::createCFGVerificationBB (BasicBlock &BB, // if the block has a predecessor we use its signature, otherwise the sig = 0 if (BBSigs.find(Predecessor) != BBSigs.end()) { - if(hasNPredecessorsOrMore(BB, 2, BBSigs)) { - PredSig = getNeighborSig(*Predecessor, BBSigs); - } - else PredSig = BBSigs.find(Predecessor)->second; + // PredSig = getNeighborSig(*Predecessor, BBSigs); + // if(PredSig == -1) { + PredSig = BBSigs.find(Predecessor)->second; + // } + } + + if(PredSig == CurSig) { + errs() << "Error: the signature of the predecessor " << Predecessor->getName() << " of basic block " << BB.getName() << " is the same as the one of the block itself.\n"; } -//no CFG for landingPad + //no CFG for landingPad IRBuilder<> B(C); - if (isa(BB.getFirstNonPHI())) - { - //if the BB start with a landing pad instruction don't create CFGVerificationBB - B.SetInsertPoint(&*BB.getFirstInsertionPt()); - B.CreateStore(llvm::ConstantInt::get(IntType, CurSig), G); - } - else { - // initialize new basic block, add it to the NewBBs and initialize the builder - BasicBlock *CFGVerificationBB = BasicBlock::Create(C, "CFGVerificationBB_"+std::to_string(CurSig), BB.getParent()); - NewBBs->insert(std::pair(CurSig, CFGVerificationBB)); - B.SetInsertPoint(CFGVerificationBB); - - // create the body of the CFG verification basic block - Value *InstrG = B.CreateLoad(IntType, G, "LoadG"); // load InstrG from memory - Value *InstrDLower = - llvm::ConstantInt::get(IntType, CurSig ^ PredSig); - Value *InstrCurSig = llvm::ConstantInt::get(IntType, CurSig); - Value *XorRes; - - if(hasNPredecessorsOrMore(BB, 2, BBSigs)) { - // if we have multiple predecessors we compute the result as d ^ G ^ D - Value *InstrD = B.CreateLoad(IntType, D, "LoadD"); - XorRes = B.CreateXor(InstrDLower, B.CreateXor(InstrG, InstrD), "RunTimeG"); + if(BB.getName().contains_insensitive("CFGErrBB")) { + return; } - else { - // otherwise the result is just d ^ G - XorRes = B.CreateXor(InstrDLower, InstrG); + + if (!isa(BB.getFirstNonPHI())) { + // initialize new basic block, add it to the NewBBs and initialize the builder + BasicBlock *CFGVerificationBB = BasicBlock::Create(C, "CFGVerificationBB_"+std::to_string(CurSig), BB.getParent()); + NewBBs->insert(std::pair(CurSig, CFGVerificationBB)); + B.SetInsertPoint(CFGVerificationBB); + + // create the body of the CFG verification basic block + Value *InstrG = B.CreateLoad(IntType, G, true, "LoadG"); // load InstrG from memory + Value *InstrDLower = + llvm::ConstantInt::get(IntType, CurSig ^ PredSig); + Value *InstrCurSig = llvm::ConstantInt::get(IntType, CurSig); + Value *XorRes; + + if(hasNPredecessorsOrMore(BB, 2, BBSigs)) { + // if we have multiple predecessors we compute the result as d ^ G ^ D + Value *InstrD = B.CreateLoad(IntType, D, true, "LoadD"); + XorRes = B.CreateXor(InstrDLower, B.CreateXor(InstrG, InstrD), "RunTimeG"); + } + else { + // otherwise the result is just d ^ G + XorRes = B.CreateXor(InstrDLower, InstrG); + + } + B.CreateStore(XorRes, G, true); + // compare the new run-time signature (stored in XorRes) with the signature of the block - } - B.CreateStore(XorRes, G, false); - // compare the new run-time signature (stored in XorRes) with the signature of the block - - // if the BB has a neighbor, it means that we also have to compute D - int NeighborSig = getNeighborSig(BB, BBSigs); - if (NeighborSig != -1) { - Value *InstrD = - llvm::ConstantInt::get(IntType, CurSig ^ NeighborSig); - B.CreateStore(InstrD, D); - } - Value *Cond = B.CreateCmp(llvm::CmpInst::ICMP_EQ, XorRes, InstrCurSig); + // if the BB has a neighbor, it means that we also have to compute D + if (NeighborSig != -1) { + Value *InstrD = + llvm::ConstantInt::get(IntType, CurSig ^ NeighborSig); + B.CreateStore(InstrD, D, true); + } + Value *Cond = B.CreateCmp(llvm::CmpInst::ICMP_EQ, XorRes, InstrCurSig); + } else { + //if the BB start with a landing pad instruction don't create CFGVerificationBB + B.SetInsertPoint(&*BB.getFirstInsertionPt()); + + if (NeighborSig != -1) { + Value *InstrD = + llvm::ConstantInt::get(IntType, CurSig ^ NeighborSig); + B.CreateStore(InstrD, D, true); + } + B.CreateStore(llvm::ConstantInt::get(IntType, CurSig), G, true); } // the Branch instruction is inserted later in the function sortBasicBlocks() @@ -243,6 +267,13 @@ PreservedAnalyses CFCSS::run(Module &Md, ModuleAnalysisManager &AM) { std::map BBSigs; initializeBlocksSignatures(Md, BBSigs); + // Precompute NeighborSigs for all original BBs before modifying the CFG + std::map NeighborSigs; + for (const auto &pair : BBSigs) { + BasicBlock *BB = pair.first; + NeighborSigs[BB] = getNeighborSig(*BB, BBSigs); + } + // map of signatures of basic blocks and their CFG-verification basic blocks std::map NewBBs; @@ -274,34 +305,34 @@ PreservedAnalyses CFCSS::run(Module &Md, ModuleAnalysisManager &AM) { Value *G = B.CreateAlloca(IntType, (llvm::Value *)nullptr, "G"); Value *D = B.CreateAlloca(IntType, (llvm::Value *)nullptr, "D"); Value *InstrG = llvm::ConstantInt::get(IntType, CurSig); - B.CreateStore(InstrG, G, false); + B.CreateStore(InstrG, G, true); // if the Fn's first BasicBlock has a neighbor, it means that we have to compute D int NeighborSig = getNeighborSig(Fn.front(), BBSigs); if (NeighborSig != -1) { Value *InstrD = llvm::ConstantInt::get(IntType, CurSig ^ NeighborSig); - B.CreateStore(InstrD, D); + B.CreateStore(InstrD, D, true); } // add the error basic block to jump to in case of error - BasicBlock *ErrBB = BasicBlock::Create(Fn.getContext(), "ErrBB", &Fn); + BasicBlock *CFGErrBB = BasicBlock::Create(Fn.getContext(), "CFGErrBB", &Fn); // insert the actual cfg verification basic blocks in the function for (auto &Elem : BBSigs) { BasicBlock *BB = Elem.first; - if (!BB->isEntryBlock() && BB->getParent() == &Fn) { - createCFGVerificationBB(*BB, BBSigs, &NewBBs, *ErrBB, G, D); + if (!BB->isEntryBlock() && BB->getParent() == &Fn) { + createCFGVerificationBB(*BB, BBSigs, &NewBBs, *CFGErrBB, G, D, NeighborSigs[BB]); } } - IRBuilder<> ErrB(ErrBB); + IRBuilder<> ErrB(CFGErrBB); assert(!getLinkageName(linkageMap,"SigMismatch_Handler").empty() && "Function SigMismatch_Handler is missing!"); - auto CalleeF = ErrBB->getModule()->getOrInsertFunction( + auto CalleeF = CFGErrBB->getModule()->getOrInsertFunction( getLinkageName(linkageMap,"SigMismatch_Handler"), FunctionType::getVoidTy(Md.getContext())); ErrB.CreateCall(CalleeF)->setDebugLoc(debugLoc); ErrB.CreateUnreachable(); - ErrBBs.insert(std::pair(&Fn, ErrBB)); + ErrBBs.insert(std::pair(&Fn, CFGErrBB)); } } diff --git a/passes/EDDI.cpp b/passes/EDDI.cpp index 138b49b..899d408 100755 --- a/passes/EDDI.cpp +++ b/passes/EDDI.cpp @@ -1622,11 +1622,13 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { BasicBlock *ErrBB = nullptr; bool newErrBB = true; - // Search pre-existant ErrBB - for(BasicBlock &BB : *Fn) { - if(BB.getName().starts_with("ErrBB")) { - ErrBB = &BB; - newErrBB = false; // ErrBB already present + // Search pre-existant ErrBB if single basic block error handling is enabled + if(!MultipleErrBBEnabled) { + for(BasicBlock &BB : *Fn) { + if(BB.getName().starts_with("ErrBB")) { + ErrBB = &BB; + newErrBB = false; // ErrBB already present + } } } @@ -1644,10 +1646,8 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { GrayAreaCallsToFix.insert(cast(I)); } - if(newErrBB) { - // insert the code for calling the error basic block in case of a mismatch - CreateErrBB(Md, *Fn, ErrBB); - } + // insert the code for calling the error basic block in case of a mismatch + CreateErrBB(Md, *Fn, ErrBB); } } } @@ -1692,11 +1692,13 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { BasicBlock *ErrBB = nullptr; bool newErrBB = true; - // Search pre-existant ErrBB - for(BasicBlock &BB : *Fn) { - if(BB.getName().starts_with("ErrBB")) { - ErrBB = &BB; - newErrBB = false; // ErrBB already present + // Search pre-existant ErrBB if single basic block error handling is enabled + if(!MultipleErrBBEnabled) { + for(BasicBlock &BB : *Fn) { + if(BB.getName().starts_with("ErrBB")) { + ErrBB = &BB; + newErrBB = false; // ErrBB already present + } } } @@ -1761,11 +1763,8 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { } } - - if(newErrBB) { - // insert the code for calling the error basic block in case of a mismatch - CreateErrBB(Md, *Fn, ErrBB); - } + // insert the code for calling the error basic block in case of a mismatch + CreateErrBB(Md, *Fn, ErrBB); } LLVM_DEBUG(dbgs() << "Fixing invokes\n"); @@ -1806,10 +1805,11 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { auto *DataCorruptionH = Md.getFunction(getLinkageName(linkageMap, "DataCorruption_Handler")); for(User *U : DataCorruptionH->users()) { if(isa(U)) { - CallBase *CallI = cast(U); - auto dbgLoc = findNearestDebugLoc(*CallI); - if(dbgLoc) - CallI->setDebugLoc(dbgLoc); + if(auto *CallI = cast(U)) { + if(auto dbgLoc = findNearestDebugLoc(*CallI)) { + CallI->setDebugLoc(dbgLoc); + } + } } } } @@ -1826,72 +1826,121 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { return PreservedAnalyses::none(); } -/** - * @brief Fix all calls to duplicated funcitons or original functions, calling the relative _dup or _original version - */ -void EDDI::fixNonDuplicatedFunctions(Module &Md, std::map DuplicatedInstructionMap, std::set DuplicatedFns){ - for(auto &Fn : Md){ - for(auto &B : Fn){ - for(auto &I : B){ - if(isa(I) ){ - CallBase &ICall = cast(I); - Function *calledFn = ICall.getCalledFunction(); - - if(DuplicatedFns.find(calledFn) != DuplicatedFns.end()){ - // If duplicated function call the _dup variant - BasicBlock *ErrBB = BasicBlock::Create(Fn.getContext(), "ErrBB", &Fn); - duplicateInstruction(I, DuplicatedInstructionMap, *ErrBB); - CreateErrBB(Md, Fn, ErrBB); - } else { - if (calledFn != NULL && calledFn->hasName()) { - Function *OriginalFn = Md.getFunction(calledFn->getName().str() + "_original"); - if (OriginalFn != NULL) { - ICall.setCalledFunction(OriginalFn); - } - } - } - } - } +Instruction *getSingleReturnInst(Function &F) { + for (BasicBlock &BB : F) { + if (auto *retInst = llvm::dyn_cast(BB.getTerminator())) { + return retInst; } } + return nullptr; } void EDDI::CreateErrBB(Module &Md, Function &Fn, BasicBlock *ErrBB){ - IRBuilder<> ErrB(ErrBB); + if(ErrBB->getNumUses() == 0) { + ErrBB->eraseFromParent(); + return; + } + + IRBuilder<> ErrB(ErrBB); + + assert(!getLinkageName(linkageMap, "DataCorruption_Handler").empty() && + "Function DataCorruption_Handler is missing!"); + auto CalleeF = ErrBB->getModule()->getOrInsertFunction( + getLinkageName(linkageMap, "DataCorruption_Handler"), + FunctionType::getVoidTy(Md.getContext())); + + auto *CallI = ErrB.CreateCall(CalleeF); + + if(MultipleErrBBEnabled) { + // Insert one error block for each consistency check so that a specific + // recovery and continuation is possible - assert(!getLinkageName(linkageMap, "DataCorruption_Handler").empty() && - "Function DataCorruption_Handler is missing!"); - auto CalleeF = ErrBB->getModule()->getOrInsertFunction( - getLinkageName(linkageMap, "DataCorruption_Handler"), - FunctionType::getVoidTy(Md.getContext())); + std::list errBranches; + for (User *U : ErrBB->users()) { + Instruction *I = cast(U); + errBranches.push_back(I); + } + + // For each consistency check branch to the ErrBB, we create a new + // error block which jumps, at the end, to the normal continuation + for (Instruction *I : errBranches) { + ValueToValueMapTy VMap; + BasicBlock *ErrBBCopy = CloneBasicBlock(ErrBB, VMap); + ErrBBCopy->insertInto(ErrBB->getParent(), I->getParent()); - auto *CallI = ErrB.CreateCall(CalleeF); - ErrB.CreateUnreachable(); + BasicBlock *NormalContinuation = nullptr; + if (isa(I)) { + BranchInst *BI = cast(I); + if (BI->isConditional()) { + NormalContinuation = BI->getSuccessor(0) == ErrBB ? + BI->getSuccessor(1) : BI->getSuccessor(0); + } + } else if (isa(I)) { + InvokeInst *II = cast(I); + NormalContinuation = II->getNormalDest(); + } - std::list errBranches; - for (User *U : ErrBB->users()) { - Instruction *I = cast(U); - errBranches.push_back(I); + if (NormalContinuation) { + IRBuilder<> ErrBCopy(ErrBBCopy); + auto *BrInst = ErrBCopy.CreateBr(NormalContinuation); + if (DebugEnabled) { + BrInst->setDebugLoc(I->getDebugLoc()); + } + } else { + errs() << "Error: consistency check without a normal continuation! " << *I << "\n"; + IRBuilder<> ErrBCopy(ErrBBCopy); + ErrBCopy.CreateUnreachable(); } + I->replaceSuccessorWith(ErrBB, ErrBBCopy); + } + ErrBB->eraseFromParent(); + + if (DebugEnabled) { for (Instruction *I : errBranches) { - ValueToValueMapTy VMap; - BasicBlock *ErrBBCopy = CloneBasicBlock(ErrBB, VMap); - ErrBBCopy->insertInto(ErrBB->getParent(), I->getParent()); + auto *ErrBB = I->getSuccessor(1); // set the debug location to the instruction the ErrBB is related to - if (DebugEnabled) { - for (Instruction &ErrI : *ErrBBCopy) { - if (!I->getDebugLoc()) { - ErrI.setDebugLoc(findNearestDebugLoc(*Fn.back().getTerminator())); - } else { - ErrI.setDebugLoc(I->getDebugLoc()); + for (Instruction &ErrI : *ErrBB) { + if (!I->getDebugLoc()) { + if(Fn.back().getTerminator()) { + if(auto DL = findNearestDebugLoc(*Fn.back().getTerminator())) { + ErrI.setDebugLoc(DL); + } + } else if(Fn.back().getPrevNode()->getTerminator()) { + // In some cases, the last block of the function may not have a terminator (e.g., an incomplete ErrBB), + // so we check the previous block's terminator as well + if(auto DL = findNearestDebugLoc(*Fn.back().getPrevNode()->getTerminator())) { + ErrI.setDebugLoc(DL); + } } + } else { + ErrI.setDebugLoc(I->getDebugLoc()); } } - I->replaceSuccessorWith(ErrBB, ErrBBCopy); } - ErrBB->eraseFromParent(); } + } else { + // Leave just one error block for all consistency checks to minimize code size + ErrB.CreateUnreachable(); + + if (DebugEnabled) { + for (Instruction &ErrI : *ErrBB) { + if(auto retInst = getSingleReturnInst(Fn)) { + auto DL = findNearestDebugLoc(*retInst); + if (!DL && Fn.back().getTerminator()) { + DL = findNearestDebugLoc(*Fn.back().getTerminator()); + } + + if(DL) { + ErrI.setDebugLoc(DL); + } else { + errs() << "Warning: no debug location found for error block in function " << Fn.getName() << "\n"; + } + } + } + } + } +} void EDDI::fixGlobalCtors(Module &M) { LLVMContext &Context = M.getContext(); @@ -1981,9 +2030,9 @@ llvm::PassPluginLibraryInfo getEDDIPluginInfo() { ArrayRef) { if (Name == "eddi-verify") { #ifdef DUPLICATE_ALL - FPM.addPass(EDDI(true)); + FPM.addPass(EDDI(true, true)); #else - FPM.addPass(EDDI(false)); + FPM.addPass(EDDI(false, true)); #endif return true; } From bfa6cab919b0ca6d431bb98fa36e546a87f191b4 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Thu, 16 Apr 2026 09:04:56 +0200 Subject: [PATCH 05/16] fix(debug_locations): Fixed findNearestDebugLoc Added basic case and more checks to avoid null dereferences --- passes/Utils/Utils.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/passes/Utils/Utils.cpp b/passes/Utils/Utils.cpp index dc64512..667cc8e 100644 --- a/passes/Utils/Utils.cpp +++ b/passes/Utils/Utils.cpp @@ -123,19 +123,31 @@ bool shouldCompile(Function &Fn, } DebugLoc findNearestDebugLoc(Instruction &I) { + // Safety check: ensure the instruction has a valid parent + if (!I.getParent()) { + errs() << "Instruction has no valid parent basic block. Returning null debug location.\n"; + return nullptr; + } + std::list candidates; Instruction *PrevI = &I; + if(I.getDebugLoc()) { + return I.getDebugLoc(); + } + while (PrevI != NULL && (PrevI = PrevI->getPrevNonDebugInstruction())) { if (auto DL = PrevI->getDebugLoc()) { return DL; } } - for (auto *U : I.getParent()->users()) { - if(isa(U)) { - candidates.push_back(cast(U)->getParent()); + if(I.getParent()) { + for (auto *U : I.getParent()->users()) { + if(isa(U)) { + candidates.push_back(cast(U)->getParent()); + } } } From 77aca6471c2371b24403e436029417729d268154 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Fri, 17 Apr 2026 19:33:14 +0200 Subject: [PATCH 06/16] feat(EDDI): Supporting indirect function calls Now indirect function calls are hardened by calling the duplicate version of the function, passing both the original and the duplicate operands. Also, a consistency check on the pointer of the function to be called is added too (in addition to the classic consistency checks of the operands). --- passes/EDDI.cpp | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/passes/EDDI.cpp b/passes/EDDI.cpp index 899d408..bc5518c 100755 --- a/passes/EDDI.cpp +++ b/passes/EDDI.cpp @@ -130,6 +130,11 @@ std::set EDDI::getVirtualMethodsFromConstructor(Function *Fn) { // Get all the virtual methods if(vtable) { // Ensure the vtable global variable has an initializer + if(!vtable->hasInitializer()) { + errs() << "Vtable does not have an initializer.\n"; + return virtualMethods; + } + Constant *Initializer = vtable->getInitializer(); if (!Initializer || !isa(Initializer)) { errs() << "Vtable initializer is not a ConstantStruct.\n"; @@ -661,6 +666,17 @@ void EDDI::duplicateOperands( IClone->setOperand(J, CloneGEPOperand); } } + } else if (isa(V)) { + // if the operand is a function we need to set the duplicate function as + // operand of the clone instruction + Function *FnOperand = cast(V); + auto DuplicateFn = getFunctionDuplicate(FnOperand); + if (DuplicateFn != NULL) { + I.setOperand(J, DuplicateFn); + if (IClone != NULL) { + IClone->setOperand(J, DuplicateFn); + } + } } if (IClone != NULL) { @@ -738,6 +754,11 @@ Value *EDDI::comparePtrs(Value &V1, Value &V2, IRBuilder<> &B) { void EDDI::addConsistencyChecks( Instruction &I, std::map &DuplicatedInstructionMap, BasicBlock &ErrBB) { + + if(InstructionsToRemove.find(&I) != InstructionsToRemove.end()) { + return ; + } + std::vector CmpInstructions; // split and add the verification BB @@ -750,6 +771,19 @@ void EDDI::addConsistencyChecks( BI->setSuccessor(0, VerificationBB); IRBuilder<> B(VerificationBB); + // if the instruction is a call with indirect function, we try to get a compare + if(isa(I) && cast(I).isIndirectCall()) { + auto Duplicate = DuplicatedInstructionMap.find(cast(I).getCalledOperand()); + if (Duplicate != DuplicatedInstructionMap.end()) { + Value *Original = Duplicate->first; + Value *Copy = Duplicate->second; + if (Original->getType()->isIntOrIntVectorTy() || Original->getType()->isPtrOrPtrVectorTy()) { + // DuplicatedInstructionMap.insert(std::pair(&I, &I)); + CmpInstructions.push_back(B.CreateCmp(CmpInst::ICMP_EQ, Original, Copy)); + } + } + } + // add a comparison for each operand for (Value *V : I.operand_values()) { // we compare the operands if they are instructions @@ -1063,6 +1097,9 @@ int EDDI::transformCallBaseInst(CallBase *CInstr, std::map &Du } } + Instruction *NewCInstr = nullptr; + IRBuilder<> CallBuilder(CInstr); + // In case of duplication of an indirect call, call the function with doubled parameters if (Callee == NULL) { // Create the new function type @@ -1070,11 +1107,9 @@ int EDDI::transformCallBaseInst(CallBase *CInstr, std::map &Du FunctionType *FuncType = FunctionType::get(ReturnType, ParamTypes, false); // Create a dummy function pointer (Fn) for the new call - IRBuilder<> CallBuilder(CInstr); Value *Fn = CallBuilder.CreateBitCast(CInstr->getCalledOperand(), FuncType->getPointerTo()); // Create the new call or invoke instruction - Instruction *NewCInstr; if (isa(CInstr)) { InvokeInst *IInst=cast(CInstr); NewCInstr = CallBuilder.CreateInvoke( @@ -1111,8 +1146,6 @@ int EDDI::transformCallBaseInst(CallBase *CInstr, std::map &Du // Remove original instruction since we created the duplicated version res = 1; } else { - Instruction *NewCInstr; - IRBuilder<> CallBuilder(CInstr); if (isa(CInstr)) { InvokeInst *IInst=cast(CInstr); NewCInstr = CallBuilder.CreateInvoke(Fn->getFunctionType(), Fn,IInst->getNormalDest(),IInst->getUnwindDest(), args); @@ -1127,6 +1160,10 @@ int EDDI::transformCallBaseInst(CallBase *CInstr, std::map &Du CInstr->replaceNonMetadataUsesWith(NewCInstr); } + if(NewCInstr) { + DuplicatedCalls.insert(NewCInstr); + } + return res; } From f525b7e812ff7efe58f61ce273064a80cc450b07 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Sun, 19 Apr 2026 21:15:51 +0200 Subject: [PATCH 07/16] fix(EDDI): Handle std::thread class and methods as to exclude from duplication --- passes/EDDI.cpp | 7 ++++++- passes/Utils/Utils.cpp | 14 ++++++++++++++ passes/Utils/Utils.h | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/passes/EDDI.cpp b/passes/EDDI.cpp index bc5518c..3036d19 100755 --- a/passes/EDDI.cpp +++ b/passes/EDDI.cpp @@ -204,7 +204,7 @@ void EDDI::fixDuplicatedConstructors(Module &Md) { } // Duplicate vtable - if(vtable) { + if(vtable && vtable->hasInitializer()) { // Ensure the vtable global variable has an initializer Constant *Initializer = vtable->getInitializer(); if (!Initializer || !isa(Initializer)) { @@ -1264,6 +1264,11 @@ int EDDI::duplicateInstruction( // order to tell the pass to duplicate the function call. Function *Callee = CInstr->getCalledFunction(); Callee = getFunctionFromDuplicate(Callee); + + if(CInstr->getCalledFunction() != NULL && isToExcludeName(CInstr->getCalledFunction()->getName())) { + return 0; + } + // check if the function call has to be duplicated if ((FuncAnnotations.find(Callee) != FuncAnnotations.end() && FuncAnnotations.find(Callee)->second.starts_with("to_duplicate")) || isToDuplicate(CInstr)) { diff --git a/passes/Utils/Utils.cpp b/passes/Utils/Utils.cpp index 667cc8e..6cabb1c 100644 --- a/passes/Utils/Utils.cpp +++ b/passes/Utils/Utils.cpp @@ -240,6 +240,20 @@ bool isToDuplicateName(StringRef FnMangledName) { return false; } + if(FnName.find("std::thread") != FnName.npos) { + return false; + } + + return true; + } + + return false; +} + +bool isToExcludeName(StringRef FnMangledName) { + auto FnName = demangle(FnMangledName.str()); + + if(FnName.find("std::thread") != FnName.npos) { return true; } diff --git a/passes/Utils/Utils.h b/passes/Utils/Utils.h index a381676..cdc7f0b 100644 --- a/passes/Utils/Utils.h +++ b/passes/Utils/Utils.h @@ -46,7 +46,7 @@ void printLinkageMap(const LinkageMap &linkageMap); StringRef getLinkageName(const LinkageMap &linkageMap, const std::string &functionName); bool isToDuplicateName(StringRef FnMangledName); bool isToDuplicate(CallBase *CInstr); -// bool isIntrinsicToDuplicate(CallBase *CInstr); +bool isToExcludeName(StringRef FnMangledName); void createFtFuncs(Module &Md); From 3084f11eececb66e9c85b59de5f35bfd519d2598 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Mon, 27 Apr 2026 19:10:11 +0200 Subject: [PATCH 08/16] test(reddi): add reddi modifications to tests to effectively test REDDI --- testing/test.py | 2 +- testing/tests/c/control_flow/function_pointer.c | 6 ++---- testing/tests/c/control_flow/loop_exit.c | 4 +++- testing/tests/c/control_flow/nested-branch.c | 4 +++- testing/tests/c/control_flow/simple-branch.c | 4 +++- testing/tests/c/control_flow/switch-case.c | 6 +++++- .../tests/c/data_duplication_integrity/data_dep_branches.c | 4 +++- testing/tests/c/data_duplication_integrity/misc_data_dup.c | 3 --- .../c/data_duplication_integrity/printf_to_duplicate.c | 2 +- testing/tests/c/misc_math/arit_pipeline.c | 5 ++++- testing/tests/c/misc_math/mixed_ops.c | 5 ++++- testing/tests/c/multi_instruction/add.c | 4 +++- testing/tests/c/multi_instruction/call_less_two.c | 3 ++- testing/tests/c/multi_instruction/function.c | 2 ++ testing/tests/c/multi_instruction/if_then_else.c | 5 +++-- testing/tests/c/multi_instruction/multi_if_then_else.c | 5 ++++- testing/tests/cpp/simple/add.cpp | 1 + testing/tests/cpp/simple/class.cpp | 4 +++- testing/tests/cpp/simple/exceptions.cpp | 1 + testing/tests/cpp/simple/fact.cpp | 1 + testing/tests/cpp/simple/func.cpp | 1 + testing/tests/cpp/simple/heap.cpp | 6 ++++-- testing/tests/cpp/simple/inline_constexpr.cpp | 1 + testing/tests/cpp/simple/lambda_captures.cpp | 1 + testing/tests/cpp/simple/mul.cpp | 1 + testing/tests/cpp/simple/ptr.cpp | 1 + testing/tests/cpp/simple/stl_containers_advanced.cpp | 4 +++- testing/tests/cpp/simple/template.cpp | 4 +++- testing/tests/cpp/simple/threads.cpp | 1 + testing/tests/cpp/simple/volatile_memory_order.cpp | 2 +- 30 files changed, 66 insertions(+), 27 deletions(-) diff --git a/testing/test.py b/testing/test.py index 283a4f2..4c5759e 100644 --- a/testing/test.py +++ b/testing/test.py @@ -13,7 +13,7 @@ LOCAL_SHARED_VOLUME = "./tests/" DOCKER_COMPOSE_FILE = "../docker/docker-compose.yml" -data_techniques = ["--no-dup", "--eddi", "--seddi", "--fdsc"] +data_techniques = ["--no-dup", "--eddi", "--reddi", "--seddi", "--fdsc"] cfc_techniques = ["--no-cfc", "--cfcss", "--rasm", "--racfed", "--inter-rasm"] # Load the test configuration diff --git a/testing/tests/c/control_flow/function_pointer.c b/testing/tests/c/control_flow/function_pointer.c index f7e8302..769efca 100644 --- a/testing/tests/c/control_flow/function_pointer.c +++ b/testing/tests/c/control_flow/function_pointer.c @@ -9,6 +9,7 @@ int foo() { return 42; } +__attribute__((annotate("to_harden"))) int add(int a, int b) { return a + b; } @@ -24,7 +25,4 @@ int main() { int sum = addptr(27, result); printf("%d\n", sum); return 0; -} - -// expected output -// 42 +} \ No newline at end of file diff --git a/testing/tests/c/control_flow/loop_exit.c b/testing/tests/c/control_flow/loop_exit.c index 55dc4e6..080f5f5 100644 --- a/testing/tests/c/control_flow/loop_exit.c +++ b/testing/tests/c/control_flow/loop_exit.c @@ -4,8 +4,10 @@ #include +__attribute__((annotate("to_harden"))) +int sum = 0; + int main() { - int sum = 0; for (int i = 0; i < 5; i++) { if (i == 1) continue; if (i == 3) break; diff --git a/testing/tests/c/control_flow/nested-branch.c b/testing/tests/c/control_flow/nested-branch.c index ab6414b..6e591cc 100644 --- a/testing/tests/c/control_flow/nested-branch.c +++ b/testing/tests/c/control_flow/nested-branch.c @@ -1,7 +1,9 @@ #include +__attribute__((annotate("to_harden"))) +int sum = 0; + int main() { - int sum = 0; for (int i = 0; i < 3; i++) { int x = i * 2; if (x % 2 == 0) { diff --git a/testing/tests/c/control_flow/simple-branch.c b/testing/tests/c/control_flow/simple-branch.c index f95521d..56135de 100644 --- a/testing/tests/c/control_flow/simple-branch.c +++ b/testing/tests/c/control_flow/simple-branch.c @@ -1,9 +1,11 @@ #include #include +__attribute__((annotate("to_harden"))) +int a = 10; int main() { - int a = 10, b = 20; + int b = 20; if (a < b) { printf("OK"); } else { diff --git a/testing/tests/c/control_flow/switch-case.c b/testing/tests/c/control_flow/switch-case.c index a0b094f..cef33e6 100644 --- a/testing/tests/c/control_flow/switch-case.c +++ b/testing/tests/c/control_flow/switch-case.c @@ -16,8 +16,12 @@ int switch_test(int value) { } } + +__attribute__((annotate("to_harden"))) +int switchN = 3; + int main() { - int result = switch_test(3); + int result = switch_test(switchN); printf("%d", result); return 0; } diff --git a/testing/tests/c/data_duplication_integrity/data_dep_branches.c b/testing/tests/c/data_duplication_integrity/data_dep_branches.c index 1bb0a30..d88f9ac 100644 --- a/testing/tests/c/data_duplication_integrity/data_dep_branches.c +++ b/testing/tests/c/data_duplication_integrity/data_dep_branches.c @@ -4,9 +4,11 @@ #include +__attribute__((annotate("to_harden"))) +int y = 0; + int main() { int x = 5; - int y = 0; if (x > 3) { y = 7; diff --git a/testing/tests/c/data_duplication_integrity/misc_data_dup.c b/testing/tests/c/data_duplication_integrity/misc_data_dup.c index 3a31410..ad5b0ed 100644 --- a/testing/tests/c/data_duplication_integrity/misc_data_dup.c +++ b/testing/tests/c/data_duplication_integrity/misc_data_dup.c @@ -4,9 +4,6 @@ #include -void DataCorruption_Handler(void) {} -void SigMismatch_Handler(void) {} - __attribute__((annotate("to_harden"))) int duplicated_global = 100; diff --git a/testing/tests/c/data_duplication_integrity/printf_to_duplicate.c b/testing/tests/c/data_duplication_integrity/printf_to_duplicate.c index 4b6950d..131e926 100644 --- a/testing/tests/c/data_duplication_integrity/printf_to_duplicate.c +++ b/testing/tests/c/data_duplication_integrity/printf_to_duplicate.c @@ -2,7 +2,7 @@ __attribute__((annotate("to_harden"))) void print_string(char *s) { - printf("The string is: %s", s); + printf("The string is: %s\n", s); } int main() { diff --git a/testing/tests/c/misc_math/arit_pipeline.c b/testing/tests/c/misc_math/arit_pipeline.c index f009449..fa576ee 100644 --- a/testing/tests/c/misc_math/arit_pipeline.c +++ b/testing/tests/c/misc_math/arit_pipeline.c @@ -1,12 +1,15 @@ #include +__attribute__((annotate("to_harden"))) +int mod = 0; + int main() { int a = 5, b = 3; int sum = a + b; int diff = sum - 2; int prod = diff * 4; int quot = prod / 3; - int mod = quot % 5; + mod = quot % 5; printf("%d", mod); // expected result: ((((5+3)-2)*4)/3)%5 = (6*4)/3 = 24/3 = 8 % 5 = 3 return 0; } diff --git a/testing/tests/c/misc_math/mixed_ops.c b/testing/tests/c/misc_math/mixed_ops.c index 87d56b6..62ebcc4 100644 --- a/testing/tests/c/misc_math/mixed_ops.c +++ b/testing/tests/c/misc_math/mixed_ops.c @@ -1,12 +1,15 @@ #include +__attribute__((annotate("to_harden"))) +float res = 0; + int main() { int i = 5; float f = 2.5; char c = 3; long l = 4; - float res = i + f + c + l; // 5 + 2.5 + 3 + 4 = 14.5 + res = i + f + c + l; // 5 + 2.5 + 3 + 4 = 14.5 printf("%.1f", res); return 0; } diff --git a/testing/tests/c/multi_instruction/add.c b/testing/tests/c/multi_instruction/add.c index 5e50264..2379ea4 100644 --- a/testing/tests/c/multi_instruction/add.c +++ b/testing/tests/c/multi_instruction/add.c @@ -3,8 +3,10 @@ // #include +__attribute__((annotate("to_harden"))) +int a = 10; + int main() { - int a = 10; int b = 20; int c = a + b; printf("%d\n", c); diff --git a/testing/tests/c/multi_instruction/call_less_two.c b/testing/tests/c/multi_instruction/call_less_two.c index f599d37..8b4475c 100644 --- a/testing/tests/c/multi_instruction/call_less_two.c +++ b/testing/tests/c/multi_instruction/call_less_two.c @@ -1,5 +1,6 @@ #include +__attribute__((annotate("to_harden"))) int foo() { return 0; } @@ -8,7 +9,7 @@ int main() { int sasso_carta = 1; int filippo_congenito = 2; for(int i = foo(); i < sasso_carta + filippo_congenito; i++) - filippo_congenito--; + filippo_congenito--; printf("%d", filippo_congenito); return 0; } diff --git a/testing/tests/c/multi_instruction/function.c b/testing/tests/c/multi_instruction/function.c index 8cc2f9f..3442356 100644 --- a/testing/tests/c/multi_instruction/function.c +++ b/testing/tests/c/multi_instruction/function.c @@ -3,6 +3,7 @@ // #include +__attribute__((annotate("to_harden"))) int foo(); void print(int c); @@ -14,6 +15,7 @@ int main() { return a > b ? 1 : 0; } +__attribute__((annotate("to_harden"))) int foo() { int c = 12; int d = 13; diff --git a/testing/tests/c/multi_instruction/if_then_else.c b/testing/tests/c/multi_instruction/if_then_else.c index 0a9ab1d..8d32ab5 100644 --- a/testing/tests/c/multi_instruction/if_then_else.c +++ b/testing/tests/c/multi_instruction/if_then_else.c @@ -3,10 +3,11 @@ // #include +__attribute__((annotate("to_harden"))) +int x = 1000; int main() { - int x = 1000; - x = x+1; + x = x+1; if (x<10) { x = x*10; printf("%d\n", x); diff --git a/testing/tests/c/multi_instruction/multi_if_then_else.c b/testing/tests/c/multi_instruction/multi_if_then_else.c index 038f068..dd3d91f 100644 --- a/testing/tests/c/multi_instruction/multi_if_then_else.c +++ b/testing/tests/c/multi_instruction/multi_if_then_else.c @@ -4,9 +4,12 @@ #define MAX 1024 +__attribute__((annotate("to_harden"))) +int r = 0; + int main() { srand(time(NULL)); - int r = rand() % MAX + 200; + r = rand() % MAX + 200; if ( r > 200 ) printf("r > 200\n"); else if ( r > 100 ) printf("100 < r < 200\n"); else if ( r > 50 ) printf("50 < r < 100\n"); diff --git a/testing/tests/cpp/simple/add.cpp b/testing/tests/cpp/simple/add.cpp index 9512744..944ecc9 100644 --- a/testing/tests/cpp/simple/add.cpp +++ b/testing/tests/cpp/simple/add.cpp @@ -13,6 +13,7 @@ extern "C" { } } +__attribute__((annotate("to_harden"))) int add(int a, int b) { return a + b; } diff --git a/testing/tests/cpp/simple/class.cpp b/testing/tests/cpp/simple/class.cpp index 6352bde..35b030c 100644 --- a/testing/tests/cpp/simple/class.cpp +++ b/testing/tests/cpp/simple/class.cpp @@ -50,6 +50,9 @@ class DerivedClass : public MyClass int c; }; +__attribute__((annotate("to_harden"))) +DerivedClass derivedObj(3, 6, 9); + int main() { // Test class and member function @@ -58,6 +61,5 @@ int main() myObj.print(); // Test derived class with overridden virtual function - DerivedClass derivedObj(3, 6, 9); derivedObj.print(); } diff --git a/testing/tests/cpp/simple/exceptions.cpp b/testing/tests/cpp/simple/exceptions.cpp index d773435..7aafc93 100644 --- a/testing/tests/cpp/simple/exceptions.cpp +++ b/testing/tests/cpp/simple/exceptions.cpp @@ -17,6 +17,7 @@ extern "C" { } // A leaf that always throws +__attribute__((annotate("to_harden"))) int nested_thrower() { throw std::runtime_error("Test exception"); } diff --git a/testing/tests/cpp/simple/fact.cpp b/testing/tests/cpp/simple/fact.cpp index bb46800..8757012 100644 --- a/testing/tests/cpp/simple/fact.cpp +++ b/testing/tests/cpp/simple/fact.cpp @@ -13,6 +13,7 @@ void SigMismatch_Handler() { } // compute n! recursively +__attribute__((annotate("to_harden"))) unsigned long long fact(unsigned int n) { return n <= 1 ? 1 : n * fact(n - 1); diff --git a/testing/tests/cpp/simple/func.cpp b/testing/tests/cpp/simple/func.cpp index a61b2c2..9842455 100644 --- a/testing/tests/cpp/simple/func.cpp +++ b/testing/tests/cpp/simple/func.cpp @@ -24,6 +24,7 @@ void fNonDup() { std::cout << "Function fNonDup called (g incremented to " << g << ")" << std::endl; } +__attribute__((annotate("to_harden"))) void f() { g++; std::cout << "Function f called (g incremented to " << g << ")" << std::endl; diff --git a/testing/tests/cpp/simple/heap.cpp b/testing/tests/cpp/simple/heap.cpp index 2786d73..e31a1c3 100644 --- a/testing/tests/cpp/simple/heap.cpp +++ b/testing/tests/cpp/simple/heap.cpp @@ -14,10 +14,12 @@ extern "C" { } } +// Allocate a single integer on the heap +__attribute__((annotate("to_harden"))) +int *p = new int(5); + // A function that allocates memory, uses it, and then deletes it int main() { - // Allocate a single integer on the heap - int *p = new int(5); *p = 10; // modify the allocated value int result = *p; delete p; diff --git a/testing/tests/cpp/simple/inline_constexpr.cpp b/testing/tests/cpp/simple/inline_constexpr.cpp index 900fb3f..58e39de 100644 --- a/testing/tests/cpp/simple/inline_constexpr.cpp +++ b/testing/tests/cpp/simple/inline_constexpr.cpp @@ -14,6 +14,7 @@ extern "C" { } } +__attribute__((annotate("to_harden"))) void printResult(int value) { std::cout << value << std::endl; } diff --git a/testing/tests/cpp/simple/lambda_captures.cpp b/testing/tests/cpp/simple/lambda_captures.cpp index 6c13f07..97bb02d 100644 --- a/testing/tests/cpp/simple/lambda_captures.cpp +++ b/testing/tests/cpp/simple/lambda_captures.cpp @@ -22,6 +22,7 @@ void runNoDup(F func) { } template +__attribute__((annotate("to_harden"))) void run(F func) { func(); } diff --git a/testing/tests/cpp/simple/mul.cpp b/testing/tests/cpp/simple/mul.cpp index 85c8cb5..40939f4 100644 --- a/testing/tests/cpp/simple/mul.cpp +++ b/testing/tests/cpp/simple/mul.cpp @@ -14,6 +14,7 @@ extern "C" { } } +__attribute__((annotate("to_harden"))) int multiply(int a, int b) { return a * b; diff --git a/testing/tests/cpp/simple/ptr.cpp b/testing/tests/cpp/simple/ptr.cpp index 08436a0..f59b8f4 100644 --- a/testing/tests/cpp/simple/ptr.cpp +++ b/testing/tests/cpp/simple/ptr.cpp @@ -16,6 +16,7 @@ extern "C" { } // Helper to print two pointer values (non-duplicated) +__attribute__((annotate("to_harden"))) void printPointers(int *p1, int *p2) { std::cout << "Value pointed by p1: " << *p1; if (p2) { diff --git a/testing/tests/cpp/simple/stl_containers_advanced.cpp b/testing/tests/cpp/simple/stl_containers_advanced.cpp index b19903d..19adf40 100644 --- a/testing/tests/cpp/simple/stl_containers_advanced.cpp +++ b/testing/tests/cpp/simple/stl_containers_advanced.cpp @@ -15,9 +15,11 @@ extern "C" { } } +__attribute__((annotate("to_harden"))) +std::vector ptrVector; + int main() { // Example of advanced container usage with dynamic memory - std::vector ptrVector; ptrVector.reserve(5); // reserve capacity for efficiency // Allocate integers on the heap and store pointers in the vector diff --git a/testing/tests/cpp/simple/template.cpp b/testing/tests/cpp/simple/template.cpp index 1b060b9..748bf42 100644 --- a/testing/tests/cpp/simple/template.cpp +++ b/testing/tests/cpp/simple/template.cpp @@ -41,11 +41,13 @@ class Accumulator { T sum; }; +__attribute__((annotate("to_harden"))) +Accumulator acc; + int main() { int x = 42, y = 17; int maxVal = myMax(x, y); // 42 - Accumulator acc; acc.add(5); acc.add(10); int sumVal = acc.total(); // 15 diff --git a/testing/tests/cpp/simple/threads.cpp b/testing/tests/cpp/simple/threads.cpp index 5bc27ae..a6ea19c 100644 --- a/testing/tests/cpp/simple/threads.cpp +++ b/testing/tests/cpp/simple/threads.cpp @@ -25,6 +25,7 @@ void SigMismatch_Handler() { std::exit(EXIT_FAILURE); } +__attribute__((annotate("to_harden"))) std::atomic counter{0}; void worker(int times) { diff --git a/testing/tests/cpp/simple/volatile_memory_order.cpp b/testing/tests/cpp/simple/volatile_memory_order.cpp index de9a851..51b4b2c 100644 --- a/testing/tests/cpp/simple/volatile_memory_order.cpp +++ b/testing/tests/cpp/simple/volatile_memory_order.cpp @@ -14,7 +14,7 @@ extern "C" { } } - +__attribute__((annotate("to_harden"))) volatile int flag = 0; void writer() From f4e87db1576bdb2a6768df978bcc7a1644f26ad3 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Mon, 27 Apr 2026 19:43:58 +0200 Subject: [PATCH 09/16] fix(DuplicateGlobals): Handling anonymous globals and fixing duplicateCall Co-authored-by: osmandagli --- passes/DuplicateGlobals.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/passes/DuplicateGlobals.cpp b/passes/DuplicateGlobals.cpp index a66c89e..6bc2056 100755 --- a/passes/DuplicateGlobals.cpp +++ b/passes/DuplicateGlobals.cpp @@ -76,9 +76,17 @@ void DuplicateGlobals::duplicateCall(Module &Md, CallBase* UCall, Value* Origina for (auto &Op : UCall->args()) { if (Op == Original) { if (AlternateMemMapEnabled == false) { - UCall->setOperand(i + UCall->arg_size()/2, Copy); + if(i < UCall->arg_size()/2) { + UCall->setOperand(i + UCall->arg_size()/2, Copy); + } else { + UCall->setOperand(i - UCall->arg_size()/2, Copy); + } } else { - UCall->setOperand(i+1, Copy); + if(i%2 == 0) { + UCall->setOperand(i+1, Copy); + } else { + UCall->setOperand(i-1, Copy); + } } } i++; @@ -163,6 +171,10 @@ PreservedAnalyses DuplicateGlobals::run(Module &Md, ModuleAnalysisManager &AM) { } for (GlobalVariable *GV : Globals) { + if (GV->getName().empty()) { + GV->setName("global_" + std::to_string(rand())); + } + // we don't care if the global is constant as it should not change at runtime // if the global is a struct or an array we cannot just duplicate the stores bool toDuplicate = !isa(GV) && From cf5d07d6203ea0589dcdc8dab559b0a8f3c0f095 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Mon, 27 Apr 2026 20:44:15 +0200 Subject: [PATCH 10/16] fix(EDDI): handling duplication of constant global variables with TAD --- passes/ASPIS.h | 1 + passes/EDDI.cpp | 86 ++++++++++++++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 33 deletions(-) diff --git a/passes/ASPIS.h b/passes/ASPIS.h index 6626f28..1051767 100644 --- a/passes/ASPIS.h +++ b/passes/ASPIS.h @@ -67,6 +67,7 @@ class EDDI : public PassInfoMixin { bool isValueDuplicated(std::map &DuplicatedInstructionMap, Instruction &V); Function *duplicateFnArgs(Function &Fn, Module &Md, std::map &DuplicatedInstructionMap); void CreateErrBB(Module &Md, Function &Fn, BasicBlock *ErrBB); + bool temporaryArgumentDuplication(Module &Md, llvm::Value *value, IRBuilder<> &B, std::map &InstructionMap); void fixGlobalCtors(Module &M); public: diff --git a/passes/EDDI.cpp b/passes/EDDI.cpp index 3036d19..fb0419d 100755 --- a/passes/EDDI.cpp +++ b/passes/EDDI.cpp @@ -618,6 +618,9 @@ void EDDI::duplicateOperands( Instruction &I, std::map &DuplicatedInstructionMap, BasicBlock &ErrBB) { Instruction *IClone = NULL; + + std::map tmpDuplicatedInstructionMap{DuplicatedInstructionMap}; + // see if I has a clone if (DuplicatedInstructionMap.find(&I) != DuplicatedInstructionMap.end()) { Value *VClone = DuplicatedInstructionMap.find(&I)->second; @@ -677,15 +680,22 @@ void EDDI::duplicateOperands( IClone->setOperand(J, DuplicateFn); } } + } else if (isa(I) && isa(V) && cast(V)->isConstant()) { + IRBuilder<> B(&I); + temporaryArgumentDuplication(*I.getModule(), V, B, tmpDuplicatedInstructionMap); } if (IClone != NULL) { // use the duplicated instruction as operand of IClone - auto Duplicate = DuplicatedInstructionMap.find(V); - if (Duplicate != DuplicatedInstructionMap.end()) - IClone->setOperand( - J, - Duplicate->second); // set the J-th operand with the duplicate value + auto Duplicate = tmpDuplicatedInstructionMap.find(V); + if (Duplicate != tmpDuplicatedInstructionMap.end()) { + IClone->setOperand(J, Duplicate->second); // set the J-th operand with the duplicate value + } else { + Duplicate = DuplicatedInstructionMap.find(V); + if (Duplicate != DuplicatedInstructionMap.end()) { + IClone->setOperand(J, Duplicate->second); + } + } } J++; } @@ -870,6 +880,8 @@ void EDDI::addConsistencyChecks( if (DebugEnabled) { CondBrInst->setDebugLoc(I.getDebugLoc()); } + } else { + errs() << "Warning: no consistency check added for instruction: " << I << "\n"; } if (VerificationBB->size() == 0) { @@ -1765,33 +1777,8 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { // Create alloca and memcpy only if ptr since if it is a value, we can just pass two times the same value if(Arg->getType()->isPointerTy() && !CInstr->isByValArgument(i) && isa(Arg) && !isa(Arg)) { - const llvm::DataLayout &DL = Md.getDataLayout(); - Type *ArgType; - - Align ArgAlign; - ArgType = getValueType(Arg, &ArgAlign); - - // If can't find type, do not duplicate argument - if(ArgType->isVoidTy()) { - continue; - } - - uint64_t SizeInBytes = DL.getTypeAllocSize(ArgType); - Value *Size = llvm::ConstantInt::get(B.getInt64Ty(), SizeInBytes); - - // Alignment (assuming alignment of 1 here; adjust as necessary) - llvm::ConstantInt *Align = B.getInt32(ArgAlign.value()); - - // Volatility (non-volatile in this example) - llvm::ConstantInt *IsVolatile = B.getInt1(false); - - // Create the memcpy call - auto CopyArg = B.CreateAlloca(ArgType); - - llvm::CallInst *memcpy_call = B.CreateMemCpy(CopyArg, Arg->getPointerAlignment(DL), Arg, Arg->getPointerAlignment(DL), Size); - - TmpDuplicatedInstructionMap.insert(std::pair(CopyArg, Arg)); - TmpDuplicatedInstructionMap.insert(std::pair(Arg, CopyArg)); + // If cannot perform TAD, do not duplicate Arg + temporaryArgumentDuplication(Md, Arg, B, TmpDuplicatedInstructionMap); } else { // Otherwise pass two times the same arg TmpDuplicatedInstructionMap.insert(std::pair(Arg, Arg)); @@ -1868,6 +1855,40 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { return PreservedAnalyses::none(); } +bool EDDI::temporaryArgumentDuplication(Module &Md, llvm::Value *value, IRBuilder<> &B, std::map &InstructionMap) { + errs() << "Performing temporary argument duplication for value: " << *value << " in function: " << B.GetInsertBlock()->getParent()->getName() << "\n"; + const llvm::DataLayout &DL = Md.getDataLayout(); + Type *valueType; + + Align valueAlign; + valueType = getValueType(value, &valueAlign); + + // If can't find type, do not duplicate value + if(valueType->isVoidTy()) { + errs() << "Error: Cannot find type of value: " << *value << "\n"; + return false; + } + + uint64_t SizeInBytes = DL.getTypeAllocSize(valueType); + Value *Size = llvm::ConstantInt::get(B.getInt64Ty(), SizeInBytes); + + // Alignment (assuming alignment of 1 here; adjust as necessary) + llvm::ConstantInt *Align = B.getInt32(valueAlign.value()); + + // Volatility (non-volatile in this example) + llvm::ConstantInt *IsVolatile = B.getInt1(false); + + // Create the memcpy call + auto Copyvalue = B.CreateAlloca(valueType); + + llvm::CallInst *memcpy_call = B.CreateMemCpy(Copyvalue, value->getPointerAlignment(DL), value, value->getPointerAlignment(DL), Size); + + InstructionMap.insert(std::pair(Copyvalue, value)); + InstructionMap.insert(std::pair(value, Copyvalue)); + + return true; +} + Instruction *getSingleReturnInst(Function &F) { for (BasicBlock &BB : F) { if (auto *retInst = llvm::dyn_cast(BB.getTerminator())) { @@ -1990,7 +2011,6 @@ void EDDI::fixGlobalCtors(Module &M) { // Retrieve the existing @llvm.global_ctors. GlobalVariable *GlobalCtors = M.getGlobalVariable("llvm.global_ctors"); if (!GlobalCtors) { - errs() << "Error: @llvm.global_ctors not found in the module.\n"; return; } From 1a23bcf641b94f4c7d7d59a077d759e51ef6948b Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Mon, 27 Apr 2026 20:47:01 +0200 Subject: [PATCH 11/16] fix(EDDI): change name to unnamed global variables Avoid aliasing of anonymous GVs when they are duplicated Co-authored-by: osmandagli --- passes/EDDI.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/passes/EDDI.cpp b/passes/EDDI.cpp index fb0419d..cf0a450 100755 --- a/passes/EDDI.cpp +++ b/passes/EDDI.cpp @@ -974,6 +974,11 @@ void EDDI::duplicateGlobals( } for (auto GV : GVars) { auto GVAnnotation = FuncAnnotations.find(GV); + + if (GV->getName().empty()) { + GV->setName("global_" + std::to_string(rand())); + } + if (!isa(GV) && GVAnnotation != FuncAnnotations.end()) { // What does these annotations do? From a1987cb4e597de46f0c4f18b66bcdbca3c2a4ac1 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Mon, 27 Apr 2026 20:47:59 +0200 Subject: [PATCH 12/16] tests(EDDI): removing blacklisting of duplication methods --- testing/config/tests.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/testing/config/tests.toml b/testing/config/tests.toml index 937595f..329c59e 100644 --- a/testing/config/tests.toml +++ b/testing/config/tests.toml @@ -42,7 +42,6 @@ source_file = "c/data_duplication_integrity/misc_data_dup.c" [[tests]] test_name = "c_printf_to_duplicate" source_file = "c/data_duplication_integrity/printf_to_duplicate.c" -black_list = ["--eddi", "--seddi", "--fdsc"] [[tests]] test_name = "c_volatile_io" @@ -63,7 +62,6 @@ source_file = "c/declared_signatures/mixed_ops.c" [[tests]] test_name = "c_xor_cypher" source_file = "c/misc_math/xor_cypher.c" -black_list = ["--eddi", "--seddi", "--fdsc"] [[tests]] test_name = "c_add" @@ -101,6 +99,7 @@ source_file = "cpp/simple/add.cpp" [[tests]] test_name = "cpp_class" source_file = "cpp/simple/class.cpp" +black_list = ["--inter-rasm"] [[tests]] test_name = "cpp_class_var-decl-sign" @@ -127,6 +126,7 @@ source_file = "cpp/simple/func.cpp" [[tests]] test_name = "cpp_heap" source_file = "cpp/simple/heap.cpp" +black_list = ["--inter-rasm"] [[tests]] test_name = "cpp_inline_constexpr" @@ -157,11 +157,12 @@ black_list = ["--inter-rasm", "--racfed"] [[tests]] test_name = "cpp_template" source_file = "cpp/simple/template.cpp" +black_list = ["--inter-rasm"] [[tests]] test_name = "cpp_threads" source_file = "cpp/simple/threads.cpp" -black_list = ["--inter-rasm", "--racfed", "--eddi", "--seddi", "--fdsc"] +black_list = ["--inter-rasm", "--racfed"] [[tests]] test_name = "cpp_volatile_memory_order" From 62e6aa6c53f0fbc57aa73e0913dd71054e848e63 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Wed, 29 Apr 2026 12:38:51 +0200 Subject: [PATCH 13/16] fix(EDDI): not duplicating extern global variables Duplicating an extern global variable would lead to a linking problem since no external library would provide for its duplicate version --- passes/EDDI.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/passes/EDDI.cpp b/passes/EDDI.cpp index cf0a450..11d0fa2 100755 --- a/passes/EDDI.cpp +++ b/passes/EDDI.cpp @@ -1003,6 +1003,7 @@ void EDDI::duplicateGlobals( bool isArray = GV->getValueType()->isArrayTy(); bool isPointer = GV->getValueType()->isPointerTy(); bool ends_withDup = GV->getName().ends_with("_dup"); + bool isExtern = GV->hasExternalLinkage() && GV->isDeclaration(); bool hasInternalLinkage = GV->hasInternalLinkage(); bool isMetadataInfo = GV->getSection() == "llvm.metadata"; bool isReservedName = GV->getName().starts_with("llvm."); @@ -1010,7 +1011,7 @@ void EDDI::duplicateGlobals( GVAnnotation != FuncAnnotations.end() && GVAnnotation->second.starts_with("exclude"); - if (! (isFunction || isConstant || ends_withDup || isMetadataInfo || isReservedName || toExclude) // is not function, constant, struct and does not end with _dup + if (! (isFunction || isConstant || isExtern || ends_withDup || isMetadataInfo || isReservedName || toExclude) // is not function, constant, struct and does not end with _dup /* && ((hasInternalLinkage && (!isArray || (isArray && !cast(GV.getValueType())->getArrayElementType()->isAggregateType() ))) // has internal linkage and is not an array, or is an array but the element type is not aggregate || !isArray) */ // if it does not have internal linkage, it is not an array or a pointer ) { From 83f3c8ce3d58c6ed9b1d2ab9d69c00354fac163f Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Wed, 29 Apr 2026 12:45:01 +0200 Subject: [PATCH 14/16] fix(CFCSS): restored check for predecessors when creating CFGVerificationBB --- passes/CFCSS.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/passes/CFCSS.cpp b/passes/CFCSS.cpp index 1ddca73..a95ed22 100755 --- a/passes/CFCSS.cpp +++ b/passes/CFCSS.cpp @@ -193,10 +193,11 @@ void CFCSS::createCFGVerificationBB (BasicBlock &BB, // if the block has a predecessor we use its signature, otherwise the sig = 0 if (BBSigs.find(Predecessor) != BBSigs.end()) { - // PredSig = getNeighborSig(*Predecessor, BBSigs); - // if(PredSig == -1) { + if(hasNPredecessorsOrMore(BB, 2, BBSigs)) { + PredSig = getNeighborSig(*Predecessor, BBSigs); + } else { PredSig = BBSigs.find(Predecessor)->second; - // } + } } if(PredSig == CurSig) { From d481693a5a6f5f17e4f22e5d825285960b2f6d08 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Wed, 29 Apr 2026 15:06:16 +0200 Subject: [PATCH 15/16] fix(EDDI): enhanced prints during EDDI pass --- passes/EDDI.cpp | 73 +++++++++++++++++-------------------------------- 1 file changed, 25 insertions(+), 48 deletions(-) diff --git a/passes/EDDI.cpp b/passes/EDDI.cpp index 11d0fa2..49d7920 100755 --- a/passes/EDDI.cpp +++ b/passes/EDDI.cpp @@ -70,14 +70,12 @@ std::set toFixInvokes; GlobalVariable* isVTableStore(StoreInst &SInst) { if(isa(SInst.getValueOperand())) { // TODO: Should see the uses of the valueOperand to find this inst in case it happens - errs() << "this is a GEP instruction\n"; auto *V = cast(SInst.getValueOperand())->getOperand(0); if(isa(V)) { auto *GV = cast(V); auto vtableName = demangle(GV->getName().str()); // Found "vtable" in name if(vtableName.find("vtable") != vtableName.npos) { - // LLVM_DEBUG(dbgs() << "[REDDI] GEP Vtable name: " << vtableName << " of function " << Fn->getName() << "\n"); return GV; } } @@ -106,7 +104,7 @@ std::set EDDI::getVirtualMethodsFromConstructor(Function *Fn) { std::set virtualMethods; if(!Fn) { - errs() << "Fn is not a valid function.\n"; + errs() << "Error: Fn is not a valid function.\n"; return virtualMethods; } @@ -131,13 +129,13 @@ std::set EDDI::getVirtualMethodsFromConstructor(Function *Fn) { if(vtable) { // Ensure the vtable global variable has an initializer if(!vtable->hasInitializer()) { - errs() << "Vtable does not have an initializer.\n"; + errs() << "Error: Vtable does not have an initializer.\n"; return virtualMethods; } Constant *Initializer = vtable->getInitializer(); if (!Initializer || !isa(Initializer)) { - errs() << "Vtable initializer is not a ConstantStruct.\n"; + errs() << "Error: Vtable initializer is not a ConstantStruct.\n"; return virtualMethods; } @@ -147,7 +145,7 @@ std::set EDDI::getVirtualMethodsFromConstructor(Function *Fn) { for(int i = 0; i < VTableStruct->getNumOperands(); i++) { Constant *ArrayField = VTableStruct->getOperand(i); if (!isa(ArrayField)) { - errs() << "Vtable field " << i << " is not a ConstantArray.\n"; + errs() << "Error: Vtable field " << i << " is not a ConstantArray.\n"; continue; } @@ -155,7 +153,6 @@ std::set EDDI::getVirtualMethodsFromConstructor(Function *Fn) { for (Value *Elem : cast(ArrayField)->operands()) { if (isa(Elem)) { virtualMethods.insert(cast(Elem)); - // LLVM_DEBUG(dbgs() << "[REDDI] Found virtual method " << cast(Elem)->getName() << " in " << Fn->getName() << "\n"); } } } @@ -182,7 +179,7 @@ void EDDI::fixDuplicatedConstructors(Module &Md) { Function *FnDup = getFunctionDuplicate(Fn); if(!FnDup) { - errs() << "Doesn't exist the dup version of " << Fn->getName() << "\n"; + errs() << "Error: Doesn't exist the dup version of " << Fn->getName() << "\n"; continue; } @@ -208,7 +205,7 @@ void EDDI::fixDuplicatedConstructors(Module &Md) { // Ensure the vtable global variable has an initializer Constant *Initializer = vtable->getInitializer(); if (!Initializer || !isa(Initializer)) { - errs() << "Vtable initializer is not a ConstantStruct.\n"; + errs() << "Error: Vtable initializer is not a ConstantStruct.\n"; return; } @@ -220,7 +217,7 @@ void EDDI::fixDuplicatedConstructors(Module &Md) { for(int i = 0; i < VTableStruct->getNumOperands(); i++) { Constant *ArrayField = VTableStruct->getOperand(i); if (!isa(ArrayField)) { - errs() << "Vtable field " << i << " is not a ConstantArray.\n"; + errs() << "Error: Vtable field " << i << " is not a ConstantArray.\n"; continue; } @@ -239,7 +236,7 @@ void EDDI::fixDuplicatedConstructors(Module &Md) { // LLVM_DEBUG(dbgs() << "Getting _dup function: " << DupFunction->getName() << "\n"); ModifiedElements.push_back(DupFunction); } else { - errs() << "Missing _dup function for: " << Func->getName() << "\n"; + errs() << "Error: Missing _dup function for: " << Func->getName() << "\n"; ModifiedElements.push_back(cast(Elem)); // Keep the original } } else { @@ -279,7 +276,7 @@ void EDDI::fixDuplicatedConstructors(Module &Md) { if(isVTableStore(SInst)) { if(isa(SInst.getValueOperand())) { // TODO: Should see the uses of the valueOperand to find this inst in case it happens - errs() << "this is a GEP instruction\n"; + errs() << "Error: GEP instruction not handled\n"; } else if(isa(SInst.getValueOperand())) { auto *CE = cast(SInst.getValueOperand()); if (CE->getOpcode() == Instruction::GetElementPtr) { @@ -328,7 +325,7 @@ void EDDI::fixDuplicatedConstructors(Module &Md) { */ void EDDI::preprocess(Module &Md) { // Replace all uses of alias to aliasee - LLVM_DEBUG(dbgs() << "[REDDI] Replacing aliases\n"); + LLVM_DEBUG(dbgs() << "Replacing aliases\n"); for (auto &alias : Md.aliases()) { auto aliasee = alias.getAliaseeObject(); if(isa(aliasee)){ @@ -389,14 +386,14 @@ void EDDI::preprocess(Module &Md) { if(isa(V) && cast(V)->hasInitializer() && toHardenVariables.find(V) == toHardenVariables.end()) { toHardenVariables.insert(V); - outs() << "Inserting GV from explicit toHarden: " << *V << "\n"; + LLVM_DEBUG(dbgs() << "Inserting GV from explicit toHarden: " << *V << "\n"); } else if(isa(V)) { for(auto &U : cast(V)->operands()) { // if(isa(U) && U->hasName() && !isToDuplicateName(U->getName())) { if(isa(U) && cast(U)->hasInitializer() && toHardenVariables.find(U) == toHardenVariables.end()) { toHardenVariables.insert(U); - outs() << "Inserting GV from explicit toHarden from GEPOp: " << *U << "\n"; + LLVM_DEBUG(dbgs() << "Inserting GV from explicit toHarden from GEPOp: " << *U << "\n"); } } } @@ -413,7 +410,6 @@ void EDDI::preprocess(Module &Md) { while(!toCheckVariables.empty()){ std::set toAddVariables; // support set to contain new to-be-checked values for(Value *V : toCheckVariables) { - // outs() << "Instruction to check: " << *V << "\n"; // Just protect the return value of the call, not the operands if((isa(V) || isa(V)) && !isa(V)) { auto Instr = cast(V); @@ -426,10 +422,8 @@ void EDDI::preprocess(Module &Md) { if(isa(Instr)) { auto PhiInst = cast(Instr); operand = PhiInst->getIncomingValue(i); - // outs() << "phi operand: " << *operand << "\n"; } else if(isa(Instr->getOperand(i)) || isa(Instr->getOperand(i)) || isa(Instr->getOperand(i))) { operand = Instr->getOperand(i); - // outs() << "operand: " << *operand << "\n"; } // Check if to add operand to toAddVariables @@ -440,21 +434,18 @@ void EDDI::preprocess(Module &Md) { (!operand->hasName() || !isToDuplicateName(operand->getName())) && (!isa(operand) || !isAllocaForExceptionHandling(*cast(operand)))) { toAddVariables.insert(operand); - // outs() << "* To be hardened operand: " << *operand << "\n"; } } } for(User *U : V->users()) { if(isa(U) || isa(U)) { - // if(isa(U) || isa(U) || isa(U) || isa(U) || isa(U) || isa(U) || isa(U)) { if(U != NULL && U != V && toHardenVariables.find(U) == toHardenVariables.end() && toCheckVariables.find(U) == toCheckVariables.end() && (FuncAnnotations.find(U) == FuncAnnotations.end() || !FuncAnnotations.find(U)->second.starts_with("exclude")) && (!U->hasName() || !isToDuplicateName(U->getName())) && (!isa(U) || !isAllocaForExceptionHandling(*cast(U)))) { - // outs() << "* To be hardened user: " << *U << "\n"; // If it is a call, add also the called function in the toHardenFunction set if(isa(U)) { CallBase *CallI = cast(U); @@ -466,9 +457,6 @@ void EDDI::preprocess(Module &Md) { // If it isn't/hasn't a duplicate version already toHardenFunctions.insert(Fn); toAddVariables.insert(U); - } else { - errs() << "[REDDI] Indirect Function to harden (called by " << V->getName() << ")\n"; - // continue; } } else { toAddVariables.insert(U); @@ -847,7 +835,7 @@ void EDDI::addConsistencyChecks( CmpInstructions.push_back( B.CreateCmp(CmpInst::ICMP_EQ, OriginalElem, CopyElem)); } else { - errs() << "Didn't create a comparison for "; + errs() << "Warning: Didn't create a comparison for "; OriginalElem->getType()->print(errs()); errs() << " type\n"; } @@ -864,7 +852,7 @@ void EDDI::addConsistencyChecks( CmpInstructions.push_back( B.CreateCmp(CmpInst::ICMP_EQ, Original, Copy)); } else { - errs() << "Didn't create a comparison for " << Original->getType() << " type\n"; + errs() << "Warning: Didn't create a comparison for " << Original->getType() << " type\n"; } } } @@ -1076,7 +1064,7 @@ int EDDI::transformCallBaseInst(CallBase *CInstr, std::map &Du Function *Fn = getFunctionDuplicate(Callee); if(Callee != NULL && (Fn == NULL || Fn == Callee)) { - errs() << "Doesn't exist or already duplicated function: " << *CInstr << "\n"; + errs() << "Error: Doesn't exist or already duplicated function: " << *CInstr << "\n"; return 0; } @@ -1339,7 +1327,7 @@ int EDDI::duplicateInstruction( B.SetInsertPoint( &*cast(CInstr)->getNormalDest()->getFirstInsertionPt()); } else { - errs() << "Can't set insert point! " << I << "\n"; + errs() << "Error: Can't set insert point! " << I << "\n"; abort(); } // get the function with the duplicated signature, if it exists @@ -1440,7 +1428,7 @@ Type *getValueType(Value *Arg, Align *ArgAlign) { Type *ElementType = Type::getInt8Ty(Ctx); // Byte type return ArrayType::get(ElementType, cast(Size)->getZExtValue()); } - errs() << "Call not supported" << *Arg << "\n"; + errs() << "Error: Call not supported" << *Arg << "\n"; return Type::getVoidTy(Arg->getContext()); } else if(isa(Arg)) { Type *ArgType = cast(Arg)->getValueType(); @@ -1450,14 +1438,13 @@ Type *getValueType(Value *Arg, Align *ArgAlign) { if (isa(ArgUsers) && cast(ArgUsers)->getPointerOperand() == Arg) { Arg = cast(ArgUsers)->getValueOperand(); *ArgAlign = cast(ArgUsers)->getAlign(); - errs() << "Store found: " << *ArgUsers << " with align " << ArgAlign->value() << "\n"; foundNewValue = true; break; } } if(!foundNewValue) { - errs() << "Global Type not supported" << *Arg << "\n"; + errs() << "Error: Global Type not supported" << *Arg << "\n"; return Type::getVoidTy(Arg->getContext()); } } else { @@ -1480,7 +1467,7 @@ Type *getValueType(Value *Arg, Align *ArgAlign) { *ArgAlign = cast(Arg)->getAlign(); Arg = cast(Arg)->getValueOperand(); } else { - errs() << "Type not supported" << *Arg << "\n"; + errs() << "Error: Type not supported" << *Arg << "\n"; return Type::getVoidTy(Arg->getContext()); } } @@ -1617,9 +1604,6 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { if (duplicateInstruction(*I, DuplicatedInstructionMap, *ErrBB)) { if(InstructionsToRemove.find(I) == InstructionsToRemove.end()) { InstructionsToRemove.insert(I); - errs() << "Remove instr ( " << *I << " ) from " << *I->getParent()->getParent() << " while duplicating fn args\n"; - } else { - errs() << "Duplicated to remove instr ( " << *I << " ) from " << *I->getParent()->getParent() << " while duplicating fn args\n"; } } } @@ -1664,7 +1648,7 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { // (already handled in a duplicated function) for (Value *V : toHardenVariables) { if(V == NULL) { - errs() << "To harden a null var\n"; + errs() << "Error: To harden a null var\n"; continue; } @@ -1718,7 +1702,7 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { if(annot.second.starts_with("to_harden")) { if(isa(annot.first)) { auto Fn = cast(annot.first); - outs() << "Adding to GrayAreaCallsToFix all calls of " << Fn->getName() << "\n"; + LLVM_DEBUG(dbgs() << "Adding to GrayAreaCallsToFix all calls of " << Fn->getName() << "\n"); // Get function calls in gray area for(auto U : getFunctionFromDuplicate(Fn)->users()) { if(isa(U)) { @@ -1726,7 +1710,7 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { // Protect this call if it's not in toHardenFunction and is not marked as `exclude` if(toHardenFunctions.find(caller) == toHardenFunctions.end() && (FuncAnnotations.find(caller) == FuncAnnotations.end() || !FuncAnnotations.find(caller)->second.starts_with("exclude"))) { - outs() << "GrayAreaCallsToFix added: " << *U << "\n"; + LLVM_DEBUG(dbgs() << "GrayAreaCallsToFix added: " << *U << "\n"); GrayAreaCallsToFix.insert(cast(U)); } } @@ -1741,7 +1725,7 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { if(FuncAnnotations.find(CInstr->getCalledFunction()) != FuncAnnotations.end() && FuncAnnotations.find(CInstr->getCalledFunction())->second.starts_with("exclude")) { // Maybe check if have to fix operands and return after the call - errs() << "About to duplicate a call not to duplciate: " << *CInstr << "\n"; + errs() << "Error: About to duplicate a call not to duplciate: " << *CInstr << "\n"; continue; } @@ -1805,7 +1789,7 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { LLVM_DEBUG(dbgs() << "Fixing invokes\n"); for(InvokeInst *IInstr : toFixInvokes) { if(IInstr == NULL) { - errs() << "To fix a null invoke\n"; + errs() << "Error: To fix a null invoke\n"; continue; } @@ -1824,7 +1808,7 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { // Drop the instructions that have been marked for removal earlier for (Instruction *I2rm : InstructionsToRemove) { if(I2rm == NULL) { - errs() << "To remove a null instruction\n"; + errs() << "Error: To remove a null instruction\n"; continue; } @@ -1852,17 +1836,10 @@ PreservedAnalyses EDDI::run(Module &Md, ModuleAnalysisManager &AM) { LLVM_DEBUG(dbgs() << "Persisting Compiled Functions...\n"); persistCompiledFunctions(CompiledFuncs, "compiled_eddi_functions.csv"); -/* if (Function *mainFunc = Md.getFunction("main")) { - errs() << *mainFunc; - } else { - errs() << "Function 'main' not found!\n"; - } - */ return PreservedAnalyses::none(); } bool EDDI::temporaryArgumentDuplication(Module &Md, llvm::Value *value, IRBuilder<> &B, std::map &InstructionMap) { - errs() << "Performing temporary argument duplication for value: " << *value << " in function: " << B.GetInsertBlock()->getParent()->getName() << "\n"; const llvm::DataLayout &DL = Md.getDataLayout(); Type *valueType; From 401278a9f9d87ae46df1f0f575a384c39ab82f13 Mon Sep 17 00:00:00 2001 From: EmilioCorigliano Date: Wed, 29 Apr 2026 15:07:01 +0200 Subject: [PATCH 16/16] test(EDDI): add complex test for cpp features --- testing/config/tests.toml | 5 + testing/tests/cpp/complex/full_test.cpp | 130 +++++++++++++++++++ testing/tests/cpp/complex/full_test.h | 106 +++++++++++++++ testing/tests/cpp/complex/main_full_test.cpp | 89 +++++++++++++ 4 files changed, 330 insertions(+) create mode 100755 testing/tests/cpp/complex/full_test.cpp create mode 100755 testing/tests/cpp/complex/full_test.h create mode 100755 testing/tests/cpp/complex/main_full_test.cpp diff --git a/testing/config/tests.toml b/testing/config/tests.toml index 329c59e..bf1599b 100644 --- a/testing/config/tests.toml +++ b/testing/config/tests.toml @@ -197,3 +197,8 @@ source_file = "c/mibench/dijkstra_small_mibench.c" test_name = "c_multiple_functions" source_file = "c/autonomous_bench/multiple_functions.c" +[[tests]] +test_name = "cpp_complex" +source_file = "cpp/complex/*.cpp" +add_compiler_flags = "-lm" +black_list = ["--inter-rasm", "--racfed"] \ No newline at end of file diff --git a/testing/tests/cpp/complex/full_test.cpp b/testing/tests/cpp/complex/full_test.cpp new file mode 100755 index 0000000..b0ceac2 --- /dev/null +++ b/testing/tests/cpp/complex/full_test.cpp @@ -0,0 +1,130 @@ +#include +#include +#include +#include "full_test.h" + +void DataCorruption_Handler() +{ + printf("DataCorruption_Handler\n"); + while(1); +} + +void SigMismatch_Handler() +{ + printf("SigMismatch_Handler\n"); + while(1); +} + +float relAltitude(float pressure, float pressureRef, float temperatureRef) +{ + return temperatureRef / 0.0065f * (1 - std::powf(pressure / pressureRef, 0.19026119f)); +} + +// testing unexpected special variable +static Class staticClassToHarden; +// static Class __attribute__((annotate("to_harden"))) staticClassToHarden; + +// Simple function +void simpleFunction() { + std::cout << "Simple function" << std::endl; +} + +// Function with parameters and return value +int add(int a, int b) { + return a + b; +} + +// Function overloading +int multiply(int a, int b) { + return a * b; +} + +double multiply(double a, double b) { + return a * b; +} + +// Class with member functions and constructor +MyClass::MyClass(int x, int y) : a(x), b(y) {} + +MyClass::MyClass(Class *c) { + printf("Start of constructor\n"); + std::cout << "x1: " << c->state.x1 << "\n"; + std::cout << "x2: " << c->state.x2 << "\n"; + a = c->state.x1; + std::cout << "a: " << a << "\n"; + b = c->state.x2; + std::cout << "b: " << b << "\n"; +} + +// Member function +int MyClass::sum() const { + return a + b; +} + +// Virtual function for testing polymorphism +void MyClass::print(Class &c) const { + std::cout << "MyClass: a = " << a << ", b = " << b << std::endl; +} + + +bool MyClass::start() { + Class c; + print(c); + + return true; +} + +// Derived class overriding a virtual function +Main::DerivedClass::DerivedClass(int x, int y, int z) : MyClass(x, y), c(z) { + bho = new Class(); +} + +// Override virtual function +void Main::DerivedClass::print(Class &cl) const { + std::cout << "DerivedClass: a = " << a << ", b = " << b << ", c = " << c << std::endl; +} + +// Recursive function +int factorial(int n) { + if (n <= 1) return 1; + return n * factorial(n - 1); +} + +// Function that throws an exception +void riskyFunction(bool throwException) { + if (throwException) { + throw std::runtime_error("An error occurred in riskyFunction!"); + } + std::cout << "RiskyFunction executed successfully." << std::endl; +} + +// Sret +Class::Class() { + printf("Class constructor\n"); + state = {1.0, 2.f, 3.f, 4.f}; + if(false) { + throw 1; + } +} + +State Class::testSretDuplication(){ + printf("testSretDuplication\n"); + bool callRecursive = (this != &staticClassToHarden); + printf("Condition calculated: %d\n", callRecursive); + if(callRecursive) { + printf("Calling recursive testSretDuplication\n"); + staticClassToHarden.testSretDuplication(); + } + return state; +} + +// noexcept +void TestNoExcept() noexcept +{ + volatile MyClass m_data(1,2); +} + +// void simpleToHardenFunction() __attribute__((annotate("to_harden"))) { +void simpleToHardenFunction() { + simpleFunction(); +} diff --git a/testing/tests/cpp/complex/full_test.h b/testing/tests/cpp/complex/full_test.h new file mode 100755 index 0000000..2f1d937 --- /dev/null +++ b/testing/tests/cpp/complex/full_test.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include + +#include + +// Simple function +void simpleFunction(); +void simpleToHardenFunction(); + +float relAltitude(float pressure, float pressureRef, float temperatureRef); + +// Function with parameters and return value +int add(int a, int b); + +// Function overloading +int multiply(int a, int b); + +double multiply(double a, double b); + +// Sret +struct State +{ + double x0; + float x1; + float x2; + float x3; +}; + +class Class +{ +public: + Class(); + + State testSretDuplication(); + + State state; +}; + +// Class with member functions and constructor +class MyClass { +public: + MyClass(Class *c); + + MyClass(int x, int y); + + // Member function + virtual int sum() const; + + bool start(); + + // Virtual function for testing polymorphism + virtual void print(Class &c) const; + + int a, b; +}; + +namespace Main { +// Derived class overriding a virtual function +class DerivedClass : public MyClass { +public: + DerivedClass(int x, int y, int z); + + // Override virtual function + void print(Class &c) const override; + + +private: + int c; + Class *bho; +}; +} // namespace Main + +// Template function +template +T square(T x) { + return x * x; +} + +// Template class +template +class TemplateClass { +public: + TemplateClass(T x, T y) : a(x), b(y) {} + + T product() const { + return a * b; + } + +private: + T a, b; +}; + +// Recursive function +int factorial(int n); + +// Function that throws an exception +void riskyFunction(bool throwException); + +// noexcept +void TestNoExcept() noexcept; + +// testing unexpected special variable +static Class staticClass; diff --git a/testing/tests/cpp/complex/main_full_test.cpp b/testing/tests/cpp/complex/main_full_test.cpp new file mode 100755 index 0000000..7f00906 --- /dev/null +++ b/testing/tests/cpp/complex/main_full_test.cpp @@ -0,0 +1,89 @@ +#include "full_test.h" + +// Main::DerivedClass __attribute__((annotate("to_harden"))) *globalDClass; +Main::DerivedClass *globalDClass; +MyClass __attribute__((annotate("to_harden"))) *myClassGlobal; + +void startClass(MyClass &c) { + c.start(); +} + +int main() { + printf("Starting main\n"); + Class toPassToMyClass; + printf("After toPassToMyClass\n"); + std::cout << toPassToMyClass.state.x0 << " " << toPassToMyClass.state.x1 << " " << toPassToMyClass.state.x2 << " " << toPassToMyClass.state.x3 << "\n"; + printf("After toPassToMyClass\n"); + myClassGlobal = new MyClass(&toPassToMyClass); + printf("Created myClassGlobal\n"); + myClassGlobal->print(toPassToMyClass); + printf("printed\n"); + myClassGlobal->a = relAltitude(myClassGlobal->b, myClassGlobal->b - 160, 273.0f); + printf("relAltituded\n"); + printf("myClassGlobal->a: %d\n", myClassGlobal->a); + myClassGlobal->print(toPassToMyClass); + printf("printed2\n"); + + globalDClass = new Main::DerivedClass(1,2,3); + + + globalDClass->sum(); + globalDClass->start(); + + startClass(*globalDClass); + + // Test sret + Class sretTest; + std::cout << "test sret: " << sretTest.testSretDuplication().x0 << std::endl; + + std::cout << "staticClass: " << staticClass.testSretDuplication().x3 << std::endl; + + // Call simple function + simpleFunction(); + + // Call function with parameters + std::cout << "Add 3 + 4 = " << add(3, 4) << std::endl; + + // Call overloaded functions + std::cout << "Multiply 3 * 4 = " << multiply(3, 4) << std::endl; + std::cout << "Multiply 2.5 * 4.5 = " << multiply(2.5, 4.5) << std::endl; + + // Test class and member function + MyClass myObj(5, 7); + std::cout << "Sum of MyClass: " << myObj.sum() << std::endl; + myObj.print(sretTest); + + // Test derived class with overridden virtual function + Main::DerivedClass derivedObj(3, 6, 9); + derivedObj.print(sretTest); + + // Test template function + std::cout << "Square of 5: " << square(5) << std::endl; + std::cout << "Square of 2.5: " << square(2.5) << std::endl; + + // Test template class + TemplateClass intObj(3, 4); + TemplateClass doubleObj(2.5, 3.5); + std::cout << "Product of intObj: " << intObj.product() << std::endl; + std::cout << "Product of doubleObj: " << doubleObj.product() << std::endl; + + // Test recursive function + std::cout << "Factorial of 5: " << factorial(5) << std::endl; + + // Test exception handling + try { + riskyFunction(true); // This will throw an exception + } catch (const std::runtime_error& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + } + + try { + riskyFunction(false); // This will not throw an exception + } catch (const std::runtime_error& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + } + + TestNoExcept(); + + return 0; +} \ No newline at end of file