10b57cec5SDimitry Andric //===- BoundsChecking.cpp - Instrumentation for run-time bounds checking --===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "llvm/Transforms/Instrumentation/BoundsChecking.h" 100b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h" 110b57cec5SDimitry Andric #include "llvm/ADT/Twine.h" 120b57cec5SDimitry Andric #include "llvm/Analysis/MemoryBuiltins.h" 130b57cec5SDimitry Andric #include "llvm/Analysis/ScalarEvolution.h" 140b57cec5SDimitry Andric #include "llvm/Analysis/TargetFolder.h" 150b57cec5SDimitry Andric #include "llvm/Analysis/TargetLibraryInfo.h" 160b57cec5SDimitry Andric #include "llvm/IR/BasicBlock.h" 170b57cec5SDimitry Andric #include "llvm/IR/Constants.h" 180b57cec5SDimitry Andric #include "llvm/IR/DataLayout.h" 190b57cec5SDimitry Andric #include "llvm/IR/Function.h" 200b57cec5SDimitry Andric #include "llvm/IR/IRBuilder.h" 210b57cec5SDimitry Andric #include "llvm/IR/InstIterator.h" 220b57cec5SDimitry Andric #include "llvm/IR/Instruction.h" 230b57cec5SDimitry Andric #include "llvm/IR/Instructions.h" 240b57cec5SDimitry Andric #include "llvm/IR/Intrinsics.h" 250b57cec5SDimitry Andric #include "llvm/IR/Value.h" 260b57cec5SDimitry Andric #include "llvm/Support/Casting.h" 270b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h" 280b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 290b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 300b57cec5SDimitry Andric #include <cstdint> 31fe6060f1SDimitry Andric #include <utility> 320b57cec5SDimitry Andric 330b57cec5SDimitry Andric using namespace llvm; 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric #define DEBUG_TYPE "bounds-checking" 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric static cl::opt<bool> SingleTrapBB("bounds-checking-single-trap", 380b57cec5SDimitry Andric cl::desc("Use one trap block per function")); 390b57cec5SDimitry Andric 405f757f3fSDimitry Andric static cl::opt<bool> DebugTrapBB("bounds-checking-unique-traps", 415f757f3fSDimitry Andric cl::desc("Always use one trap per check")); 425f757f3fSDimitry Andric 430b57cec5SDimitry Andric STATISTIC(ChecksAdded, "Bounds checks added"); 440b57cec5SDimitry Andric STATISTIC(ChecksSkipped, "Bounds checks skipped"); 450b57cec5SDimitry Andric STATISTIC(ChecksUnable, "Bounds checks unable to add"); 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric using BuilderTy = IRBuilder<TargetFolder>; 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric /// Gets the conditions under which memory accessing instructions will overflow. 500b57cec5SDimitry Andric /// 510b57cec5SDimitry Andric /// \p Ptr is the pointer that will be read/written, and \p InstVal is either 520b57cec5SDimitry Andric /// the result from the load or the value being stored. It is used to determine 530b57cec5SDimitry Andric /// the size of memory block that is touched. 540b57cec5SDimitry Andric /// 550b57cec5SDimitry Andric /// Returns the condition under which the access will overflow. 560b57cec5SDimitry Andric static Value *getBoundsCheckCond(Value *Ptr, Value *InstVal, 570b57cec5SDimitry Andric const DataLayout &DL, TargetLibraryInfo &TLI, 580b57cec5SDimitry Andric ObjectSizeOffsetEvaluator &ObjSizeEval, 590b57cec5SDimitry Andric BuilderTy &IRB, ScalarEvolution &SE) { 6006c3fb27SDimitry Andric TypeSize NeededSize = DL.getTypeStoreSize(InstVal->getType()); 610b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Instrument " << *Ptr << " for " << Twine(NeededSize) 620b57cec5SDimitry Andric << " bytes\n"); 630b57cec5SDimitry Andric 641db9f3b2SDimitry Andric SizeOffsetValue SizeOffset = ObjSizeEval.compute(Ptr); 650b57cec5SDimitry Andric 661db9f3b2SDimitry Andric if (!SizeOffset.bothKnown()) { 670b57cec5SDimitry Andric ++ChecksUnable; 680b57cec5SDimitry Andric return nullptr; 690b57cec5SDimitry Andric } 700b57cec5SDimitry Andric 711db9f3b2SDimitry Andric Value *Size = SizeOffset.Size; 721db9f3b2SDimitry Andric Value *Offset = SizeOffset.Offset; 730b57cec5SDimitry Andric ConstantInt *SizeCI = dyn_cast<ConstantInt>(Size); 740b57cec5SDimitry Andric 7506c3fb27SDimitry Andric Type *IndexTy = DL.getIndexType(Ptr->getType()); 7606c3fb27SDimitry Andric Value *NeededSizeVal = IRB.CreateTypeSize(IndexTy, NeededSize); 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric auto SizeRange = SE.getUnsignedRange(SE.getSCEV(Size)); 790b57cec5SDimitry Andric auto OffsetRange = SE.getUnsignedRange(SE.getSCEV(Offset)); 800b57cec5SDimitry Andric auto NeededSizeRange = SE.getUnsignedRange(SE.getSCEV(NeededSizeVal)); 810b57cec5SDimitry Andric 820b57cec5SDimitry Andric // three checks are required to ensure safety: 830b57cec5SDimitry Andric // . Offset >= 0 (since the offset is given from the base ptr) 840b57cec5SDimitry Andric // . Size >= Offset (unsigned) 850b57cec5SDimitry Andric // . Size - Offset >= NeededSize (unsigned) 860b57cec5SDimitry Andric // 870b57cec5SDimitry Andric // optimization: if Size >= 0 (signed), skip 1st check 880b57cec5SDimitry Andric // FIXME: add NSW/NUW here? -- we dont care if the subtraction overflows 890b57cec5SDimitry Andric Value *ObjSize = IRB.CreateSub(Size, Offset); 900b57cec5SDimitry Andric Value *Cmp2 = SizeRange.getUnsignedMin().uge(OffsetRange.getUnsignedMax()) 910b57cec5SDimitry Andric ? ConstantInt::getFalse(Ptr->getContext()) 920b57cec5SDimitry Andric : IRB.CreateICmpULT(Size, Offset); 930b57cec5SDimitry Andric Value *Cmp3 = SizeRange.sub(OffsetRange) 940b57cec5SDimitry Andric .getUnsignedMin() 950b57cec5SDimitry Andric .uge(NeededSizeRange.getUnsignedMax()) 960b57cec5SDimitry Andric ? ConstantInt::getFalse(Ptr->getContext()) 970b57cec5SDimitry Andric : IRB.CreateICmpULT(ObjSize, NeededSizeVal); 980b57cec5SDimitry Andric Value *Or = IRB.CreateOr(Cmp2, Cmp3); 990b57cec5SDimitry Andric if ((!SizeCI || SizeCI->getValue().slt(0)) && 1000b57cec5SDimitry Andric !SizeRange.getSignedMin().isNonNegative()) { 10106c3fb27SDimitry Andric Value *Cmp1 = IRB.CreateICmpSLT(Offset, ConstantInt::get(IndexTy, 0)); 1020b57cec5SDimitry Andric Or = IRB.CreateOr(Cmp1, Or); 1030b57cec5SDimitry Andric } 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric return Or; 1060b57cec5SDimitry Andric } 1070b57cec5SDimitry Andric 1080b57cec5SDimitry Andric /// Adds run-time bounds checks to memory accessing instructions. 1090b57cec5SDimitry Andric /// 1100b57cec5SDimitry Andric /// \p Or is the condition that should guard the trap. 1110b57cec5SDimitry Andric /// 1120b57cec5SDimitry Andric /// \p GetTrapBB is a callable that returns the trap BB to use on failure. 1130b57cec5SDimitry Andric template <typename GetTrapBBT> 1145ffd83dbSDimitry Andric static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) { 1150b57cec5SDimitry Andric // check if the comparison is always false 1160b57cec5SDimitry Andric ConstantInt *C = dyn_cast_or_null<ConstantInt>(Or); 1170b57cec5SDimitry Andric if (C) { 1180b57cec5SDimitry Andric ++ChecksSkipped; 1190b57cec5SDimitry Andric // If non-zero, nothing to do. 1200b57cec5SDimitry Andric if (!C->getZExtValue()) 1210b57cec5SDimitry Andric return; 1220b57cec5SDimitry Andric } 1230b57cec5SDimitry Andric ++ChecksAdded; 1240b57cec5SDimitry Andric 1250b57cec5SDimitry Andric BasicBlock::iterator SplitI = IRB.GetInsertPoint(); 1260b57cec5SDimitry Andric BasicBlock *OldBB = SplitI->getParent(); 1270b57cec5SDimitry Andric BasicBlock *Cont = OldBB->splitBasicBlock(SplitI); 1280b57cec5SDimitry Andric OldBB->getTerminator()->eraseFromParent(); 1290b57cec5SDimitry Andric 1300b57cec5SDimitry Andric if (C) { 1310b57cec5SDimitry Andric // If we have a constant zero, unconditionally branch. 1320b57cec5SDimitry Andric // FIXME: We should really handle this differently to bypass the splitting 1330b57cec5SDimitry Andric // the block. 1340b57cec5SDimitry Andric BranchInst::Create(GetTrapBB(IRB), OldBB); 1350b57cec5SDimitry Andric return; 1360b57cec5SDimitry Andric } 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric // Create the conditional branch. 1390b57cec5SDimitry Andric BranchInst::Create(GetTrapBB(IRB), Cont, Or, OldBB); 1400b57cec5SDimitry Andric } 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI, 1430b57cec5SDimitry Andric ScalarEvolution &SE) { 14481ad6265SDimitry Andric if (F.hasFnAttribute(Attribute::NoSanitizeBounds)) 14581ad6265SDimitry Andric return false; 14681ad6265SDimitry Andric 147*0fca6ea1SDimitry Andric const DataLayout &DL = F.getDataLayout(); 1480b57cec5SDimitry Andric ObjectSizeOpts EvalOpts; 1490b57cec5SDimitry Andric EvalOpts.RoundToAlign = true; 150bdd1243dSDimitry Andric EvalOpts.EvalMode = ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset; 1510b57cec5SDimitry Andric ObjectSizeOffsetEvaluator ObjSizeEval(DL, &TLI, F.getContext(), EvalOpts); 1520b57cec5SDimitry Andric 1530b57cec5SDimitry Andric // check HANDLE_MEMORY_INST in include/llvm/Instruction.def for memory 1540b57cec5SDimitry Andric // touching instructions 1550b57cec5SDimitry Andric SmallVector<std::pair<Instruction *, Value *>, 4> TrapInfo; 1560b57cec5SDimitry Andric for (Instruction &I : instructions(F)) { 1570b57cec5SDimitry Andric Value *Or = nullptr; 1580b57cec5SDimitry Andric BuilderTy IRB(I.getParent(), BasicBlock::iterator(&I), TargetFolder(DL)); 1590b57cec5SDimitry Andric if (LoadInst *LI = dyn_cast<LoadInst>(&I)) { 1605ffd83dbSDimitry Andric if (!LI->isVolatile()) 1610b57cec5SDimitry Andric Or = getBoundsCheckCond(LI->getPointerOperand(), LI, DL, TLI, 1620b57cec5SDimitry Andric ObjSizeEval, IRB, SE); 1630b57cec5SDimitry Andric } else if (StoreInst *SI = dyn_cast<StoreInst>(&I)) { 1645ffd83dbSDimitry Andric if (!SI->isVolatile()) 1650b57cec5SDimitry Andric Or = getBoundsCheckCond(SI->getPointerOperand(), SI->getValueOperand(), 1660b57cec5SDimitry Andric DL, TLI, ObjSizeEval, IRB, SE); 1670b57cec5SDimitry Andric } else if (AtomicCmpXchgInst *AI = dyn_cast<AtomicCmpXchgInst>(&I)) { 1685ffd83dbSDimitry Andric if (!AI->isVolatile()) 1695ffd83dbSDimitry Andric Or = 1705ffd83dbSDimitry Andric getBoundsCheckCond(AI->getPointerOperand(), AI->getCompareOperand(), 1710b57cec5SDimitry Andric DL, TLI, ObjSizeEval, IRB, SE); 1720b57cec5SDimitry Andric } else if (AtomicRMWInst *AI = dyn_cast<AtomicRMWInst>(&I)) { 1735ffd83dbSDimitry Andric if (!AI->isVolatile()) 1745ffd83dbSDimitry Andric Or = getBoundsCheckCond(AI->getPointerOperand(), AI->getValOperand(), 1755ffd83dbSDimitry Andric DL, TLI, ObjSizeEval, IRB, SE); 1760b57cec5SDimitry Andric } 1770b57cec5SDimitry Andric if (Or) 1780b57cec5SDimitry Andric TrapInfo.push_back(std::make_pair(&I, Or)); 1790b57cec5SDimitry Andric } 1800b57cec5SDimitry Andric 1810b57cec5SDimitry Andric // Create a trapping basic block on demand using a callback. Depending on 1820b57cec5SDimitry Andric // flags, this will either create a single block for the entire function or 1830b57cec5SDimitry Andric // will create a fresh block every time it is called. 1840b57cec5SDimitry Andric BasicBlock *TrapBB = nullptr; 1850b57cec5SDimitry Andric auto GetTrapBB = [&TrapBB](BuilderTy &IRB) { 1860b57cec5SDimitry Andric Function *Fn = IRB.GetInsertBlock()->getParent(); 1870b57cec5SDimitry Andric auto DebugLoc = IRB.getCurrentDebugLocation(); 1880b57cec5SDimitry Andric IRBuilder<>::InsertPointGuard Guard(IRB); 1895f757f3fSDimitry Andric 1905f757f3fSDimitry Andric if (TrapBB && SingleTrapBB && !DebugTrapBB) 1915f757f3fSDimitry Andric return TrapBB; 1925f757f3fSDimitry Andric 1930b57cec5SDimitry Andric TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn); 1940b57cec5SDimitry Andric IRB.SetInsertPoint(TrapBB); 1950b57cec5SDimitry Andric 1965f757f3fSDimitry Andric Intrinsic::ID IntrID = DebugTrapBB ? Intrinsic::ubsantrap : Intrinsic::trap; 1975f757f3fSDimitry Andric auto *F = Intrinsic::getDeclaration(Fn->getParent(), IntrID); 1985f757f3fSDimitry Andric 1995f757f3fSDimitry Andric CallInst *TrapCall; 2005f757f3fSDimitry Andric if (DebugTrapBB) { 2015f757f3fSDimitry Andric TrapCall = 2025f757f3fSDimitry Andric IRB.CreateCall(F, ConstantInt::get(IRB.getInt8Ty(), Fn->size())); 2035f757f3fSDimitry Andric } else { 2045f757f3fSDimitry Andric TrapCall = IRB.CreateCall(F, {}); 2055f757f3fSDimitry Andric } 2065f757f3fSDimitry Andric 2070b57cec5SDimitry Andric TrapCall->setDoesNotReturn(); 2080b57cec5SDimitry Andric TrapCall->setDoesNotThrow(); 2090b57cec5SDimitry Andric TrapCall->setDebugLoc(DebugLoc); 2100b57cec5SDimitry Andric IRB.CreateUnreachable(); 2110b57cec5SDimitry Andric 2120b57cec5SDimitry Andric return TrapBB; 2130b57cec5SDimitry Andric }; 2140b57cec5SDimitry Andric 2150b57cec5SDimitry Andric // Add the checks. 2160b57cec5SDimitry Andric for (const auto &Entry : TrapInfo) { 2170b57cec5SDimitry Andric Instruction *Inst = Entry.first; 2180b57cec5SDimitry Andric BuilderTy IRB(Inst->getParent(), BasicBlock::iterator(Inst), TargetFolder(DL)); 2190b57cec5SDimitry Andric insertBoundsCheck(Entry.second, IRB, GetTrapBB); 2200b57cec5SDimitry Andric } 2210b57cec5SDimitry Andric 2220b57cec5SDimitry Andric return !TrapInfo.empty(); 2230b57cec5SDimitry Andric } 2240b57cec5SDimitry Andric 2250b57cec5SDimitry Andric PreservedAnalyses BoundsCheckingPass::run(Function &F, FunctionAnalysisManager &AM) { 2260b57cec5SDimitry Andric auto &TLI = AM.getResult<TargetLibraryAnalysis>(F); 2270b57cec5SDimitry Andric auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F); 2280b57cec5SDimitry Andric 2290b57cec5SDimitry Andric if (!addBoundsChecking(F, TLI, SE)) 2300b57cec5SDimitry Andric return PreservedAnalyses::all(); 2310b57cec5SDimitry Andric 2320b57cec5SDimitry Andric return PreservedAnalyses::none(); 2330b57cec5SDimitry Andric } 234