diff --git a/resolve-cveassert/src/ArithmeticSanitizer.cpp b/resolve-cveassert/src/ArithmeticSanitizer.cpp index 5b0b8430e..974ba57fe 100644 --- a/resolve-cveassert/src/ArithmeticSanitizer.cpp +++ b/resolve-cveassert/src/ArithmeticSanitizer.cpp @@ -3,6 +3,8 @@ * LGPL-3; See LICENSE.txt in the repo root for details. */ +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Twine.h" #include "llvm/AsmParser/Parser.h" @@ -18,6 +20,7 @@ #include "Vulnerability.hpp" #include +#include #include #include #include @@ -25,6 +28,31 @@ using namespace llvm; +static Constant *getContinueValue(Instruction *I) { + Type *Ty = I->getType(); + + if (Ty->isIntegerTy()) { + unsigned bitwidth = Ty->getIntegerBitWidth(); + + switch (I->getOpcode()) { + case Instruction::SDiv: + case Instruction::SRem: + return ConstantInt::get(Ty, APInt::getSignedMaxValue(bitwidth)); + + case Instruction::UDiv: + case Instruction::URem: + return ConstantInt::get(Ty, APInt::getMaxValue(bitwidth)); + + default: + llvm_unreachable("Unsupported integer operation"); + } + } + + if (Ty->isFloatingPointTy()) { + return ConstantFP::get(Ty, APFloat::getLargest(Ty->getFltSemantics())); + } +} + static void widenIntOverflow(Function *F) { // Basic algorithm: // Find the pattern of overflowing op -> sext @@ -126,16 +154,16 @@ void sanitizeDivideByZero(Function *F, } // Loop over each instruction in the list - for (auto *binaryOp : worklist) { + for (auto *binaryInst : worklist) { Value *dividend; Value *divisor; Value *isZero; - BasicBlock *checkMapEntryBB = binaryOp->getParent(); - BasicBlock *joinResultBB = checkMapEntryBB->splitBasicBlock(binaryOp); + BasicBlock *checkMapEntryBB = binaryInst->getParent(); + BasicBlock *joinResultBB = checkMapEntryBB->splitBasicBlock(binaryInst); BasicBlock *checkZeroBB = BasicBlock::Create(Ctx, "check.zero", F); BasicBlock *preserveDivBB = - BasicBlock::Create(Ctx, "safe.div", F, joinResultBB); + BasicBlock::Create(Ctx, "perform.div", F, joinResultBB); BasicBlock *remedDivBB = BasicBlock::Create(Ctx, "sanitize.div", F, joinResultBB); @@ -147,10 +175,10 @@ void sanitizeDivideByZero(Function *F, builder.SetInsertPoint(checkZeroBB); // Extract dividend and divisor - dividend = binaryOp->getOperand(0); - divisor = binaryOp->getOperand(1); + dividend = binaryInst->getOperand(0); + divisor = binaryInst->getOperand(1); - switch (binaryOp->getOpcode()) { + switch (binaryInst->getOpcode()) { case Instruction::SDiv: case Instruction::UDiv: case Instruction::SRem: @@ -168,88 +196,76 @@ void sanitizeDivideByZero(Function *F, builder.CreateCondBr(isZero, remedDivBB, preserveDivBB); builder.SetInsertPoint(remedDivBB); - if (Function *fn = getOrCreateRemediationBehavior(M, strategy)) { - builder.CreateCall(fn); - } - Value *safeDiv = nullptr; - Value *safeIntDivisor; - Value *safeFpDivisor; - - switch (binaryOp->getOpcode()) { - case Instruction::UDiv: - safeIntDivisor = ConstantInt::get(divisor->getType(), 1); - safeDiv = builder.CreateUDiv(dividend, safeIntDivisor); - break; - - case Instruction::SDiv: - safeIntDivisor = ConstantInt::get(divisor->getType(), 1); - safeDiv = builder.CreateSDiv(dividend, safeIntDivisor); - break; - case Instruction::FDiv: - safeFpDivisor = ConstantFP::get(binaryOp->getType(), 1.0); - safeDiv = builder.CreateFDiv(dividend, safeFpDivisor); + Value *remedValue = nullptr; + switch (strategy) { + case Vulnerability::RemediationStrategies::CONTINUE: + remedValue = getContinueValue(binaryInst); + builder.CreateBr(joinResultBB); break; - case Instruction::URem: - safeIntDivisor = ConstantInt::get(divisor->getType(), 1); - safeDiv = builder.CreateURem(dividend, safeIntDivisor); - break; + case Vulnerability::RemediationStrategies::EXIT: + case Vulnerability::RemediationStrategies::RECOVER: + builder.CreateCall(getOrCreateRemediationBehavior(M, strategy)); - case Instruction::SRem: - safeIntDivisor = ConstantInt::get(divisor->getType(), 1); - safeDiv = builder.CreateSRem(dividend, safeIntDivisor); + // EXIT/RECOVER transfer control elsewhere + builder.CreateUnreachable(); break; - case Instruction::FRem: - safeFpDivisor = ConstantFP::get(divisor->getType(), 1.0); - safeDiv = builder.CreateFRem(dividend, safeFpDivisor); - break; + // The only remediation policies that work with this sanitizer + // are CONTINUE, EXIT, and RECOVER. + // This default case should never be triggered because + // the beginning of the function checks for the correct + // sanitizer-policy combination + default: + llvm_unreachable(""); } - builder.CreateBr(joinResultBB); builder.SetInsertPoint(preserveDivBB); - Value *normalResult = nullptr; - switch (binaryOp->getOpcode()) { + Value *divOp = nullptr; + switch (binaryInst->getOpcode()) { case Instruction::SDiv: - normalResult = builder.CreateSDiv(dividend, divisor); + divOp = builder.CreateSDiv(dividend, divisor); builder.CreateBr(joinResultBB); break; - case Instruction::UDiv: - normalResult = builder.CreateUDiv(dividend, divisor); + case Instruction::SRem: + divOp = builder.CreateSRem(dividend, divisor); builder.CreateBr(joinResultBB); break; - case Instruction::FDiv: - normalResult = builder.CreateFDiv(dividend, divisor); + case Instruction::UDiv: + divOp = builder.CreateUDiv(dividend, divisor); builder.CreateBr(joinResultBB); break; - case Instruction::SRem: - normalResult = builder.CreateSRem(dividend, divisor); + case Instruction::URem: + divOp = builder.CreateURem(dividend, divisor); builder.CreateBr(joinResultBB); break; - case Instruction::URem: - normalResult = builder.CreateURem(dividend, divisor); + case Instruction::FDiv: + divOp = builder.CreateFDiv(dividend, divisor); builder.CreateBr(joinResultBB); break; case Instruction::FRem: - normalResult = builder.CreateFRem(dividend, divisor); + divOp = builder.CreateFRem(dividend, divisor); builder.CreateBr(joinResultBB); break; } builder.SetInsertPoint(&*joinResultBB->begin()); - PHINode *phi = builder.CreatePHI(binaryOp->getType(), 2); - phi->addIncoming(safeDiv, remedDivBB); - phi->addIncoming(normalResult, preserveDivBB); + PHINode *phi = builder.CreatePHI(binaryInst->getType(), 2); + phi->addIncoming(divOp, preserveDivBB); + + if (strategy == Vulnerability::RemediationStrategies::CONTINUE) { + phi->addIncoming(remedValue, remedDivBB); + } - binaryOp->replaceAllUsesWith(phi); - binaryOp->eraseFromParent(); + binaryInst->replaceAllUsesWith(phi); + binaryInst->eraseFromParent(); } } @@ -293,29 +309,29 @@ void sanitizeIntOverflow(Function *F, Value *op1; Value *op2; - for (auto *binaryOp : worklist) { - if (!binaryOp->hasNoSignedWrap() && !binaryOp->hasNoUnsignedWrap()) { + for (auto *binaryInst : worklist) { + if (!binaryInst->hasNoSignedWrap() && !binaryInst->hasNoUnsignedWrap()) { continue; } - op1 = binaryOp->getOperand(0); - op2 = binaryOp->getOperand(1); + op1 = binaryInst->getOperand(0); + op2 = binaryInst->getOperand(1); - builder.SetInsertPoint(binaryOp); + builder.SetInsertPoint(binaryInst); auto insertSafeOp = [&builder, - M](Instruction *binaryOp, Value *op1, + M](Instruction *binaryInst, Value *op1, Value *op2) -> std::pair { Intrinsic::ID intrinsic_id; - Type *BinOpType = binaryOp->getType(); + Type *BinOpType = binaryInst->getType(); bool isUnsigned = false; // Heuristic: If instruction has NUW but not NSW then, treat as unsigned - if (binaryOp->hasNoUnsignedWrap() && !binaryOp->hasNoSignedWrap()) { + if (binaryInst->hasNoUnsignedWrap() && !binaryInst->hasNoSignedWrap()) { isUnsigned = true; } - switch (binaryOp->getOpcode()) { + switch (binaryInst->getOpcode()) { case Instruction::Add: intrinsic_id = isUnsigned ? Intrinsic::uadd_with_overflow : Intrinsic::sadd_with_overflow; @@ -343,17 +359,17 @@ void sanitizeIntOverflow(Function *F, return {result, isOverflow}; }; - auto insertSatOp = [&builder, M](Instruction *binaryOp, Value *op1, + auto insertSatOp = [&builder, M](Instruction *binaryInst, Value *op1, Value *op2) -> Instruction * { Intrinsic::ID intrinsic_id; - Type *BinOpType = binaryOp->getType(); + Type *BinOpType = binaryInst->getType(); bool isUnsigned = false; - if (binaryOp->hasNoUnsignedWrap() && !binaryOp->hasNoSignedWrap()) { + if (binaryInst->hasNoUnsignedWrap() && !binaryInst->hasNoSignedWrap()) { isUnsigned = true; } - switch (binaryOp->getOpcode()) { + switch (binaryInst->getOpcode()) { case Instruction::Add: intrinsic_id = isUnsigned ? Intrinsic::uadd_sat : Intrinsic::sadd_sat; break; @@ -378,7 +394,7 @@ void sanitizeIntOverflow(Function *F, // Add fracBits parameter for saturated multiplication operations // LLVM LangRef: // https://llvm.org/docs/LangRef.html#fixed-point-arithmetic-intrinsics - if (binaryOp->getOpcode() == Instruction::Mul) { + if (binaryInst->getOpcode() == Instruction::Mul) { Value *fracBits = ConstantInt::get(BinOpType, 0); return builder.CreateCall(satOp, {op1, op2, fracBits}); @@ -387,11 +403,11 @@ void sanitizeIntOverflow(Function *F, } }; - auto [safeResult, isOverflow] = insertSafeOp(binaryOp, op1, op2); - auto satResult = insertSatOp(binaryOp, op1, op2); + auto [safeResult, isOverflow] = insertSafeOp(binaryInst, op1, op2); + auto satResult = insertSatOp(binaryInst, op1, op2); - BasicBlock *checkMapEntryBB = binaryOp->getParent(); - BasicBlock *joinResultBB = checkMapEntryBB->splitBasicBlock(binaryOp); + BasicBlock *checkMapEntryBB = binaryInst->getParent(); + BasicBlock *joinResultBB = checkMapEntryBB->splitBasicBlock(binaryInst); BasicBlock *checkOverflowBB = BasicBlock::Create(Ctx, "check.overflow", F); BasicBlock *remedOverflowBB = BasicBlock::Create(Ctx, "sanitize.overflow", F, joinResultBB); @@ -412,12 +428,12 @@ void sanitizeIntOverflow(Function *F, builder.SetInsertPoint(&*joinResultBB->begin()); if (strategy == Vulnerability::RemediationStrategies::SAT) { - binaryOp->replaceAllUsesWith(satResult); + binaryInst->replaceAllUsesWith(satResult); } else { - binaryOp->replaceAllUsesWith(safeResult); + binaryInst->replaceAllUsesWith(safeResult); } - binaryOp->eraseFromParent(); + binaryInst->eraseFromParent(); } } @@ -457,13 +473,13 @@ void sanitizeBitShift(Function *F, } } - for (auto *binaryOp : worklist) { + for (auto *binaryInst : worklist) { Value *isNegative; Value *isGreaterThanBitwidth; Value *CheckShiftAmtCond; - BasicBlock *checkMapEntryBB = binaryOp->getParent(); - BasicBlock *joinResultBB = checkMapEntryBB->splitBasicBlock(binaryOp); + BasicBlock *checkMapEntryBB = binaryInst->getParent(); + BasicBlock *joinResultBB = checkMapEntryBB->splitBasicBlock(binaryInst); BasicBlock *checkShiftBB = BasicBlock::Create(Ctx, "check.zero", F); BasicBlock *preserveShiftBB = BasicBlock::Create(Ctx, "safe.shift", F, joinResultBB); @@ -476,8 +492,8 @@ void sanitizeBitShift(Function *F, preserveShiftBB, checkShiftBB); builder.SetInsertPoint(checkShiftBB); - Value *shifted_value = binaryOp->getOperand(0); - Value *bit_pos = binaryOp->getOperand(1); + Value *shifted_value = binaryInst->getOperand(0); + Value *bit_pos = binaryInst->getOperand(1); unsigned BitWidth = shifted_value->getType()->getIntegerBitWidth(); isNegative = @@ -494,7 +510,7 @@ void sanitizeBitShift(Function *F, Value *safeShift = nullptr; Value *safeBitPos; - switch (binaryOp->getOpcode()) { + switch (binaryInst->getOpcode()) { case Instruction::Shl: safeBitPos = ConstantInt::get(bit_pos->getType(), 0); safeShift = builder.CreateShl(shifted_value, safeBitPos); @@ -515,7 +531,7 @@ void sanitizeBitShift(Function *F, builder.SetInsertPoint(preserveShiftBB); Value *normalResult = nullptr; - switch (binaryOp->getOpcode()) { + switch (binaryInst->getOpcode()) { case Instruction::Shl: normalResult = builder.CreateShl(shifted_value, bit_pos); builder.CreateBr(joinResultBB); @@ -533,11 +549,11 @@ void sanitizeBitShift(Function *F, } builder.SetInsertPoint(&*joinResultBB->begin()); - PHINode *phi = builder.CreatePHI(binaryOp->getType(), 2); + PHINode *phi = builder.CreatePHI(binaryInst->getType(), 2); phi->addIncoming(safeShift, remedShiftBB); phi->addIncoming(normalResult, preserveShiftBB); - binaryOp->replaceAllUsesWith(phi); - binaryOp->eraseFromParent(); + binaryInst->replaceAllUsesWith(phi); + binaryInst->eraseFromParent(); } } diff --git a/resolve-cveassert/src/IRUtils.cpp b/resolve-cveassert/src/IRUtils.cpp index 5d9cb9146..09ce63ae6 100644 --- a/resolve-cveassert/src/IRUtils.cpp +++ b/resolve-cveassert/src/IRUtils.cpp @@ -498,6 +498,11 @@ getOrCreateRemediationBehavior(Module *M, return fn; } + AttrBuilder FnAttrs(Ctx); + FnAttrs.addAttribute(Attribute::NoReturn); + AttributeList attrs = + AttributeList::get(Ctx, AttributeList::FunctionIndex, FnAttrs); + BasicBlock *entryBB = BasicBlock::Create(Ctx, "entry", fn); IRBuilder<> builder(entryBB); diff --git a/toolchains/resolve-toolchain.cmake b/toolchains/resolve-toolchain.cmake index d458aa6b0..98dac2a83 100644 --- a/toolchains/resolve-toolchain.cmake +++ b/toolchains/resolve-toolchain.cmake @@ -17,7 +17,7 @@ set(RESOLVE_INTRINSICS_FLAGS # Keep basic debug info to make it eaiser to lookup function names and files for inlined functions set(RESOLVE_DEBUG_INFO_FLAGS - "-g1" + "-g1 -fno-omit-frame-pointer" ) set(CMAKE_C_FLAGS_INIT "${CMAKE_C_FLAGS} ${RESOLVE_PLUGINS} ${RESOLVE_INTRINSICS_FLAGS} ${RESOLVE_DEBUG_INFO_FLAGS}" CACHE STRING "c flags")