xref: /freebsd-src/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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