diff options
Diffstat (limited to 'gnu/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp')
| -rw-r--r-- | gnu/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp | 114 |
1 files changed, 67 insertions, 47 deletions
diff --git a/gnu/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp b/gnu/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp index be9a22a8681..a0c78e0468c 100644 --- a/gnu/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp +++ b/gnu/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/Twine.h" #include "llvm/Analysis/MemoryBuiltins.h" +#include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/TargetFolder.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/BasicBlock.h" @@ -46,30 +47,26 @@ STATISTIC(ChecksUnable, "Bounds checks unable to add"); using BuilderTy = IRBuilder<TargetFolder>; -/// Adds run-time bounds checks to memory accessing instructions. +/// Gets the conditions under which memory accessing instructions will overflow. /// /// \p Ptr is the pointer that will be read/written, and \p InstVal is either /// the result from the load or the value being stored. It is used to determine /// the size of memory block that is touched. /// -/// \p GetTrapBB is a callable that returns the trap BB to use on failure. -/// -/// Returns true if any change was made to the IR, false otherwise. -template <typename GetTrapBBT> -static bool instrumentMemAccess(Value *Ptr, Value *InstVal, - const DataLayout &DL, TargetLibraryInfo &TLI, - ObjectSizeOffsetEvaluator &ObjSizeEval, - BuilderTy &IRB, - GetTrapBBT GetTrapBB) { +/// Returns the condition under which the access will overflow. +static Value *getBoundsCheckCond(Value *Ptr, Value *InstVal, + const DataLayout &DL, TargetLibraryInfo &TLI, + ObjectSizeOffsetEvaluator &ObjSizeEval, + BuilderTy &IRB, ScalarEvolution &SE) { uint64_t NeededSize = DL.getTypeStoreSize(InstVal->getType()); - DEBUG(dbgs() << "Instrument " << *Ptr << " for " << Twine(NeededSize) - << " bytes\n"); + LLVM_DEBUG(dbgs() << "Instrument " << *Ptr << " for " << Twine(NeededSize) + << " bytes\n"); SizeOffsetEvalType SizeOffset = ObjSizeEval.compute(Ptr); if (!ObjSizeEval.bothKnown(SizeOffset)) { ++ChecksUnable; - return false; + return nullptr; } Value *Size = SizeOffset.first; @@ -79,6 +76,10 @@ static bool instrumentMemAccess(Value *Ptr, Value *InstVal, Type *IntTy = DL.getIntPtrType(Ptr->getType()); Value *NeededSizeVal = ConstantInt::get(IntTy, NeededSize); + auto SizeRange = SE.getUnsignedRange(SE.getSCEV(Size)); + auto OffsetRange = SE.getUnsignedRange(SE.getSCEV(Offset)); + auto NeededSizeRange = SE.getUnsignedRange(SE.getSCEV(NeededSizeVal)); + // three checks are required to ensure safety: // . Offset >= 0 (since the offset is given from the base ptr) // . Size >= Offset (unsigned) @@ -87,21 +88,38 @@ static bool instrumentMemAccess(Value *Ptr, Value *InstVal, // optimization: if Size >= 0 (signed), skip 1st check // FIXME: add NSW/NUW here? -- we dont care if the subtraction overflows Value *ObjSize = IRB.CreateSub(Size, Offset); - Value *Cmp2 = IRB.CreateICmpULT(Size, Offset); - Value *Cmp3 = IRB.CreateICmpULT(ObjSize, NeededSizeVal); + Value *Cmp2 = SizeRange.getUnsignedMin().uge(OffsetRange.getUnsignedMax()) + ? ConstantInt::getFalse(Ptr->getContext()) + : IRB.CreateICmpULT(Size, Offset); + Value *Cmp3 = SizeRange.sub(OffsetRange) + .getUnsignedMin() + .uge(NeededSizeRange.getUnsignedMax()) + ? ConstantInt::getFalse(Ptr->getContext()) + : IRB.CreateICmpULT(ObjSize, NeededSizeVal); Value *Or = IRB.CreateOr(Cmp2, Cmp3); - if (!SizeCI || SizeCI->getValue().slt(0)) { + if ((!SizeCI || SizeCI->getValue().slt(0)) && + !SizeRange.getSignedMin().isNonNegative()) { Value *Cmp1 = IRB.CreateICmpSLT(Offset, ConstantInt::get(IntTy, 0)); Or = IRB.CreateOr(Cmp1, Or); } + return Or; +} + +/// Adds run-time bounds checks to memory accessing instructions. +/// +/// \p Or is the condition that should guard the trap. +/// +/// \p GetTrapBB is a callable that returns the trap BB to use on failure. +template <typename GetTrapBBT> +static void insertBoundsCheck(Value *Or, BuilderTy IRB, GetTrapBBT GetTrapBB) { // check if the comparison is always false ConstantInt *C = dyn_cast_or_null<ConstantInt>(Or); if (C) { ++ChecksSkipped; // If non-zero, nothing to do. if (!C->getZExtValue()) - return true; + return; } ++ChecksAdded; @@ -115,26 +133,40 @@ static bool instrumentMemAccess(Value *Ptr, Value *InstVal, // FIXME: We should really handle this differently to bypass the splitting // the block. BranchInst::Create(GetTrapBB(IRB), OldBB); - return true; + return; } // Create the conditional branch. BranchInst::Create(GetTrapBB(IRB), Cont, Or, OldBB); - return true; } -static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI) { +static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI, + ScalarEvolution &SE) { const DataLayout &DL = F.getParent()->getDataLayout(); ObjectSizeOffsetEvaluator ObjSizeEval(DL, &TLI, F.getContext(), /*RoundToAlign=*/true); // check HANDLE_MEMORY_INST in include/llvm/Instruction.def for memory // touching instructions - std::vector<Instruction *> WorkList; + SmallVector<std::pair<Instruction *, Value *>, 4> TrapInfo; for (Instruction &I : instructions(F)) { - if (isa<LoadInst>(I) || isa<StoreInst>(I) || isa<AtomicCmpXchgInst>(I) || - isa<AtomicRMWInst>(I)) - WorkList.push_back(&I); + Value *Or = nullptr; + BuilderTy IRB(I.getParent(), BasicBlock::iterator(&I), TargetFolder(DL)); + if (LoadInst *LI = dyn_cast<LoadInst>(&I)) { + Or = getBoundsCheckCond(LI->getPointerOperand(), LI, DL, TLI, + ObjSizeEval, IRB, SE); + } else if (StoreInst *SI = dyn_cast<StoreInst>(&I)) { + Or = getBoundsCheckCond(SI->getPointerOperand(), SI->getValueOperand(), + DL, TLI, ObjSizeEval, IRB, SE); + } else if (AtomicCmpXchgInst *AI = dyn_cast<AtomicCmpXchgInst>(&I)) { + Or = getBoundsCheckCond(AI->getPointerOperand(), AI->getCompareOperand(), + DL, TLI, ObjSizeEval, IRB, SE); + } else if (AtomicRMWInst *AI = dyn_cast<AtomicRMWInst>(&I)) { + Or = getBoundsCheckCond(AI->getPointerOperand(), AI->getValOperand(), DL, + TLI, ObjSizeEval, IRB, SE); + } + if (Or) + TrapInfo.push_back(std::make_pair(&I, Or)); } // Create a trapping basic block on demand using a callback. Depending on @@ -163,35 +195,21 @@ static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI) { return TrapBB; }; - bool MadeChange = false; - for (Instruction *Inst : WorkList) { + // Add the checks. + for (const auto &Entry : TrapInfo) { + Instruction *Inst = Entry.first; BuilderTy IRB(Inst->getParent(), BasicBlock::iterator(Inst), TargetFolder(DL)); - if (LoadInst *LI = dyn_cast<LoadInst>(Inst)) { - MadeChange |= instrumentMemAccess(LI->getPointerOperand(), LI, DL, TLI, - ObjSizeEval, IRB, GetTrapBB); - } else if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) { - MadeChange |= - instrumentMemAccess(SI->getPointerOperand(), SI->getValueOperand(), - DL, TLI, ObjSizeEval, IRB, GetTrapBB); - } else if (AtomicCmpXchgInst *AI = dyn_cast<AtomicCmpXchgInst>(Inst)) { - MadeChange |= - instrumentMemAccess(AI->getPointerOperand(), AI->getCompareOperand(), - DL, TLI, ObjSizeEval, IRB, GetTrapBB); - } else if (AtomicRMWInst *AI = dyn_cast<AtomicRMWInst>(Inst)) { - MadeChange |= - instrumentMemAccess(AI->getPointerOperand(), AI->getValOperand(), DL, - TLI, ObjSizeEval, IRB, GetTrapBB); - } else { - llvm_unreachable("unknown Instruction type"); - } + insertBoundsCheck(Entry.second, IRB, GetTrapBB); } - return MadeChange; + + return !TrapInfo.empty(); } PreservedAnalyses BoundsCheckingPass::run(Function &F, FunctionAnalysisManager &AM) { auto &TLI = AM.getResult<TargetLibraryAnalysis>(F); + auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F); - if (!addBoundsChecking(F, TLI)) + if (!addBoundsChecking(F, TLI, SE)) return PreservedAnalyses::all(); return PreservedAnalyses::none(); @@ -207,11 +225,13 @@ struct BoundsCheckingLegacyPass : public FunctionPass { bool runOnFunction(Function &F) override { auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(); - return addBoundsChecking(F, TLI); + auto &SE = getAnalysis<ScalarEvolutionWrapperPass>().getSE(); + return addBoundsChecking(F, TLI, SE); } void getAnalysisUsage(AnalysisUsage &AU) const override { AU.addRequired<TargetLibraryInfoWrapperPass>(); + AU.addRequired<ScalarEvolutionWrapperPass>(); } }; } // namespace |
