diff --git a/.gitignore b/.gitignore index 8851850..50f5d77 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ *.a *.lib +# IR files +*.ll + # Executables *.exe *.out @@ -62,4 +65,7 @@ examples/sha/*.csv examples/sha/sha examples/cuda/*.txt -examples/cuda/add \ No newline at end of file +examples/cuda/add + +# IDE +.idea/ diff --git a/aspis.sh b/aspis.sh index c0c92c3..d55c999 100755 --- a/aspis.sh +++ b/aspis.sh @@ -19,7 +19,8 @@ input_files="" clang_options= eddi_options="-S" cfc_options="-S" -llvm_bin=$(dirname $(which clang &> /dev/null) &> /dev/null) +llvm_bin=$(dirname "$(which clang)") +suffix="" build_dir="." dup=0 # 0 = eddi, 1 = seddi, 2 = fdsc cfc=0 # 0 = cfcss, 1 = rasm, 2 = inter-rasm @@ -65,15 +66,15 @@ title_msg () { perform_platform_checks() { if [ ! -f $1 ]; then - error_msg "\nCommand clang not found. Expected path: ${1}. Please check --llvm_bin parameter." + error_msg "\nCommand clang not found. Expected path: ${1}. Please check --llvm-bin parameter." fi if [ ! -f $2 ]; then - error_msg "\nCommand opt not found. Expected path: ${2}. Please check --llvm_bin parameter." + error_msg "\nCommand opt not found. Expected path: ${2}. Please check --llvm-bin parameter." fi if [ ! -f $3 ]; then - error_msg "\nCommand llvm-link not found. Expected path: ${3}. Please check --llvm_bin parameter." + error_msg "\nCommand llvm-link not found. Expected path: ${3}. Please check --llvm-bin parameter." fi } @@ -112,12 +113,14 @@ parse_commands() { -o Write the compilation output to . --build-dir Specify the directory where to place all the build files. - --llvm-bin Set the path to the llvm binaries (clang, opt, + --llvm-bin Set the path to the llvm binaries (clang, opt, llvm-link) to . - --exclude Set the files to exclude from the compilation. The + --suffix Set the suffix of the binary to use (clang, opt, + llvm-link) to . + --exclude Set the files to exclude from the compilation. The content of is the list of files to exclude, one for each line (wildcard * allowed). - --asmfiles Defines the set of assembly files required for the + --asmfiles Defines the set of assembly files required for the compilation. The content of is the list of assembly files to pass to the linker at compilation termination, one for each line (wildcard * allowed). @@ -128,12 +131,13 @@ parse_commands() { --eddi (Default) Enable EDDI. --seddi Enable Selective-EDDI. --fdsc Enable Full Duplication with Selective Checking. - --no-dup Completely disable data duplication + --no-dup Completely disable data duplication. --cfcss (Default) Enable CFCSS. --rasm Enable RASM. --inter-rasm Enable inter-RASM with the default signature -0xDEAD. - --no-cfc Completely disable control-flow checking + --racfed Enable RACFED. + --no-cfc Completely disable control-flow checking. Hardening options: --alternate-memmap When set, alternates the definition of original and @@ -155,16 +159,25 @@ EOF if [[ ${#opt} -eq 2 ]]; then parse_state=1; else - output_file=`echo "$opt" | cut -b 2`; + output_file=${opt##"-o"}; fi; ;; --llvm-bin*) + echo ${#opt} if [[ ${#opt} -eq 10 ]]; then parse_state=3; else - llvm_bin=`echo "$opt" | cut -b 10`; + llvm_bin=${opt##"--llvm-bin="}; fi; ;; + --suffix*) + if [[ ${#opt} -eq 8 ]]; then + parse_state=7; + else + suffix='-'; + suffix+=${opt##"--suffix="}; + fi; + ;; --exclude*) if [[ ${#opt} -eq 9 ]]; then parse_state=4; @@ -207,6 +220,9 @@ EOF --inter-rasm) cfc=2 ;; + --racfed) + cfc=3 + ;; --no-cfc) cfc=-1 ;; @@ -267,6 +283,10 @@ EOF build_dir="$opt"; parse_state=0; ;; + 7) + suffix="$opt"; + parse_state=0; + ;; esac done @@ -291,9 +311,9 @@ EOF } fi - CLANG="${llvm_bin}/clang" - OPT="${llvm_bin}/opt" - LLVM_LINK="${llvm_bin}/llvm-link" + CLANG="${llvm_bin}/clang${suffix}" + OPT="${llvm_bin}/opt${suffix}" + LLVM_LINK="${llvm_bin}/llvm-link${suffix}" if [[ -n "$config_file" ]]; then CLANG="${CLANG} --config ${config_file}" @@ -319,19 +339,19 @@ run_aspis() { done ## LINK & PREPROCESS - exe $LLVM_LINK $build_dir/*.ll -o $build_dir/out.ll -opaque-pointers + exe $LLVM_LINK $build_dir/*.ll -o $build_dir/out.ll success_msg "Emitted and linked IR." if [[ $debug_enabled == false ]]; then - exe $OPT --enable-new-pm=1 --passes="strip" $build_dir/out.ll -o $build_dir/out.ll + exe $OPT --passes="strip" $build_dir/out.ll -o $build_dir/out.ll echo " Debug mode disabled, stripped debug symbols." fi - - exe $OPT --enable-new-pm=1 --passes="lowerswitch" $build_dir/out.ll -o $build_dir/out.ll + + exe $OPT --passes="lower-switch" $build_dir/out.ll -o $build_dir/out.ll ## FuncRetToRef - if [[ dup != -1 ]]; then + if [[ dup -ne -1 ]]; then exe $OPT --enable-new-pm=1 -load-pass-plugin=$DIR/build/passes/libEDDI.so --passes="func-ret-to-ref" $build_dir/out.ll -o $build_dir/out.ll fi; @@ -352,19 +372,21 @@ run_aspis() { esac success_msg "Applied data protection passes." - exe $OPT --enable-new-pm=1 --passes="simplifycfg" $build_dir/out.ll -o $build_dir/out.ll - + exe $OPT --passes="simplifycfg" $build_dir/out.ll -o $build_dir/out.ll ## CONTROL-FLOW CHECKING case $cfc in 0) - exe $OPT --enable-new-pm=1 -load-pass-plugin=$DIR/build/passes/libCFCSS.so --passes="cfcss-verify" $build_dir/out.ll -o $build_dir/out.ll $cfc_options + exe $OPT -load-pass-plugin=$DIR/build/passes/libCFCSS.so --passes="cfcss-verify" $build_dir/out.ll -o $build_dir/out.ll $cfc_options ;; 1) - exe $OPT --enable-new-pm=1 -load-pass-plugin=$DIR/build/passes/libRASM.so --passes="rasm-verify" $build_dir/out.ll -o $build_dir/out.ll $cfc_options + exe $OPT -load-pass-plugin=$DIR/build/passes/libRASM.so --passes="rasm-verify" $build_dir/out.ll -o $build_dir/out.ll $cfc_options ;; 2) - exe $OPT --enable-new-pm=1 -load-pass-plugin=$DIR/build/passes/libINTER_RASM.so --passes="rasm-verify" $build_dir/out.ll -o $build_dir/out.ll $cfc_options + exe $OPT -load-pass-plugin=$DIR/build/passes/libINTER_RASM.so --passes="rasm-verify" $build_dir/out.ll -o $build_dir/out.ll $cfc_options + ;; + 3) + exe $OPT -load-pass-plugin=$DIR/build/passes/libRACFED.so --passes="racfed-verify" $build_dir/out.ll -o $build_dir/out.ll $cfc_options ;; *) echo -e "\t--no-cfc specified!" @@ -393,8 +415,8 @@ run_aspis() { success_msg "Linked excluded files to the compilation." ## DuplicateGlobals - if [[ dup != -1 ]]; then - exe $OPT --enable-new-pm=1 -load-pass-plugin=$DIR/build/passes/libEDDI.so --passes="duplicate-globals" $build_dir/out.ll -o $build_dir/out.ll -S $eddi_options + if [[ dup -ne -1 ]]; then + exe $OPT -load-pass-plugin=$DIR/build/passes/libEDDI.so --passes="duplicate-globals" $build_dir/out.ll -o $build_dir/out.ll -S $eddi_options success_msg "Duplicated globals." fi; @@ -411,7 +433,7 @@ run_aspis() { exe $OPT $build_dir/out.ll -o $build_dir/out.ll -S $opt_flags if [[ "$enable_profiling" == "true" ]]; then title_msg "ASPIS Profiling" - exe $OPT --enable-new-pm=1 -load-pass-plugin=$DIR/build/passes/libPROFILER.so --passes="aspis-insert-check-profile" $build_dir/out.ll -o $build_dir/out.ll -S + exe $OPT -load-pass-plugin=$DIR/build/passes/libPROFILER.so --passes="aspis-insert-check-profile" $build_dir/out.ll -o $build_dir/out.ll -S success_msg "Code instrumented." exe $CLANG $clang_options $build_dir/out.ll $asm_files -o $build_dir/$output_file @@ -421,7 +443,7 @@ run_aspis() { success_msg "Profiled code executed." echo -e "Analyzing..." - exe $OPT --enable-new-pm=1 -load-pass-plugin=$DIR/build/passes/libPROFILER.so --passes="aspis-check-profile" $build_dir/out.ll -o $build_dir/out.ll -S + exe $OPT -load-pass-plugin=$DIR/build/passes/libPROFILER.so --passes="aspis-check-profile" $build_dir/out.ll -o $build_dir/out.ll -S exit fi; @@ -437,6 +459,6 @@ run_aspis() { success_msg "Done!" } -parse_commands $@ +parse_commands "$@" perform_platform_checks $CLANG $OPT $LLVM_LINK -run_aspis \ No newline at end of file +run_aspis diff --git a/passes/ASPIS.h b/passes/ASPIS.h index 4117034..c799e77 100644 --- a/passes/ASPIS.h +++ b/passes/ASPIS.h @@ -19,7 +19,7 @@ using namespace llvm; // DATA PROTECTION class FuncRetToRef : public PassInfoMixin { private: - Function* updateFnSignature(Function &Fn, Module &Md); + void updateFnSignature(Function &Fn, Module &Md); void updateRetInstructions(Function &Fn); void updateFunctionCalls(Function &Fn, Function &NewFn); @@ -156,4 +156,46 @@ class RASM : public PassInfoMixin { }; -#endif \ No newline at end of file +class RACFED : public PassInfoMixin { +private: + std::map FuncAnnotations; + std::map NewBBs; + std::unordered_map compileTimeSig; + std::unordered_map subRanPrevVals; + std::unordered_map sumIntraInstruction; + + +#if (LOG_COMPILED_FUNCS == 1) + std::set CompiledFuncs; +#endif + + // -- INITIALIZE BLOCKS -- + void initializeBlocksSignatures(Function &Fn); + // -- UPDATE COMPILE SIG RANDOM -- + void updateCompileSigRandom(Function &Fn, + GlobalVariable *RuntimeSigGV, Type *IntType); + + // --- CHECK BLOCKS AT JUMP END --- + void checkJumpSignature(BasicBlock &BB, + GlobalVariable *RuntimeSigGV, Type *IntType, + BasicBlock &ErrBB); + + Value *getCondition(Instruction &I); + + // --- UPDATE BRANCH SIGNATURE BEFORE JUMP --- + void checkBranches(Module &Md, BasicBlock &BB, GlobalVariable *RuntimeSigGV, + Type *IntType); + + // --- UPDATE RETURN VALUE AND CHECK --- + Instruction *checkReturnValue(BasicBlock &BB, + GlobalVariable *RuntimeSigGV, + Type *IntType, BasicBlock &ErrBB, + Value *BckupRunSig); + +public: + PreservedAnalyses run(Module &Md, ModuleAnalysisManager &); + + static bool isRequired() { return true; } +}; + +#endif diff --git a/passes/CMakeLists.txt b/passes/CMakeLists.txt index b522814..691f0ac 100644 --- a/passes/CMakeLists.txt +++ b/passes/CMakeLists.txt @@ -49,6 +49,12 @@ add_library(INTER_RASM SHARED ) target_compile_definitions(INTER_RASM PRIVATE INTRA_FUNCTION_CFC=1) +# RACFED +add_library(RACFED SHARED + RACFED.cpp + Utils/Utils.cpp +) + add_library(PROFILER SHARED Profiling/ASPISCheckProfiler.cpp Utils/Utils.cpp diff --git a/passes/RACFED.cpp b/passes/RACFED.cpp new file mode 100644 index 0000000..ee7fb15 --- /dev/null +++ b/passes/RACFED.cpp @@ -0,0 +1,519 @@ +#include "ASPIS.h" +#include "Utils/Utils.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" + +#include + +#define INIT_SIGNATURE \ + -0xDEAD // The same value has to be used as initializer for the signatures in + // the code + +#define MARTI_DEBUG true + +using namespace llvm; + +/** + * initializeBlockSignatures +1:for all Basic Block (BB) in CFG do +2: repeat compileTimeSig ← random number +3: until compileTimeSig is unique +4: repeat subRanPrevVal ← random number +5: until (compileTimeSig + subRanP revV al) is unique + * updateSignatureRandom +6:for all BB in CFG do +7: if NrInstrBB > 2 then +8: for all original instructions insert after +9: signature ← signature + random number + * checkCompileTimeSigAtJump +10:for all BB in CFG insert at beginning +11: signature ← signature − subRanPrevVal +12: if signature != compileTimeSig error() +13:for all BB in CFG do + * checkRetVal +14: if Last Instr. is return instr. and NrIntrBB > 1 then +15: Calculate needed variables +16: return Val ← random number +17: adjust Value ← (compileTimeSigBB + SumIntraInstructions) - +18: return Val +19: Insert signature update before return instr. +20: signature ← signature + adjustValue +21: if signature != returnVal error() +22: else + * checkJump +23: for all Successor of BB do +24: adjustValue ← (compileTimeSigBB + \Sum{instrMonUpdates}) - +25: (compileTimeSigsuccs + subRanPrevValsuccs) +26: Insert signature update at BB end +27: signature ← signature + adjustValue +*/ + +std::uniform_int_distribution dist32(1, 0x7fffffff); +std::uniform_int_distribution dist64(1, 0xffffffff); +std::mt19937 rng64(0x5EED00); // constant seed for reproducibility + +// --- INITIALIZE BLOCKS RANDOM --- +bool isNotUniqueCompileTimeSig( + const uint32_t bb_num, + const std::unordered_map &compileTimeSig +) { + for (const auto &[_, other_bb_id] : compileTimeSig) { + if ( other_bb_id == bb_num ) return true; + } + return false; +} + +bool isNotUnique( + const uint32_t current_id, + const std::unordered_map &RandomNumBBs, + const std::unordered_map &SubRanPrevVals +) { + for (const auto &[other_bb, other_bb_num] : RandomNumBBs) { + uint32_t other_id = static_cast(other_bb_num) + SubRanPrevVals.at(other_bb); + if ( other_id == current_id ) { + return true; + } + } + + return false; +} + +void RACFED::initializeBlocksSignatures(Function &Fn) { + std::mt19937 rng(0xB00BA5); // constant seed for reproducibility + uint32_t randomBB; + uint32_t randomSub; + + for (BasicBlock &BB : Fn) { + do { + randomBB = dist32(rng); + } while ( isNotUniqueCompileTimeSig(randomBB, compileTimeSig) ); + + do { + randomSub = dist32(rng); + } while ( isNotUnique( + randomBB + randomSub, + compileTimeSig, + subRanPrevVals) ); + + compileTimeSig.insert(std::pair(&BB, randomBB)); + subRanPrevVals.insert(std::pair(&BB, randomSub)); + } +} + + +// --- UPDATE SIGNATURE RANDOM --- +void originalInstruction(BasicBlock &BB, std::vector &OrigInstructions) { + for (Instruction &I : BB) { + if (isa(&I)) continue; // NON è originale + if (I.isTerminator()) continue; // NON è originale + if (isa(&I)) continue; // debug, ignora OrigInstructions.push_back(&I); + OrigInstructions.push_back(&I); + } +} + +void RACFED::updateCompileSigRandom(Function &Fn, + GlobalVariable *RuntimeSigGV, + Type *IntType) { + std::mt19937 rng(0xC0FFEE); // seed fisso per riproducibilità + + for (auto &BB: Fn){ + std::vector OrigInstructions; + originalInstruction(BB, OrigInstructions); + + if (OrigInstructions.size() <= 2) continue; + + uint64_t partial_sum = 0; + + for (Instruction *I : OrigInstructions) { + Instruction *InsertPt = nullptr; + + // Non puoi inserire "dopo" un terminator: inserisci prima del terminator stesso + if (I->isTerminator()) { + InsertPt = I; // insert BEFORE terminator + } else { + InsertPt = I->getNextNode(); // insert BEFORE next instruction (equivale a "dopo I") + } + + IRBuilder<> B(InsertPt); + + // signature = signature + randomConstant + uint64_t K = dist32(rng); + partial_sum += K; + + Value *Sig = B.CreateLoad(IntType, RuntimeSigGV); + Value *NewSig = B.CreateAdd(Sig, ConstantInt::get(IntType, K), "sig_add"); + B.CreateStore(NewSig, RuntimeSigGV); + sumIntraInstruction[&BB] = partial_sum; + } + } +} + +// --- CHECK BLOCKS AT JUMP END --- +// FIXME: Fix warnings +void RACFED::checkJumpSignature(BasicBlock &BB, + GlobalVariable *RuntimeSigGV, Type *IntType, + BasicBlock &ErrBB) { + if( BB.isEntryBlock() ) return; + + // in this case BB is not the first Basic Block of the function, so it has + // to update RuntimeSig and check it + Instruction *FirstNonPHI = BB.getFirstNonPHI(); + if ( (FirstNonPHI && isa(FirstNonPHI)) || + BB.getName().contains_insensitive("verification") ) { + + if (BB.getFirstInsertionPt() == BB.end()) return; // Skip empty/invalid blocks + + int randomNumberBB = compileTimeSig.find(&BB)->second; + IRBuilder<> BChecker(&*BB.getFirstInsertionPt()); + BChecker.CreateStore(llvm::ConstantInt::get(IntType, randomNumberBB), RuntimeSigGV, true); + } else if (!BB.getName().contains_insensitive("errbb")) { + int randomNumberBB = compileTimeSig.find(&BB)->second; + int subRanPrevVal = subRanPrevVals.find(&BB)->second; + BasicBlock *NewBB = BasicBlock::Create( + BB.getContext(), "RACFED_Verification_BB", BB.getParent(), &BB); + IRBuilder<> BChecker(NewBB); + + // add instructions for the first runtime signature update + Value *InstrRuntimeSig = + BChecker.CreateLoad(IntType, RuntimeSigGV, true); + + Value *RuntimeSignatureVal = BChecker.CreateSub( + InstrRuntimeSig, llvm::ConstantInt::get(IntType, subRanPrevVal)); + BChecker.CreateStore(RuntimeSignatureVal, RuntimeSigGV, true); + + // update phi placing them in the new block + while (isa(&BB.front())) { + Instruction *PhiInst = &BB.front(); + PhiInst->removeFromParent(); + PhiInst->insertBefore(&NewBB->front()); + } + + // replace the uses of BB with NewBB + BB.replaceAllUsesWith(NewBB); + + // Fix PHI nodes in successors + // replaceAllUsesWith updates PHI nodes in successors to point to NewBB, + // but the actual control flow is NewBB -> BB -> Succ, so Succ still sees BB + // as predecessor. + for (BasicBlock *Succ : successors(&BB)) { + for (PHINode &Phi : Succ->phis()) { + for (unsigned i = 0; i < Phi.getNumIncomingValues(); ++i) { + if (Phi.getIncomingBlock(i) == NewBB) { + Phi.setIncomingBlock(i, &BB); + } + } + } + } + + // add instructions for checking the runtime signature + Value *CmpVal = + BChecker.CreateCmp(llvm::CmpInst::ICMP_EQ, RuntimeSignatureVal, + llvm::ConstantInt::get(IntType, randomNumberBB)); + BChecker.CreateCondBr(CmpVal, &BB, &ErrBB); + + // add NewBB and BB into the NewBBs map + NewBBs.insert(std::pair(NewBB, &BB)); + + // Map NewBB to the same signature requirements as BB so predecessors can + // target it correctly + compileTimeSig[NewBB] = randomNumberBB; + subRanPrevVals[NewBB] = subRanPrevVal; + } +} + +// --- UPDATE BRANCH SIGNATURE BEFORE JUMP --- +Constant* expectedSignature( + BasicBlock *Succ, Type *IntType, + const std::unordered_map &compileTimeSig, + const std::unordered_map &subRanPrevVals +) { + const int expected = compileTimeSig.at(Succ); + const int expectedSub = subRanPrevVals.at(Succ); + return ConstantInt::get(IntType, expected+expectedSub); +} + +Value *RACFED::getCondition(Instruction &I) { + if (isa(I) && cast(I).isConditional()) { + if (!cast(I).isConditional()) { + return nullptr; + } else { + return cast(I).getCondition(); + } + } else if (isa(I)) { + errs() << "There is a switch!\n"; + abort(); + return cast(I).getCondition(); + } else { + assert(false && "Tried to get a condition on a function that is not a " + "branch or a switch"); + } +} + +static void printSig(Module &Md, IRBuilder<> &B, Value *SigVal, const char *Msg) { + LLVMContext &Ctx = Md.getContext(); + + // int printf(const char*, ...) + FunctionCallee Printf = Md.getOrInsertFunction( + "printf", + FunctionType::get(IntegerType::getInt32Ty(Ctx), + PointerType::getUnqual(Ctx), + true)); + + // Crea stringa globale "Msg: %ld\n" + std::string Fmt = std::string(Msg) + ": %ld\n"; + Value *FmtStr = B.CreateGlobalStringPtr(Fmt); + + + if (SigVal->getType()->isIntegerTy(32)) { + SigVal = B.CreateZExt(SigVal, Type::getInt64Ty(Ctx)); + } + + B.CreateCall(Printf, {FmtStr, SigVal}); +} + +/* +* checkBranches +23: for all Successor of BB do +24: adjustValue ← (compileTimeSigBB + \Sum{instrMonUpdates}) - +25: (compileTimeSigsuccs + subRanPrevValsuccs) +26: Insert signature update at BB end +27: signature ← signature + adjustValue +*/ +void RACFED::checkBranches(Module &Md, BasicBlock &BB, GlobalVariable *RuntimeSigGV, + Type *IntType) { + Instruction *Term = BB.getTerminator(); + IRBuilder<> B(&BB); + B.SetInsertPoint(Term); + auto *BI = dyn_cast(Term); + if (!BI) return; + + //TODO: check this + + // Calculate Source Static Signature: CT_BB + SumIntra + uint64_t SourceStatic = + static_cast(compileTimeSig[&BB]) + sumIntraInstruction[&BB]; + + Value *Current = B.CreateLoad(IntType, RuntimeSigGV, "current"); + printSig(Md, B, Current, "current"); + + //TODO: until here + + //define if conditional or unconditional branch + //Conditional: expected= CT_succ+subRan_succ + //adj = CTB-exp--> new signature = RT -adj + if ( BI->isUnconditional() ) { // only one successor + BasicBlock *Succ = BI->getSuccessor(0); + uint64_t Expected = + static_cast(compileTimeSig[Succ] + subRanPrevVals[Succ]); + // adj = expected - current + uint64_t AdjValue = Expected - SourceStatic; + Value *Adj = ConstantInt::get(IntType, AdjValue); + Value *NewSig = B.CreateAdd(Current, Adj, "racfed_newsig"); + B.CreateStore(NewSig, RuntimeSigGV); + printSig(Md,B, NewSig, "newsig"); + + return; + } + + if ( BI-> isConditional()) { + BasicBlock *SuccT = BI->getSuccessor(0); + BasicBlock *SuccF = BI->getSuccessor(1); + Instruction *Terminator = BB.getTerminator(); + Value *BrCondition = getCondition(*Terminator); + + // Target T + uint64_t expectedT = + static_cast(compileTimeSig[SuccT] + subRanPrevVals[SuccT]); + uint64_t adj1 = expectedT - SourceStatic; + + // Target F + uint64_t expectedF = + static_cast(compileTimeSig[SuccF] + subRanPrevVals[SuccF]); + uint64_t adj2 = expectedF - SourceStatic; + + Value *Adj = B.CreateSelect(BrCondition, ConstantInt::get(IntType, adj1), ConstantInt::get(IntType, adj2)); + Value *NewSig = B.CreateAdd(Current, Adj, "racfed_newsig"); + B.CreateStore(NewSig, RuntimeSigGV); + + printSig(Md, B, NewSig, "SIG after cond"); + } +} + + + +// --- CHECK RETURN UPDATE VALUE --- +// checkRetVal: +// +// 14: if Last Instr. is return instr. and NrIntrBB > 1 then +// 15: Calculate needed variables +// 16: returnVal ← random number +// 17: adjustValue ← (compileTimeSigBB + Sum) - +// 18: returnVal +// 19: Insert signature update before return instr. +// 20: signature ← signature + adjustValue +// 21: if signature != returnVal error() +Instruction *RACFED::checkReturnValue(BasicBlock &BB, + GlobalVariable *RuntimeSigGV, + Type* IntType, BasicBlock &ErrBB, + Value *BckupRunSig) { + Instruction *Term = BB.getTerminator(); + + if( !isa(Term) ) return nullptr; + + std::vector org_instr; + originalInstruction(BB, org_instr); + if( org_instr.size() > 2 ) { + + // Splits the BB that contains the return instruction into + // two basic blocks: + // BB will contain the return instruction + // BeforeRetBB will contain all of the instructions before the return one + // + // These two BBs will be linked meaning that BeforeRetBB->successor == BB + BasicBlock *BeforeRetBB = BB.splitBasicBlockBefore(Term); + + // Creating control basic block to insert before + // the return instruction + BasicBlock *ControlBB = BasicBlock::Create( + BB.getContext(), + "RAFCED_ret_verification_BB", + BB.getParent(), + &BB + ); + + // Relinking the basic blocks so that the structure + // results in: BeforeRetBB->ControlBB->BB + BeforeRetBB->getTerminator()->replaceSuccessorWith(&BB, ControlBB); + + // Inserting instructions into ControlBB + IRBuilder<> ControlIR(ControlBB); + // 15: Calculate needed variables + // 16: returnVal ← random number + // 17: adjustValue ← (compileTimeSigBB + Sum) - + // 18: returnVal + uint64_t random_ret_value = dist64(rng64); + // This is a 64 bit SIGNED integer (cause a subtraction happens + // and it cannot previously be established that it will be positive) + long int adj_value = compileTimeSig[&BB] + sumIntraInstruction[&BB] + - random_ret_value; + + // 19: Insert signature update before return instr. + // 20: signature ← signature + adjustValue // wrong must be subtracted + // 21: if signature != returnVal error() + Value *Sig = ControlIR.CreateLoad(IntType, RuntimeSigGV, true, "checking_sign"); + Value *CmpVal = ControlIR.CreateSub(Sig, llvm::ConstantInt::get(IntType, adj_value), "checking_value"); + Value *CmpSig = ControlIR.CreateCmp(llvm::CmpInst::ICMP_EQ, CmpVal, + llvm::ConstantInt::get(IntType, random_ret_value)); + + ControlIR.CreateCondBr(CmpSig, &BB, &ErrBB); + } + + return Term; +} + +PreservedAnalyses RACFED::run(Module &Md, ModuleAnalysisManager &AM) { + // mappa: istruzione originale -> random r usato per S = S + r + // std::unordered_map InstrUpdates; + + auto *I64 = llvm::Type::getInt64Ty(Md.getContext()); + + GlobalVariable *RuntimeSig = new GlobalVariable( + Md, I64, + /*isConstant=*/false, + GlobalValue::ExternalLinkage, + ConstantInt::get(I64, 0), + "signature" + ); + Instruction *RetInst = nullptr; + + createFtFuncs(Md); + getFuncAnnotations(Md, FuncAnnotations); + LinkageMap linkageMap = mapFunctionLinkageNames((Md)); + + for (Function &Fn : Md) { + if (shouldCompile(Fn, FuncAnnotations)) + initializeBlocksSignatures(Fn); + } + + for (Function &Fn: Md) { + if (Fn.isDeclaration() || Fn.empty()) continue; + updateCompileSigRandom(Fn, RuntimeSig, I64); + } + + for(Function &Fn: Md) { + if(!shouldCompile(Fn, FuncAnnotations)) continue; + + #if MARTI_DEBUG + errs() << "Analysing func " << Fn.getName() << "\n"; + #endif + + // Search debug location point + DebugLoc debugLoc; + for (auto &I : Fn.front()) { + if (I.getDebugLoc()) { + debugLoc = I.getDebugLoc(); + break; + } + } + + // Create error basic block + assert(!getLinkageName(linkageMap,"SigMismatch_Handler").empty() + && "Function SigMismatch_Handler is missing!"); + + BasicBlock *ErrBB = BasicBlock::Create(Fn.getContext(), "ErrBB", &Fn); + auto CalleeF = ErrBB->getModule()->getOrInsertFunction( + getLinkageName(linkageMap,"SigMismatch_Handler"), + FunctionType::getVoidTy(Md.getContext()) + ); + + IRBuilder<> ErrIR(ErrBB); + ErrIR.CreateCall(CalleeF)->setDebugLoc(debugLoc); + ErrIR.CreateUnreachable(); + + Value * runtime_sign_bkup = nullptr; + for (BasicBlock &BB : Fn) { + // TODO: Should the error basic block that is inserted be checked? + + // Backup of compile time sign when entering a function + if( BB.isEntryBlock() ) { + IRBuilder<> InstrIR(&*BB.getFirstInsertionPt()); + runtime_sign_bkup = + InstrIR.CreateLoad(I64, RuntimeSig, true, "backup_run_sig"); + InstrIR.CreateStore(llvm::ConstantInt::get(I64, compileTimeSig[&BB]), + RuntimeSig); + } + + checkJumpSignature(BB, RuntimeSig, I64, *ErrBB); + RetInst = checkReturnValue(BB, RuntimeSig, I64, *ErrBB, runtime_sign_bkup); + checkBranches(Md, BB, RuntimeSig, I64); + + // Restore signature on return + if( RetInst != nullptr ) { + IRBuilder<> RetInstIR(RetInst); + RetInstIR.CreateStore(runtime_sign_bkup, RuntimeSig); + } + } + } + + // There is nothing that this pass preserved + return PreservedAnalyses::none(); +} + +extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK + +llvmGetPassPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "RACFED", "v0.1", [](PassBuilder &PB) { + PB.registerPipelineParsingCallback( + [](StringRef Name, ModulePassManager &MPM, + ArrayRef) { + if (Name == "racfed-verify") { + MPM.addPass(RACFED()); + return true; + } + return false; + }); + }}; +} + diff --git a/passes/RASM.cpp b/passes/RASM.cpp index 36e8714..10e9ca4 100644 --- a/passes/RASM.cpp +++ b/passes/RASM.cpp @@ -264,7 +264,7 @@ void RASM::createCFGVerificationBB ( BasicBlock &BB, * 3) more than three successors -> we have a switch: impossible since we lower switches in a previous pass */ int numSuccessors = Terminator->getNumSuccessors(); - if (numSuccessors>1 and Terminator->isExceptionalTerminator()){ + if (numSuccessors>1 and Terminator->isSpecialTerminator()){ numSuccessors=1; } switch (numSuccessors) @@ -348,10 +348,10 @@ PreservedAnalyses RASM::run(Module &Md, ModuleAnalysisManager &AM) { // find the global variables required for the runtime signatures for (GlobalVariable &GV : Md.globals()) { if (!isa(GV) && FuncAnnotations.find(&GV) != FuncAnnotations.end()) { - if ((FuncAnnotations.find(&GV))->second.startswith("runtime_sig")) { + if ((FuncAnnotations.find(&GV))->second.starts_with("runtime_sig")) { RuntimeSig = &GV; } - else if ((FuncAnnotations.find(&GV))->second.startswith("run_adj_sig")) { + else if ((FuncAnnotations.find(&GV))->second.starts_with("run_adj_sig")) { RetSig = &GV; } } diff --git a/passes/Utils/Utils.cpp b/passes/Utils/Utils.cpp index 9584f55..8fea5b8 100644 --- a/passes/Utils/Utils.cpp +++ b/passes/Utils/Utils.cpp @@ -5,10 +5,8 @@ #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" -#include "llvm/IR/Metadata.h" #include "llvm/Pass.h" #include "llvm/Support/raw_ostream.h" -#include #include #include #include @@ -117,7 +115,7 @@ bool shouldCompile(Function &Fn, !Fn.getName().contains("aspis.syncpt") // Moreover, it does not have to be marked as excluded or to_duplicate && (FuncAnnotations.find(&Fn) == FuncAnnotations.end() || - (!FuncAnnotations.find(&Fn)->second.startswith("exclude") /* && + (!FuncAnnotations.find(&Fn)->second.starts_with("exclude") /* && !FuncAnnotations.find(&Fn)->second.startswith("to_duplicate") */)) // nor it is one of the original functions && OriginalFunctions.find(&Fn) == OriginalFunctions.end(); diff --git a/passes/Utils/Utils.h b/passes/Utils/Utils.h index c34824c..f0c5d37 100644 --- a/passes/Utils/Utils.h +++ b/passes/Utils/Utils.h @@ -1,16 +1,14 @@ #ifndef UTILS_H #define UTILS_H -#include "llvm/ADT/Statistic.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" -#include "llvm/IR/Metadata.h" -#include "llvm/Pass.h" -#include "llvm/Support/raw_ostream.h" +#include "llvm/IR/Module.h" #include #include +#include using namespace llvm; using LinkageMap = std::unordered_map>; diff --git a/testing/tests/c/multi_instruction/add.c b/testing/tests/c/multi_instruction/add.c new file mode 100644 index 0000000..9fc27b7 --- /dev/null +++ b/testing/tests/c/multi_instruction/add.c @@ -0,0 +1,12 @@ +// +// Created by Martina Starone on 24/12/25. +// +#include + +int main() { + int a = 10; + int b = 20; + int c = a + b; + printf("c=%d\n", c); + return 0; +} diff --git a/testing/tests/c/multi_instruction/call_less_two.c b/testing/tests/c/multi_instruction/call_less_two.c new file mode 100644 index 0000000..f599d37 --- /dev/null +++ b/testing/tests/c/multi_instruction/call_less_two.c @@ -0,0 +1,14 @@ +#include + +int foo() { + return 0; +} + +int main() { + int sasso_carta = 1; + int filippo_congenito = 2; + for(int i = foo(); i < sasso_carta + filippo_congenito; i++) + filippo_congenito--; + printf("%d", filippo_congenito); + return 0; +} diff --git a/testing/tests/c/multi_instruction/func_ret.c b/testing/tests/c/multi_instruction/func_ret.c new file mode 100644 index 0000000..494adfd --- /dev/null +++ b/testing/tests/c/multi_instruction/func_ret.c @@ -0,0 +1,28 @@ +#include +#include + +void SigMismatch_Handler(void) { + printf("An error occurred and the signature value was different from expected"); + exit(-1); +} + +int foo(); +void print(int c); + +int main() { + int a = 10; + int b = 20; + int c = foo(); + print(c); + return a > b ? a : b; +} + +int foo() { + int c = 12; + int d = 13; + return c + d; +} + +void print(int c) { + printf("foo() %d\n", c); +} diff --git a/testing/tests/c/multi_instruction/function.c b/testing/tests/c/multi_instruction/function.c new file mode 100644 index 0000000..7ed50d1 --- /dev/null +++ b/testing/tests/c/multi_instruction/function.c @@ -0,0 +1,14 @@ +int max(int a, int b, int c) { + int max = a > b ? a : b; + max = max > c ? max : c; + return max; +} + + +int main() { + int a = 10; + int b = 20; + int c = 30; + max(a, b, c); + return 0; +} diff --git a/testing/tests/c/multi_instruction/glob.c b/testing/tests/c/multi_instruction/glob.c new file mode 100644 index 0000000..b70b773 --- /dev/null +++ b/testing/tests/c/multi_instruction/glob.c @@ -0,0 +1,9 @@ +int global = 0; + +int main() { + int a = global + 1; + global += 10; + global += 11; + a += 12; + return a + global; +} diff --git a/testing/tests/c/multi_instruction/if_then_else.c b/testing/tests/c/multi_instruction/if_then_else.c new file mode 100644 index 0000000..95e9cf7 --- /dev/null +++ b/testing/tests/c/multi_instruction/if_then_else.c @@ -0,0 +1,17 @@ +// +// Created by martina on 28/12/25. +// +#include + + +int main() { + int x = 1000; + x = x+1; + if (x<10) { + x = x*10; + printf("valore: %d\n", x); + } + else + printf("valore: %d\n", x); + return 0; +} diff --git a/testing/tests/c/multi_instruction/phi.cpp b/testing/tests/c/multi_instruction/phi.cpp new file mode 100644 index 0000000..4d688c7 --- /dev/null +++ b/testing/tests/c/multi_instruction/phi.cpp @@ -0,0 +1,10 @@ +// +// Created by martina on 07/01/26. +// + +int main() { + int y = 1; + int r = 0; + int a = y || r; + return a; +} \ No newline at end of file diff --git a/testing/tests/c/multi_instruction/ret.c b/testing/tests/c/multi_instruction/ret.c new file mode 100644 index 0000000..3d11e63 --- /dev/null +++ b/testing/tests/c/multi_instruction/ret.c @@ -0,0 +1,14 @@ +#include +#include + +void SigMismatch_Handler(void) { + printf("An error occurred and the signature value was different from expected"); + exit(-1); +} + +int main() { + int a = 10; + int b = 20; + printf("a + b %d", a+b); + return 0; +}