diff --git a/.github/actions/prepare-llvm-cache/action.yml b/.github/actions/prepare-llvm-cache/action.yml new file mode 100644 index 0000000..180667b --- /dev/null +++ b/.github/actions/prepare-llvm-cache/action.yml @@ -0,0 +1,65 @@ +name: Prepare LLVM Cache +description: Restore or build the cached LLVM toolchain. + +inputs: + llvm-commit: + description: LLVM commit to build. + required: true + +runs: + using: composite + steps: + + - name: Restore LLVM Repository Cache + id: restore-llvm-project-cache + uses: actions/cache/restore@v4 + with: + path: ${{ github.workspace }}/llvm-project/ + key: llvm-project-${{ runner.os }}-${{ inputs.llvm-commit }} + + - name: Clone LLVM + if: steps.restore-llvm-project-cache.outputs.cache-hit != 'true' + shell: bash + run: | + git clone --filter=blob:none --no-checkout https://github.com/llvm/llvm-project.git llvm-project + cd llvm-project + git fetch --depth 1 origin ${{ inputs.llvm-commit }} + git checkout FETCH_HEAD + + - name: Save LLVM Project Cache + if: steps.restore-llvm-project-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: ${{ github.workspace }}/llvm-project/ + key: llvm-project-${{ runner.os }}-${{ inputs.llvm-commit }} + + + - name: Restore LLVM Build Cache + id: restore-llvm-cache + uses: actions/cache/restore@v4 + with: + path: ${{ github.workspace }}/llvm-build/ + key: llvm-${{ runner.os }}-${{ inputs.llvm-commit }} + + - name: Build LLVM + if: steps.restore-llvm-cache.outputs.cache-hit != 'true' + shell: bash + working-directory: ${{ github.workspace }}/llvm-project + run: | + builddir="${{ github.workspace }}/llvm-build/" + cmake -G Ninja \ + -B "$builddir" \ + -S llvm \ + -DLLVM_ENABLE_PROJECTS="clang" \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_ENABLE_ASSERTIONS=OFF \ + -DLLDB_INCLUDE_TESTS=OFF \ + -DLIBCLC_TARGETS_TO_BUILD="RISCV;X86" + cmake --build "$builddir" + + - name: Save LLVM Build Cache + if: steps.restore-llvm-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: ${{ github.workspace }}/llvm-build/ + key: llvm-${{ runner.os }}-${{ inputs.llvm-commit }} \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7f2129c..53482fc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,13 @@ name: ASPIS main CI +env: + LLVM_COMMIT: 2078da43e25a4623cab2d0d60decddf709aaea28 # llvmorg-21.1.8 + CMAKE_VERSION: 4.3.0 + MOLD_LINKER_FLAGS: -fuse-ld=mold + CCACHE_DIR: ${{ github.workspace }}/.ccache + CCACHE_MAXSIZE: 10G + CCACHE_BASEDIR: ${{ github.workspace }} + CCACHE_COMPILERCHECK: content + CCACHE_NOHASHDIR: "true" on: [push] jobs: compile-ASPIS: @@ -8,64 +17,64 @@ jobs: uses: actions/checkout@v4 with: path: ASPIS - - name: Cache LLVM build - uses: actions/cache@v4 - id: llvm-cache + + - name: Install system dependencies + run: | + sudo apt update + sudo apt install -y cmake ninja-build build-essential mold curl ca-certificates ccache + + - name: Prepare LLVM Cache + uses: ./ASPIS/.github/actions/prepare-llvm-cache with: - path: /mnt/build/ - key: llvm-build-${{ hashFiles('llvm-project/**', 'ASPIS/CMakeLists.txt') }} - restore-keys: | - llvm-build- - - name: Checking out LLVM project - uses: actions/checkout@master - if: steps.llvm-cache.outputs.cache-hit != 'true' + llvm-commit: ${{ env.LLVM_COMMIT }} + + - name: Restore ccache + id: restore-ccache + uses: actions/cache/restore@v4 with: - repository: llvm/llvm-project - ref: llvmorg-21.1.8 - path: llvm-project - - name: Compiling LLVM - if: steps.llvm-cache.outputs.cache-hit != 'true' - shell: bash - working-directory: ./llvm-project - run: | - builddir="/mnt/build/" - sudo mkdir -p $builddir - sudo chmod 777 $builddir - cmake -G Ninja \ - -B "$builddir" \ - -S llvm \ - -DLLVM_ENABLE_PROJECTS="clang" \ - -DCMAKE_BUILD_TYPE=Release \ - -DLLVM_ENABLE_ASSERTIONS=OFF \ - -DLLDB_INCLUDE_TESTS=OFF \ - -DLIBCLC_TARGETS_TO_BUILD="RISCV;X86" - cmake --build "$builddir" + path: ${{ env.CCACHE_DIR }} + key: ccache-${{ runner.os }}-${{ github.ref_name }}-${{ env.LLVM_COMMIT }}-${{ env.CMAKE_VERSION }}-${{ github.sha }} + restore-keys: | + ccache-${{ runner.os }}-${{ github.ref_name }}-${{ env.LLVM_COMMIT }}-${{ env.CMAKE_VERSION }}- + ccache-${{ runner.os }}-${{ env.LLVM_COMMIT }}-${{ env.CMAKE_VERSION }}- + - name: Compiling ASPIS shell: bash - working-directory: ./ASPIS + working-directory: ${{ github.workspace }}/ASPIS run: | - mkdir build - cmake -B build -DLLVM_DIR=/mnt/build/lib/cmake/llvm/ - cmake --build build + ccache --set-config=max_size=${CCACHE_MAXSIZE} + ccache --zero-stats + ccache --show-config + ccache --show-stats + + mkdir build + cmake -B build -DLLVM_DIR=${{ github.workspace }}/llvm-build/lib/cmake/llvm/ + cmake --build build + + - name: Save ccache + if: steps.build-llvm.outcome == 'success' && steps.restore-ccache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: ${{ env.CCACHE_DIR }} + key: ccache-${{ runner.os }}-${{ github.ref_name }}-${{ env.LLVM_COMMIT }}-${{ env.CMAKE_VERSION }}-${{ github.sha }} + - name: Cache pip uses: actions/cache@v4 id: pip-cache with: path: ~/.cache/pip - key: pip-${{ hashFiles('ASPIS/testing/requirements.txt') }} + key: pip-${{ hashFiles('${{ github.workspace }}/ASPIS/testing/requirements.txt') }} restore-keys: | pip- - name: Install Python requirements shell: bash - if: steps.llvm-cache.outputs.cache-hit != 'true' - working-directory: ./ASPIS/testing + if: steps.restore-ccache.outputs.cache-hit != 'true' + working-directory: ${{ github.workspace }}/ASPIS/testing run: | pip install -r requirements.txt - name: Testing ASPIS shell: bash - working-directory: ./ASPIS/testing + working-directory: ${{ github.workspace }}/ASPIS/testing run: | - pytest test.py --tests-file config/tests.toml - pytest test.py --tests-file config/racfed+eddi.toml - pytest test.py --tests-file config/racfed.toml + pytest test.py 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..6626f28 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,23 @@ 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; + + std::string entryPoint; - // Map of for which we need to always use the duplicate in place of the original - std::map ValuesToAlwaysDup; + LinkageMap linkageMap; + bool duplicateAll; + bool MultipleErrBBEnabled; + + 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 +58,20 @@ 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); public: + explicit EDDI(bool duplicateAll, bool MultipleErrBBEnabled = false, std::string entryPoint = "main") : duplicateAll(duplicateAll), MultipleErrBBEnabled(MultipleErrBBEnabled), entryPoint(entryPoint) {} + PreservedAnalyses run(Module &M, ModuleAnalysisManager &); @@ -118,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/CMakeLists.txt b/passes/CMakeLists.txt index 691f0ac..a2983ea 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 @@ -16,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 @@ -25,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 diff --git a/passes/EDDI.cpp b/passes/EDDI.cpp index d8e5a35..3036d19 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,491 @@ 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 + 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"; + 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 && vtable->hasInitializer()) { + // 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 { + 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 { + // 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 +633,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 @@ -177,22 +666,26 @@ 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) { // 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,26 +736,29 @@ 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 */ 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 @@ -275,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 @@ -324,9 +833,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 +850,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 +865,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 +878,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 +892,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 +943,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 +985,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 +1023,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 +1071,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()) { @@ -658,25 +1097,19 @@ 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) { - 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 - 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( @@ -710,12 +1143,9 @@ 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 { - Instruction *NewCInstr; - IRBuilder<> CallBuilder(CInstr); if (isa(CInstr)) { InvokeInst *IInst=cast(CInstr); NewCInstr = CallBuilder.CreateInvoke(Fn->getFunctionType(), Fn,IInst->getNormalDest(),IInst->getUnwindDest(), args); @@ -726,9 +1156,12 @@ 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); + } + + if(NewCInstr) { + DuplicatedCalls.insert(NewCInstr); } return res; @@ -786,7 +1219,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 +1231,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 +1246,53 @@ 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); + + 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")) || - 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 +1305,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 +1353,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 +1363,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 +1386,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 +1400,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 +1513,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,222 +1527,529 @@ 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"); +#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) { + // 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); - 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; + // 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() << "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"); + + std::set InstToDuplicate; + for (BasicBlock &BB : *Fn) { + for (Instruction &I : BB) { + InstToDuplicate.insert(&I); + } + } - // 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++; + 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 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 } - 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); - } - } + } + + 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); } } + } else { + GrayAreaCallsToFix.insert(cast(I)); } + + // insert the code for calling the error basic block in case of a mismatch + CreateErrBB(Md, *Fn, ErrBB); } - for (Instruction *I2rm : InstructionsToRemove) { - I2rm->eraseFromParent(); - } - InstructionsToRemove.clear(); - std::list instructionsToDuplicate; - for (BasicBlock &BB : Fn) { - for (Instruction &I : BB) { - if (!isValueDuplicated(DuplicatedInstructionMap, I)) { - instructionsToDuplicate.push_back(&I); + } + } + + // 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)); + } } } } + } + } + + 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; + } - 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); + + // 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 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 + } + } + } + + 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)); + } + } + + // Finally, duplicate the call with the temporary DuplicatedInstructionMap + if(duplicateInstruction(*CInstr, TmpDuplicatedInstructionMap, *ErrBB)) { + if(InstructionsToRemove.find(CInstr) == InstructionsToRemove.end()) { + InstructionsToRemove.insert(CInstr); + } + } + + // 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 + if(DebugEnabled){ + LLVM_DEBUG(dbgs() << "Fixing DataCorruptionHandlers\n"); + auto *DataCorruptionH = Md.getFunction(getLinkageName(linkageMap, "DataCorruption_Handler")); + for(User *U : DataCorruptionH->users()) { + if(isa(U)) { + if(auto *CallI = cast(U)) { + if(auto dbgLoc = findNearestDebugLoc(*CallI)) { + 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(); +} + +Instruction *getSingleReturnInst(Function &F) { + for (BasicBlock &BB : F) { + if (auto *retInst = llvm::dyn_cast(BB.getTerminator())) { + return retInst; + } + } + return nullptr; +} - // insert the code for calling the error basic block in case of a mismatch - IRBuilder<> ErrB(ErrBB); +void EDDI::CreateErrBB(Module &Md, Function &Fn, BasicBlock *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())); - 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 + + std::list errBranches; + for (User *U : ErrBB->users()) { + Instruction *I = cast(U); + errBranches.push_back(I); + } - auto *CallI = ErrB.CreateCall(CalleeF); - ErrB.CreateUnreachable(); + // 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()); + + 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(); + } - #ifdef DC_HANDLER_INLINE - 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) { + for (Instruction &ErrI : *ErrBB) { if (!I->getDebugLoc()) { - ErrI.setDebugLoc(findNearestDebugLoc(Fn.back().getTerminator())); + 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 - if (DebugEnabled) { - // TODO fix possible null derereference below - for (Instruction &ErrI : *ErrBB) { - auto DL = findNearestDebugLoc(getSingleReturnInst(Fn)); - if (!DL) { - DL = findNearestDebugLoc(Fn.back().getTerminator()); + } + } 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) { - 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"; } - 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 +2071,14 @@ llvm::PassPluginLibraryInfo getEDDIPluginInfo() { [](StringRef Name, ModulePassManager &FPM, ArrayRef) { if (Name == "eddi-verify") { - FPM.addPass(EDDI()); +#ifdef DUPLICATE_ALL + FPM.addPass(EDDI(true, true)); +#else + FPM.addPass(EDDI(false, true)); +#endif return true; } + return false; }); PB.registerPipelineParsingCallback( diff --git a/passes/Utils/Utils.cpp b/passes/Utils/Utils.cpp index 7fcbb15..6cabb1c 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,58 @@ bool shouldCompile(Function &Fn, && OriginalFunctions.find(&Fn) == OriginalFunctions.end(); } -DebugLoc findNearestDebugLoc(Instruction *I) { - std::list candidates; - if (I == nullptr) { +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; } - if (I->getDebugLoc()) return I->getDebugLoc(); + std::list candidates; + + Instruction *PrevI = &I; - auto *PrevI = I->getPrevNonDebugInstruction(); + if(I.getDebugLoc()) { + return I.getDebugLoc(); + } - 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()); + if(I.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 +191,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 +218,46 @@ 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; + } + + 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; + } - return false; + return false; } void createFtFunc(Module &Md, StringRef name) { diff --git a/passes/Utils/Utils.h b/passes/Utils/Utils.h index f0c5d37..cdc7f0b 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 isToExcludeName(StringRef FnMangledName); void createFtFuncs(Module &Md); diff --git a/testing/README.md b/testing/README.md index 7033b79..2b23956 100644 --- a/testing/README.md +++ b/testing/README.md @@ -4,10 +4,10 @@ This directory contains utilities and scripts for testing ASPIS. ## Local Testing -For local testing, use the `test.py` script. Configure your tests using `tests.toml`. Configure the llvm_bin flag in `llvm_bin.toml`. +For local testing, use the `test.py` script. Configure your tests using `complete_testing.toml`. Configure the llvm_bin flag in `llvm_bin.toml`. Then run: ```bash -pytest test.py +pytest test.py --no-header -v ``` > To run pytest the modules listed in requirements.txt must be installed. @@ -26,12 +26,12 @@ Test config files must be `.toml` files with the following structure for each te [[tests]] test_name = source_file = -expected_output = -aspis_options = ``` > `` is a relative path from `./tests/` folder +All the combinations of ASPIS protection mechanisms will be used for each different test. + ### Flags It is possible to write different configuration test files. diff --git a/testing/config/llvm.toml b/testing/config/llvm.toml index 0a61754..8b36024 100644 --- a/testing/config/llvm.toml +++ b/testing/config/llvm.toml @@ -1,2 +1,2 @@ #llvm_bin = "" -llvm_bin = "/mnt/build/bin/" \ No newline at end of file +llvm_bin = "/home/runner/work/ASPIS/ASPIS/llvm-build/bin" \ No newline at end of file diff --git a/testing/config/racfed+eddi.toml b/testing/config/racfed+eddi.toml deleted file mode 100644 index ce3d34e..0000000 --- a/testing/config/racfed+eddi.toml +++ /dev/null @@ -1,108 +0,0 @@ -[[tests]] -test_name = "racfed_eddi_ml_add" -source_file = "c/multi_instruction/add.c" -expected_output = "30" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_ml_function_call" -source_file = "c/multi_instruction/function.c" -expected_output = "foo() 25" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_preserve_runtime_sig" -source_file = "c/multi_instruction/call_less_two.c" -expected_output = "0" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_ml_if_then_else" -source_file = "c/multi_instruction/if_then_else.c" -expected_output = "1001" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_ml_phi_instruction" -source_file = "c/multi_instruction/phi.c" -expected_output = "SUCCESS" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_function_pointer" -source_file = "c/control_flow/function_pointer.c" -expected_output = "42" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_loop_exit" -source_file = "c/control_flow/loop_exit.c" -expected_output = "2" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_n-branch_eddi" -source_file = "c/control_flow/nested-branch.c" -expected_output = "6" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_s-branch_eddi" -source_file = "c/control_flow/simple-branch.c" -expected_output = "OK" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_s-case_eddi" -source_file = "c/control_flow/switch-case.c" -expected_output = "300" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_data_dep_branches" -source_file = "c/data_duplication_integrity/data_dep_branches.c" -expected_output = "7" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_global_var_across_functions" -source_file = "c/data_duplication_integrity/global_var_across_functions.c" -expected_output = "2" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_misc_data_dup" -source_file = "c/data_duplication_integrity/misc_data_dup.c" -expected_output = "OK" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_volatile_io" -source_file = "c/data_duplication_integrity/volatile_io.c" -expected_output = "42" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_arit_pipeline" -source_file = "c/misc_math/arit_pipeline.c" -expected_output = "3" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_mixed_ops" -source_file = "c/misc_math/mixed_ops.c" -expected_output = "14.5" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_eddi_xor_cypher" -source_file = "c/misc_math/xor_cypher.c" -expected_output = "SUCCESS" -aspis_options = "--eddi --racfed" - -[[tests]] -test_name = "racfed_ml_multi_if_else" -source_file = "c/multi_instruction/multi_if_then_else.c" -expected_output = "r > 200" -aspis_options = "--eddi --racfed" - diff --git a/testing/config/racfed.toml b/testing/config/racfed.toml deleted file mode 100644 index 5129d1f..0000000 --- a/testing/config/racfed.toml +++ /dev/null @@ -1,108 +0,0 @@ -[[tests]] -test_name = "racfed_ml_add" -source_file = "c/multi_instruction/add.c" -expected_output = "30" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_ml_function_call" -source_file = "c/multi_instruction/function.c" -expected_output = "foo() 25" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_preserve_runtime_sig" -source_file = "c/multi_instruction/call_less_two.c" -expected_output = "0" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_ml_if_then_else" -source_file = "c/multi_instruction/if_then_else.c" -expected_output = "1001" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_ml_phi_instruction" -source_file = "c/multi_instruction/phi.c" -expected_output = "SUCCESS" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_function_pointer" -source_file = "c/control_flow/function_pointer.c" -expected_output = "42" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_loop_exit" -source_file = "c/control_flow/loop_exit.c" -expected_output = "2" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_nested-branch" -source_file = "c/control_flow/nested-branch.c" -expected_output = "6" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_simple-branch" -source_file = "c/control_flow/simple-branch.c" -expected_output = "OK" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_switch-case" -source_file = "c/control_flow/switch-case.c" -expected_output = "300" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_data_dep_branches" -source_file = "c/data_duplication_integrity/data_dep_branches.c" -expected_output = "7" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_global_var_across_functions" -source_file = "c/data_duplication_integrity/global_var_across_functions.c" -expected_output = "2" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_misc_data_dup" -source_file = "c/data_duplication_integrity/misc_data_dup.c" -expected_output = "OK" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_volatile_io" -source_file = "c/data_duplication_integrity/volatile_io.c" -expected_output = "42" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_arit_pipeline" -source_file = "c/misc_math/arit_pipeline.c" -expected_output = "3" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_mixed_ops" -source_file = "c/misc_math/mixed_ops.c" -expected_output = "14.5" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_xor_cypher" -source_file = "c/misc_math/xor_cypher.c" -expected_output = "SUCCESS" -aspis_options = "--no-dup --racfed" - -[[tests]] -test_name = "racfed_ml_multi_if_else" -source_file = "c/multi_instruction/multi_if_then_else.c" -expected_output = "r > 200" -aspis_options = "--no-dup --racfed" - diff --git a/testing/config/tests.toml b/testing/config/tests.toml index f7eed84..61d0239 100644 --- a/testing/config/tests.toml +++ b/testing/config/tests.toml @@ -1,750 +1,135 @@ - -[[tests]] -test_name = "add1" -source_file = "cpp/simple/add.cpp" -expected_output = "7" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "add2" -source_file = "cpp/simple/add.cpp" -expected_output = "7" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "add3" -source_file = "cpp/simple/add.cpp" -expected_output = "7" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "add4" -source_file = "cpp/simple/add.cpp" -expected_output = "7" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "class_polymorphism1" -source_file = "cpp/simple/class.cpp" -expected_output = """ -12 -5, 7 -3, 6, 9""" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "class_polymorphism2" -source_file = "cpp/simple/class.cpp" -expected_output = """ -12 -5, 7 -3, 6, 9""" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "class_polymorphism3" -source_file = "cpp/simple/class.cpp" -expected_output = """ -12 -5, 7 -3, 6, 9""" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "class_polymorphism4" -source_file = "cpp/simple/class.cpp" -expected_output = """ -12 -5, 7 -3, 6, 9""" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "exception_handling1" -source_file = "cpp/simple/exceptions.cpp" -expected_output = """ -Exception caught: Test exception -Exception caught: Test exception""" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "exception_handling2" -source_file = "cpp/simple/exceptions.cpp" -expected_output = """ -Exception caught: Test exception -Exception caught: Test exception""" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "exception_handling3" -source_file = "cpp/simple/exceptions.cpp" -expected_output = """ -Exception caught: Test exception -Exception caught: Test exception""" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "exception_handling4" -source_file = "cpp/simple/exceptions.cpp" -expected_output = """ -Exception caught: Test exception -Exception caught: Test exception""" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "fact1" -source_file = "cpp/simple/fact.cpp" -expected_output = """ -3628800""" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "fact2" -source_file = "cpp/simple/fact.cpp" -expected_output = """ -3628800""" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "fact3" -source_file = "cpp/simple/fact.cpp" -expected_output = """ -3628800""" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "fact4" -source_file = "cpp/simple/fact.cpp" -expected_output = """ -3628800""" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "func1" -source_file = "cpp/simple/func.cpp" -expected_output = """ -Function f called (g incremented to 1) -Final value of g: 1""" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "func2" -source_file = "cpp/simple/func.cpp" -expected_output = """ -Function f called (g incremented to 1) -Final value of g: 1""" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "func3" -source_file = "cpp/simple/func.cpp" -expected_output = """ -Function f called (g incremented to 1) -Final value of g: 1""" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "func4" -source_file = "cpp/simple/func.cpp" -expected_output = """ -Function f called (g incremented to 1) -Final value of g: 1""" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "mul1" -source_file = "cpp/simple/mul.cpp" -expected_output = """ -42""" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "mul2" -source_file = "cpp/simple/mul.cpp" -expected_output = """ -42""" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "mul3" -source_file = "cpp/simple/mul.cpp" -expected_output = """ -42""" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "mul4" -source_file = "cpp/simple/mul.cpp" -expected_output = """ -42""" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "template1" -source_file = "cpp/simple/template.cpp" -expected_output = """ -Result: 42 -Result: 15""" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "template2" -source_file = "cpp/simple/template.cpp" -expected_output = """ -Result: 42 -Result: 15""" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "template3" -source_file = "cpp/simple/template.cpp" -expected_output = """ -Result: 42 -Result: 15""" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "template4" -source_file = "cpp/simple/template.cpp" -expected_output = """ -Result: 42 -Result: 15""" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "ptrsSharedUnique1" -source_file = "cpp/simple/ptr.cpp" -expected_output = """ -Value pointed by p1: 42, Value pointed by p2: 42""" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "ptrsSharedUnique2" -source_file = "cpp/simple/ptr.cpp" -expected_output = """ -Value pointed by p1: 42, Value pointed by p2: 42""" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "ptrsSharedUnique3" -source_file = "cpp/simple/ptr.cpp" -expected_output = """ -Value pointed by p1: 42, Value pointed by p2: 42""" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "ptrsSharedUnique4" -source_file = "cpp/simple/ptr.cpp" -expected_output = """ -Value pointed by p1: 42, Value pointed by p2: 42""" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "heap1" -source_file = "cpp/simple/heap.cpp" -expected_output = """ -Value: 10""" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "heap2" -source_file = "cpp/simple/heap.cpp" -expected_output = """ -Value: 10""" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "heap3" -source_file = "cpp/simple/heap.cpp" -expected_output = """ -Value: 10""" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "heap4" -source_file = "cpp/simple/heap.cpp" -expected_output = """ -Value: 10""" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "raii_filehandler1" -source_file = "cpp/simple/file_handler.cpp" -expected_output = """ -Handler created -File closed""" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "raii_filehandler2" -source_file = "cpp/simple/file_handler.cpp" -expected_output = """ -Handler created -File closed""" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "raii_filehandler3" -source_file = "cpp/simple/file_handler.cpp" -expected_output = """ -Handler created -File closed""" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "raii_filehandler4" -source_file = "cpp/simple/file_handler.cpp" -expected_output = """ -Handler created -File closed""" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "inline_constexpr1" -source_file = "cpp/simple/inline_constexpr.cpp" -aspis_options = "--eddi --cfcss" -expected_output = """ -9 -16 -5""" - -[[tests]] -test_name = "inline_constexpr2" -source_file = "cpp/simple/inline_constexpr.cpp" -aspis_options = "--seddi --rasm" -expected_output = """ -9 -16 -5""" - -[[tests]] -test_name = "inline_constexpr3" -source_file = "cpp/simple/inline_constexpr.cpp" -aspis_options = "--fdsc --rasm" -expected_output = """ -9 -16 -5""" - -[[tests]] -test_name = "inline_constexpr4" -source_file = "cpp/simple/inline_constexpr.cpp" -aspis_options = "--eddi --no-cfc" -expected_output = """ -9 -16 -5""" - -[[tests]] -test_name = "lambda_captures1" -source_file = "cpp/simple/lambda_captures.cpp" -aspis_options = "--eddi --cfcss" -expected_output = """ -x incremented by 5 -Value pointed by p: 11""" - [[tests]] -test_name = "lambda_captures2" -source_file = "cpp/simple/lambda_captures.cpp" -aspis_options = "--seddi --rasm" -expected_output = """ -x incremented by 5 -Value pointed by p: 11""" - -[[tests]] -test_name = "lambda_captures3" -source_file = "cpp/simple/lambda_captures.cpp" -aspis_options = "--fdsc --rasm" -expected_output = """ -x incremented by 5 -Value pointed by p: 11""" - -[[tests]] -test_name = "lambda_captures4" -source_file = "cpp/simple/lambda_captures.cpp" -aspis_options = "--eddi --no-cfc" -expected_output = """ -x incremented by 5 -Value pointed by p: 11""" - -[[tests]] -test_name = "volatile_memory_order1" -source_file = "cpp/simple/volatile_memory_order.cpp" -aspis_options = "--eddi --cfcss" -expected_output = """ -42""" - -[[tests]] -test_name = "volatile_memory_order2" -source_file = "cpp/simple/volatile_memory_order.cpp" -aspis_options = "--seddi --rasm" -expected_output = """ -42""" - -[[tests]] -test_name = "volatile_memory_order3" -source_file = "cpp/simple/volatile_memory_order.cpp" -aspis_options = "--fdsc --rasm" -expected_output = """ -42""" - -[[tests]] -test_name = "volatile_memory_order4" -source_file = "cpp/simple/volatile_memory_order.cpp" -aspis_options = "--eddi --no-cfc" -expected_output = """ -42""" - -[[tests]] -test_name = "stl_containers_advanced1" -source_file = "cpp/simple/stl_containers_advanced.cpp" -aspis_options = "--eddi --cfcss" -expected_output = """ -0 1 2 3 4""" - -[[tests]] -test_name = "stl_containers_advanced2" -source_file = "cpp/simple/stl_containers_advanced.cpp" -aspis_options = "--seddi --rasm" -expected_output = """ -0 1 2 3 4""" - -[[tests]] -test_name = "stl_containers_advanced3" -source_file = "cpp/simple/stl_containers_advanced.cpp" -aspis_options = "--fdsc --rasm" -expected_output = """ -0 1 2 3 4""" - -[[tests]] -test_name = "stl_containers_advanced4" -source_file = "cpp/simple/stl_containers_advanced.cpp" -aspis_options = "--eddi --no-cfc" -expected_output = """ -0 1 2 3 4""" - -# [[tests]] -# test_name = "threads1" -# source_file = "cpp/simple/threads.cpp" -# aspis_options = "--eddi --cfcss" -# expected_output = """ -# 4000 -# """ -# -# [[tests]] -# test_name = "threads2" -# source_file = "cpp/simple/threads.cpp" -# aspis_options = "--seddi --rasm" -# expected_output = """ -# 4000 -# """ -# -# [[tests]] -# test_name = "threads3" -# source_file = "cpp/simple/threads.cpp" -# aspis_options = "--fdsc --rasm" -# expected_output = """ -# 4000 -# """ -# -# [[tests]] -# test_name = "threads4" -# source_file = "cpp/simple/threads.cpp" -# aspis_options = "--eddi --no-cfc" -# expected_output = """ -# 4000 -# """ - -[[tests]] -test_name = "function_pointer_v0" -source_file = "c/control_flow/function_pointer.c" -expected_output = "42" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "function_pointer_v1" -source_file = "c/control_flow/function_pointer.c" -expected_output = "42" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "function_pointer_v2" -source_file = "c/control_flow/function_pointer.c" -expected_output = "42" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "function_pointer_v3" +test_name = "c_function_pointer" source_file = "c/control_flow/function_pointer.c" -expected_output = "42" -aspis_options = "--eddi --no-cfc" [[tests]] -test_name = "loop_exit_v0" +test_name = "c_loop_exit" source_file = "c/control_flow/loop_exit.c" -expected_output = "2" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "loop_exit_v1" -source_file = "c/control_flow/loop_exit.c" -expected_output = "2" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "loop_exit_v2" -source_file = "c/control_flow/loop_exit.c" -expected_output = "2" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "loop_exit_v3" -source_file = "c/control_flow/loop_exit.c" -expected_output = "2" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "nested-branch_v0" -source_file = "c/control_flow/nested-branch.c" -expected_output = "6" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "nested-branch_v1" -source_file = "c/control_flow/nested-branch.c" -expected_output = "6" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "nested-branch_v2" -source_file = "c/control_flow/nested-branch.c" -expected_output = "6" -aspis_options = "--fdsc --rasm" [[tests]] -test_name = "nested-branch_v3" +test_name = "c_nested-branch" source_file = "c/control_flow/nested-branch.c" -expected_output = "6" -aspis_options = "--eddi --no-cfc" [[tests]] -test_name = "simple-branch_v0" +test_name = "c_simple-branch" source_file = "c/control_flow/simple-branch.c" -expected_output = "OK" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "simple-branch_v1" -source_file = "c/control_flow/simple-branch.c" -expected_output = "OK" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "simple-branch_v2" -source_file = "c/control_flow/simple-branch.c" -expected_output = "OK" -aspis_options = "--fdsc --rasm" - -[[tests]] -test_name = "simple-branch_v3" -source_file = "c/control_flow/simple-branch.c" -expected_output = "OK" -aspis_options = "--eddi --no-cfc" - -[[tests]] -test_name = "switch-case_v0" -source_file = "c/control_flow/switch-case.c" -expected_output = "300" -aspis_options = "--eddi --cfcss" - -[[tests]] -test_name = "switch-case_v1" -source_file = "c/control_flow/switch-case.c" -expected_output = "300" -aspis_options = "--seddi --rasm" - -[[tests]] -test_name = "switch-case_v2" -source_file = "c/control_flow/switch-case.c" -expected_output = "300" -aspis_options = "--fdsc --rasm" [[tests]] -test_name = "switch-case_v3" +test_name = "c_switch-case" source_file = "c/control_flow/switch-case.c" -expected_output = "300" -aspis_options = "--eddi --no-cfc" [[tests]] -test_name = "data_dep_branches_v0" +test_name = "c_data_dep_branches" source_file = "c/data_duplication_integrity/data_dep_branches.c" -expected_output = "7" -aspis_options = "--eddi --cfcss" [[tests]] -test_name = "data_dep_branches_v1" -source_file = "c/data_duplication_integrity/data_dep_branches.c" -expected_output = "7" -aspis_options = "--seddi --rasm" +test_name = "c_global_var_across_functions" +source_file = "c/data_duplication_integrity/global_var_across_functions.c" [[tests]] -test_name = "data_dep_branches_v2" -source_file = "c/data_duplication_integrity/data_dep_branches.c" -expected_output = "7" -aspis_options = "--fdsc --rasm" +test_name = "c_misc_data_dup" +source_file = "c/data_duplication_integrity/misc_data_dup.c" -[[tests]] -test_name = "data_dep_branches_v3" -source_file = "c/data_duplication_integrity/data_dep_branches.c" -expected_output = "7" -aspis_options = "--eddi --no-cfc" +# [[tests]] +# test_name = "c_printf_to_duplicate" +# source_file = "c/data_duplication_integrity/printf_to_duplicate.c" [[tests]] -test_name = "global_var_across_functions_v0" -source_file = "c/data_duplication_integrity/global_var_across_functions.c" -expected_output = "2" -aspis_options = "--eddi --cfcss" +test_name = "c_volatile_io" +source_file = "c/data_duplication_integrity/volatile_io.c" [[tests]] -test_name = "global_var_across_functions_v1" -source_file = "c/data_duplication_integrity/global_var_across_functions.c" -expected_output = "2" -aspis_options = "--seddi --rasm" +test_name = "c_arit_pipeline" +source_file = "c/misc_math/arit_pipeline.c" [[tests]] -test_name = "global_var_across_functions_v2" -source_file = "c/data_duplication_integrity/global_var_across_functions.c" -expected_output = "2" -aspis_options = "--fdsc --rasm" +test_name = "c_mixed_ops" +source_file = "c/misc_math/mixed_ops.c" -[[tests]] -test_name = "global_var_across_functions_v3" -source_file = "c/data_duplication_integrity/global_var_across_functions.c" -expected_output = "2" -aspis_options = "--eddi --no-cfc" +# [[tests]] +# test_name = "c_xor_cypher" +# source_file = "c/misc_math/xor_cypher.c" [[tests]] -test_name = "misc_data_dup_v0" -source_file = "c/data_duplication_integrity/misc_data_dup.c" -expected_output = "OK" -aspis_options = "--eddi --cfcss" +test_name = "c_add" +source_file = "c/multi_instruction/add.c" [[tests]] -test_name = "misc_data_dup_v1" -source_file = "c/data_duplication_integrity/misc_data_dup.c" -expected_output = "OK" -aspis_options = "--seddi --rasm" +test_name = "c_call_less_two" +source_file = "c/multi_instruction/call_less_two.c" [[tests]] -test_name = "misc_data_dup_v2" -source_file = "c/data_duplication_integrity/misc_data_dup.c" -expected_output = "OK" -aspis_options = "--fdsc --rasm" +test_name = "c_function" +source_file = "c/multi_instruction/function.c" [[tests]] -test_name = "misc_data_dup_v3" -source_file = "c/data_duplication_integrity/misc_data_dup.c" -expected_output = "OK" -aspis_options = "--eddi --no-cfc" +test_name = "c_if_then_else" +source_file = "c/multi_instruction/if_then_else.c" [[tests]] -test_name = "volatile_io_v0" -source_file = "c/data_duplication_integrity/volatile_io.c" -expected_output = "42" -aspis_options = "--eddi --cfcss" +test_name = "c_multi_if_then_else" +source_file = "c/multi_instruction/multi_if_then_else.c" [[tests]] -test_name = "volatile_io_v1" -source_file = "c/data_duplication_integrity/volatile_io.c" -expected_output = "42" -aspis_options = "--seddi --rasm" +test_name = "c_phi" +source_file = "c/multi_instruction/phi.c" [[tests]] -test_name = "volatile_io_v2" -source_file = "c/data_duplication_integrity/volatile_io.c" -expected_output = "42" -aspis_options = "--fdsc --rasm" +test_name = "cpp_add" +source_file = "cpp/simple/add.cpp" [[tests]] -test_name = "volatile_io_v3" -source_file = "c/data_duplication_integrity/volatile_io.c" -expected_output = "42" -aspis_options = "--eddi --no-cfc" +test_name = "cpp_class" +source_file = "cpp/simple/class.cpp" [[tests]] -test_name = "arit_pipeline_v0" -source_file = "c/misc_math/arit_pipeline.c" -expected_output = "3" -aspis_options = "--eddi --cfcss" +test_name = "cpp_exceptions" +source_file = "cpp/simple/exceptions.cpp" [[tests]] -test_name = "arit_pipeline_v1" -source_file = "c/misc_math/arit_pipeline.c" -expected_output = "3" -aspis_options = "--seddi --rasm" +test_name = "cpp_fact" +source_file = "cpp/simple/fact.cpp" [[tests]] -test_name = "arit_pipeline_v2" -source_file = "c/misc_math/arit_pipeline.c" -expected_output = "3" -aspis_options = "--fdsc --rasm" +test_name = "cpp_file_handler" +source_file = "cpp/simple/file_handler.cpp" [[tests]] -test_name = "arit_pipeline_v3" -source_file = "c/misc_math/arit_pipeline.c" -expected_output = "3" -aspis_options = "--eddi --no-cfc" +test_name = "cpp_func" +source_file = "cpp/simple/func.cpp" [[tests]] -test_name = "mixed_ops_v0" -source_file = "c/misc_math/mixed_ops.c" -expected_output = "14.5" -aspis_options = "--eddi --cfcss" +test_name = "cpp_heap" +source_file = "cpp/simple/heap.cpp" [[tests]] -test_name = "mixed_ops_v1" -source_file = "c/misc_math/mixed_ops.c" -expected_output = "14.5" -aspis_options = "--seddi --rasm" +test_name = "cpp_inline_constexpr" +source_file = "cpp/simple/inline_constexpr.cpp" [[tests]] -test_name = "mixed_ops_v2" -source_file = "c/misc_math/mixed_ops.c" -expected_output = "14.5" -aspis_options = "--fdsc --rasm" +test_name = "cpp_lambda_captures" +source_file = "cpp/simple/lambda_captures.cpp" [[tests]] -test_name = "mixed_ops_v3" -source_file = "c/misc_math/mixed_ops.c" -expected_output = "14.5" -aspis_options = "--eddi --no-cfc" +test_name = "cpp_mul" +source_file = "cpp/simple/mul.cpp" [[tests]] -test_name = "xor_cypher_v0" -source_file = "c/misc_math/xor_cypher.c" -expected_output = "SUCCESS" -aspis_options = "--eddi --cfcss" +test_name = "cpp_ptr" +source_file = "cpp/simple/ptr.cpp" [[tests]] -test_name = "xor_cypher_v1" -source_file = "c/misc_math/xor_cypher.c" -expected_output = "SUCCESS" -aspis_options = "--seddi --rasm" +test_name = "cpp_stl_containers_advanced" +source_file = "cpp/simple/stl_containers_advanced.cpp" [[tests]] -test_name = "xor_cypher_v2" -source_file = "c/misc_math/xor_cypher.c" -expected_output = "SUCCESS" -aspis_options = "--fdsc --rasm" +test_name = "cpp_template" +source_file = "cpp/simple/template.cpp" -[[tests]] -test_name = "xor_cypher_v3" -source_file = "c/misc_math/xor_cypher.c" -expected_output = "SUCCESS" -aspis_options = "--eddi --no-cfc" +# [[tests]] +# test_name = "cpp_threads" +# source_file = "cpp/simple/threads.cpp" [[tests]] -test_name = "printf_to_duplicate" -source_file = "c/data_duplication_integrity/printf_to_duplicate.c" -expected_output = "The string is: Hello WorldThe string is: Hello World" -aspis_options = "--eddi --no-cfc" \ No newline at end of file +test_name = "cpp_volatile_memory_order" +source_file = "cpp/simple/volatile_memory_order.cpp" diff --git a/testing/requirements.txt b/testing/requirements.txt index 48ea52e..db2b2c7 100644 --- a/testing/requirements.txt +++ b/testing/requirements.txt @@ -3,3 +3,4 @@ packaging==25.0 pluggy==1.6.0 Pygments==2.19.2 pytest==8.4.1 +pytest-timeout==2.4.0 diff --git a/testing/test.py b/testing/test.py index 6c1f571..77e1d6c 100644 --- a/testing/test.py +++ b/testing/test.py @@ -1,6 +1,8 @@ import os import subprocess import pytest + +import pytest_timeout import tomllib # Default configurations @@ -11,6 +13,9 @@ LOCAL_SHARED_VOLUME = "./tests/" DOCKER_COMPOSE_FILE = "../docker/docker-compose.yml" +data_techniques = ["--no-dup", "--eddi", "--seddi", "--fdsc"] # "--reddi" +cfc_techniques = ["--no-cfc", "--cfcss", "--rasm"] #, "--racfed", "--inter-rasm"] + # Load the test configuration def load_config(): assert os.path.exists("../aspis.sh") and "Cannot find aspis.sh, please run the tests from the ASPIS testing directory as `pytest test.py`" @@ -35,6 +40,16 @@ def compile_with_aspis(source_file, output_file, options, llvm_bin, build_dir): raise RuntimeError(f"[{output_file}] Compilation failed: {stderr}") return stdout +# Compile without ASPIS to get expected output +def compile_without_aspis(source_file, output_file, llvm_bin, build_dir): + """Compile a file without ASPIS.""" + command = f"{llvm_bin}/clang++ {source_file} -o {build_dir}/{output_file}.out --verbose" + print(command) + stdout, stderr, exit_code = run_command(command) + if exit_code != 0: + raise RuntimeError(f"[{output_file}] Compilation failed: {stderr}") + return stdout + def execute_binary(local_build_dir, test_name): """Execute the compiled binary and return its output.""" binary_file = f"./{local_build_dir}/{test_name}.out"; @@ -59,15 +74,18 @@ def pytest_generate_tests(metafunc): ids = [t.get("test_name", str(i)) for i, t in enumerate(test_list)] metafunc.parametrize("test_data", test_list, ids=ids) + # Tests -def test_aspis(test_data, use_container, aspis_addopt): +@pytest.mark.timeout(60) # Set a timeout of 60 seconds for each test +@pytest.mark.parametrize("data_technique", data_techniques) +@pytest.mark.parametrize("cfc_technique", cfc_techniques) +def test_aspis(test_data, use_container, aspis_addopt, data_technique, cfc_technique): """Run a single ASPIS test.""" config = load_config() llvm_bin = config["llvm_bin"] test_name = test_data["test_name"] source_file = test_data["source_file"] - aspis_options = aspis_addopt + " " + test_data["aspis_options"] - expected_output = test_data["expected_output"] + # use docker compose rather than ASPIS if --use-container is set if use_container: ASPIS_SCRIPT = f"docker compose -f {DOCKER_COMPOSE_FILE} run --rm aspis_runner" @@ -78,14 +96,28 @@ def test_aspis(test_data, use_container, aspis_addopt): docker_build_dir = "./build/test/"+test_name local_build_dir = "./build/test/"+test_name + # Create build directory if it doesn't exist + os.makedirs(local_build_dir, exist_ok=True) + source_path = os.path.join(TEST_DIR, source_file) + if not os.path.exists(docker_build_dir + "/" + test_name + ".out"): + print("Compiling without ASPIS to get expected output...") + compile_without_aspis(source_path, test_name, llvm_bin, docker_build_dir) + + print("Executing binary compiled without ASPIS...") + expected_output = execute_binary(local_build_dir, test_name) + print(f"Expected output: {expected_output}") + + aspis_options = aspis_addopt + " " + data_technique + " " + cfc_technique + + test_name_complete = f"{test_name}_{data_technique}_{cfc_technique}" # Compile the source file - compile_with_aspis(source_path, test_name, aspis_options, llvm_bin, docker_build_dir) + compile_with_aspis(source_path, test_name_complete, aspis_options, llvm_bin, docker_build_dir) # Execute the binary and check output - result = execute_binary(local_build_dir, test_name) - assert result == expected_output, f"Test {test_name} failed: {result}" + result = execute_binary(local_build_dir, test_name_complete) + assert result == expected_output, f"Test {test_name_complete} failed: {result}" if __name__ == "__main__": pytest.main() diff --git a/testing/tests/c/control_flow/function_pointer.c b/testing/tests/c/control_flow/function_pointer.c index 7a3d13c..f7e8302 100644 --- a/testing/tests/c/control_flow/function_pointer.c +++ b/testing/tests/c/control_flow/function_pointer.c @@ -9,10 +9,20 @@ int foo() { return 42; } +int add(int a, int b) { + return a + b; +} + int main() { + // Simple function pointer call int (*fptr)() = foo; int result = fptr(); - printf("%d", result); + printf("%d\n", result); + + // Function pointer call with parameters + int (*addptr)(int, int) = add; + int sum = addptr(27, result); + printf("%d\n", sum); return 0; } diff --git a/testing/tests/c/data_duplication_integrity/global_var_across_functions.c b/testing/tests/c/data_duplication_integrity/global_var_across_functions.c index 40c3d2b..a02356a 100644 --- a/testing/tests/c/data_duplication_integrity/global_var_across_functions.c +++ b/testing/tests/c/data_duplication_integrity/global_var_across_functions.c @@ -4,7 +4,8 @@ #include -int __attribute__((annotate("to_duplicate"))) g = 0; +__attribute__((annotate("to_harden"))) +int g = 0; void increment() { g += 1; 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 0619c3c..3a31410 100644 --- a/testing/tests/c/data_duplication_integrity/misc_data_dup.c +++ b/testing/tests/c/data_duplication_integrity/misc_data_dup.c @@ -7,7 +7,7 @@ void DataCorruption_Handler(void) {} void SigMismatch_Handler(void) {} -__attribute__((annotate("to_duplicate"))) +__attribute__((annotate("to_harden"))) int duplicated_global = 100; __attribute__((annotate("exclude"))) @@ -17,7 +17,7 @@ int increment(int x) { return x + 1; } -__attribute__((annotate("to_duplicate"))) +__attribute__((annotate("to_harden"))) int multiply_by_two(int x) { return x * 2; } 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 5d83fac..4b6950d 100644 --- a/testing/tests/c/data_duplication_integrity/printf_to_duplicate.c +++ b/testing/tests/c/data_duplication_integrity/printf_to_duplicate.c @@ -1,6 +1,6 @@ #include -__attribute__((annotate("to_duplicate"))) +__attribute__((annotate("to_harden"))) void print_string(char *s) { printf("The string is: %s", s); } diff --git a/testing/tests/c/misc_math/xor_cypher.c b/testing/tests/c/misc_math/xor_cypher.c index 59aff92..1d219ef 100644 --- a/testing/tests/c/misc_math/xor_cypher.c +++ b/testing/tests/c/misc_math/xor_cypher.c @@ -12,7 +12,7 @@ void DataCorruption_Handler(void) {} void SigMismatch_Handler(void) {} // Global key -__attribute__((annotate("to_duplicate"))) +__attribute__((annotate("to_harden"))) unsigned char key = 0x5A; // Encrypt/decrypt function @@ -21,7 +21,7 @@ unsigned char xor_crypt(unsigned char data, unsigned char k) { } // Process buffer function -__attribute__((annotate("to_duplicate"))) +__attribute__((annotate("to_harden"))) void process_buffer(unsigned char *buf, size_t len, unsigned char k) { for (size_t i = 0; i < len; i++) { buf[i] = xor_crypt(buf[i], k); diff --git a/testing/tests/cpp/simple/add.cpp b/testing/tests/cpp/simple/add.cpp index c7d653a..9512744 100644 --- a/testing/tests/cpp/simple/add.cpp +++ b/testing/tests/cpp/simple/add.cpp @@ -1,5 +1,18 @@ #include +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } + + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } +} + 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 2d86302..6352bde 100644 --- a/testing/tests/cpp/simple/class.cpp +++ b/testing/tests/cpp/simple/class.cpp @@ -2,12 +2,13 @@ #include // Gestori di errore ASPIS +extern "C" void DataCorruption_Handler() { - std::cerr << "Errore ASPIS: Data corruption rilevata\n"; + std::cerr << "Errore ASPIS: Data corruption detected\n"; std::exit(1); } void SigMismatch_Handler() { - std::cerr << "Errore ASPIS: Signature mismatch rilevata\n"; + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; std::exit(1); } diff --git a/testing/tests/cpp/simple/exceptions.cpp b/testing/tests/cpp/simple/exceptions.cpp index 7a43fad..d773435 100644 --- a/testing/tests/cpp/simple/exceptions.cpp +++ b/testing/tests/cpp/simple/exceptions.cpp @@ -3,16 +3,17 @@ #include // ASPIS error handlers (non-duplicated) -__attribute__((no_duplicate)) -void DataCorruption_Handler() { - std::cerr << "ASPIS error: Data corruption detected\n"; - std::exit(1); -} +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } -__attribute__((no_duplicate)) -void SigMismatch_Handler() { - std::cerr << "ASPIS error: Signature mismatch detected\n"; - std::exit(1); + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } } // A leaf that always throws diff --git a/testing/tests/cpp/simple/fact.cpp b/testing/tests/cpp/simple/fact.cpp index a7382ba..bb46800 100644 --- a/testing/tests/cpp/simple/fact.cpp +++ b/testing/tests/cpp/simple/fact.cpp @@ -1,12 +1,12 @@ #include -extern "C" __attribute__((no_duplicate)) +extern "C" void DataCorruption_Handler() { std::cerr << "ASPIS error: Data corruption detected\n"; std::exit(EXIT_FAILURE); } -extern "C" __attribute__((no_duplicate)) +extern "C" void SigMismatch_Handler() { std::cerr << "ASPIS error: Signature mismatch detected\n"; std::exit(EXIT_FAILURE); diff --git a/testing/tests/cpp/simple/file_handler.cpp b/testing/tests/cpp/simple/file_handler.cpp index fa5d8a9..1332647 100644 --- a/testing/tests/cpp/simple/file_handler.cpp +++ b/testing/tests/cpp/simple/file_handler.cpp @@ -2,13 +2,13 @@ #include // ASPIS handlers -extern "C" __attribute__((no_duplicate)) +extern "C" void DataCorruption_Handler() { std::cerr << "Data corruption detected" << std::endl; std::exit(EXIT_FAILURE); } -extern "C" __attribute__((no_duplicate)) +extern "C" void SigMismatch_Handler() { std::cerr << "Signature mismatch detected" << std::endl; std::exit(EXIT_FAILURE); diff --git a/testing/tests/cpp/simple/func.cpp b/testing/tests/cpp/simple/func.cpp index 9598821..a61b2c2 100644 --- a/testing/tests/cpp/simple/func.cpp +++ b/testing/tests/cpp/simple/func.cpp @@ -1,21 +1,29 @@ #include // ASPIS error handlers (non-duplicated) -__attribute__((no_duplicate)) -void DataCorruption_Handler() { - std::cerr << "ASPIS error: Data corruption detected\n"; -} +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } -__attribute__((no_duplicate)) -void SigMismatch_Handler() { - std::cerr << "ASPIS error: Signature mismatch detected\n"; + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } } // Global variable used in the test int g = 0; // A function with a side effect on a global variable (non-duplicated) -__attribute__((no_duplicate)) +__attribute__((annotate("exclude"))) // Marked as non-duplicable +void fNonDup() { + g++; + std::cout << "Function fNonDup called (g incremented to " << g << ")" << std::endl; +} + void f() { g++; std::cout << "Function f called (g incremented to " << g << ")" << std::endl; @@ -23,6 +31,7 @@ void f() { int main() { f(); // Called once + fNonDup(); std::cout << "Final value of g: " << g << std::endl; return 0; } diff --git a/testing/tests/cpp/simple/heap.cpp b/testing/tests/cpp/simple/heap.cpp index 3d2a500..2786d73 100644 --- a/testing/tests/cpp/simple/heap.cpp +++ b/testing/tests/cpp/simple/heap.cpp @@ -1,14 +1,17 @@ #include // ASPIS error handlers (non-duplicated) -__attribute__((no_duplicate)) -void DataCorruption_Handler() { - std::cerr << "ASPIS error: Data corruption detected\n"; -} +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } -__attribute__((no_duplicate)) -void SigMismatch_Handler() { - std::cerr << "ASPIS error: Signature mismatch detected\n"; + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } } // A function that allocates memory, uses it, and then deletes it diff --git a/testing/tests/cpp/simple/inline_constexpr.cpp b/testing/tests/cpp/simple/inline_constexpr.cpp index f74c5f3..900fb3f 100644 --- a/testing/tests/cpp/simple/inline_constexpr.cpp +++ b/testing/tests/cpp/simple/inline_constexpr.cpp @@ -1,18 +1,19 @@ #include // ASPIS error handlers (non-duplicated) -__attribute__((no_duplicate)) -void DataCorruption_Handler() { - std::cerr << "ASPIS error: Data corruption detected\n"; +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } + + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } } -__attribute__((no_duplicate)) -void SigMismatch_Handler() { - std::cerr << "ASPIS error: Signature mismatch detected\n"; -} - -// Print function (non-duplicated) -__attribute__((no_duplicate)) 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 af08e3a..6c13f07 100644 --- a/testing/tests/cpp/simple/lambda_captures.cpp +++ b/testing/tests/cpp/simple/lambda_captures.cpp @@ -1,23 +1,31 @@ #include // ASPIS error handlers (non-duplicated) -__attribute__((no_duplicate)) -void DataCorruption_Handler() { - std::cerr << "ASPIS error: Data corruption detected\n"; -} +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } -__attribute__((no_duplicate)) -void SigMismatch_Handler() { - std::cerr << "ASPIS error: Signature mismatch detected\n"; + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } } // Executes a function/lambda in a non-duplicated context template -__attribute__((no_duplicate)) +__attribute__((annotate("exclude"))) // Marked as non-duplicable void runNoDup(F func) { func(); } +template +void run(F func) { + func(); +} + int main() { // Example 1: lambda capturing a local variable by reference int x = 0; @@ -34,6 +42,8 @@ int main() { }; runNoDup(incPtr); // non-duplicated increment of shared memory + run(incPtr); // duplicated increment of shared memory + std::cout << "Value pointed by p: " << *p << std::endl; delete p; return 0; diff --git a/testing/tests/cpp/simple/mul.cpp b/testing/tests/cpp/simple/mul.cpp index ee681a9..85c8cb5 100644 --- a/testing/tests/cpp/simple/mul.cpp +++ b/testing/tests/cpp/simple/mul.cpp @@ -1,13 +1,17 @@ #include #include -void DataCorruption_Handler() { - std::cerr << "Errore ASPIS: Data corruption rilevata\n"; - std::exit(1); -} -void SigMismatch_Handler() { - std::cerr << "Errore ASPIS: Signature mismatch rilevata\n"; - std::exit(1); +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } + + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } } int multiply(int a, int b) diff --git a/testing/tests/cpp/simple/ptr.cpp b/testing/tests/cpp/simple/ptr.cpp index 23bab0d..08436a0 100644 --- a/testing/tests/cpp/simple/ptr.cpp +++ b/testing/tests/cpp/simple/ptr.cpp @@ -2,18 +2,20 @@ #include // ASPIS error handlers (non-duplicated) -__attribute__((no_duplicate)) -void DataCorruption_Handler() { - std::cerr << "ASPIS error: Data corruption detected\n"; -} +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } -__attribute__((no_duplicate)) -void SigMismatch_Handler() { - std::cerr << "ASPIS error: Signature mismatch detected\n"; + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } } // Helper to print two pointer values (non-duplicated) -__attribute__((no_duplicate)) 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 96804b1..b19903d 100644 --- a/testing/tests/cpp/simple/stl_containers_advanced.cpp +++ b/testing/tests/cpp/simple/stl_containers_advanced.cpp @@ -2,15 +2,17 @@ #include #include -// ASPIS error handling functions -void DataCorruption_Handler() { - std::cerr << "Errore ASPIS: Data corruption rilevata\n"; - std::exit(EXIT_FAILURE); -} +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } -void SigMismatch_Handler() { - std::cerr << "Errore ASPIS: Signature mismatch rilevata\n"; - std::exit(EXIT_FAILURE); + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } } int main() { diff --git a/testing/tests/cpp/simple/template.cpp b/testing/tests/cpp/simple/template.cpp index efadd67..1b060b9 100644 --- a/testing/tests/cpp/simple/template.cpp +++ b/testing/tests/cpp/simple/template.cpp @@ -1,18 +1,21 @@ #include // ASPIS error handlers (non-duplicated) -__attribute__((no_duplicate)) -void DataCorruption_Handler() { - std::cerr << "ASPIS error: Data corruption detected\n"; -} +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } -__attribute__((no_duplicate)) -void SigMismatch_Handler() { - std::cerr << "ASPIS error: Signature mismatch detected\n"; + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } } // Print function (non-duplicated) -__attribute__((no_duplicate)) +__attribute__((annotate("exclude"))) void printResult(int value) { std::cout << "Result: " << value << std::endl; } diff --git a/testing/tests/cpp/simple/threads.cpp b/testing/tests/cpp/simple/threads.cpp index 28eaf55..5bc27ae 100644 --- a/testing/tests/cpp/simple/threads.cpp +++ b/testing/tests/cpp/simple/threads.cpp @@ -13,13 +13,13 @@ __attribute__((annotate("run_adj_sig"))) thread_local int run_adj_sig = -0xDEAD; // ASPIS error handlers -extern "C" __attribute__((no_duplicate)) +extern "C" void DataCorruption_Handler() { std::cerr << "ASPIS error: Data corruption detected\n"; std::exit(EXIT_FAILURE); } -extern "C" __attribute__((no_duplicate)) +extern "C" void SigMismatch_Handler() { std::cerr << "ASPIS error: Signature mismatch detected\n"; std::exit(EXIT_FAILURE); @@ -27,8 +27,6 @@ void SigMismatch_Handler() { std::atomic counter{0}; -// Worker function marked as non-duplicable due to atomic update -__attribute__((no_duplicate)) void worker(int times) { int local_count = 0; for (int i = 0; i < times; ++i) { @@ -37,8 +35,6 @@ void worker(int times) { counter.fetch_add(local_count, std::memory_order_relaxed); } -// Thread launching logic is marked non-duplicable -__attribute__((no_duplicate)) void run_all_threads(int num_threads, int work_per_thread) { for (int i = 0; i < num_threads; ++i) { std::thread t(worker, work_per_thread); diff --git a/testing/tests/cpp/simple/volatile_memory_order.cpp b/testing/tests/cpp/simple/volatile_memory_order.cpp index 2a4455c..de9a851 100644 --- a/testing/tests/cpp/simple/volatile_memory_order.cpp +++ b/testing/tests/cpp/simple/volatile_memory_order.cpp @@ -1,14 +1,17 @@ #include // ASPIS error handlers (never duplicated) -void DataCorruption_Handler() { - std::cerr << "Data corruption detected\n"; - std::exit(1); -} +extern "C" { + // ASPIS error handling functions + void DataCorruption_Handler() { + std::cerr << "Errore ASPIS: Data corruption detected\n"; + std::exit(EXIT_FAILURE); + } -void SigMismatch_Handler() { - std::cerr << "Signature mismatch detected\n"; - std::exit(1); + void SigMismatch_Handler() { + std::cerr << "Errore ASPIS: Signature mismatch detected\n"; + std::exit(EXIT_FAILURE); + } }