Path: blob/main/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
35266 views
//===- BoundsChecking.cpp - Instrumentation for run-time bounds checking --===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/Transforms/Instrumentation/BoundsChecking.h"9#include "llvm/ADT/Statistic.h"10#include "llvm/ADT/Twine.h"11#include "llvm/Analysis/MemoryBuiltins.h"12#include "llvm/Analysis/ScalarEvolution.h"13#include "llvm/Analysis/TargetFolder.h"14#include "llvm/Analysis/TargetLibraryInfo.h"15#include "llvm/IR/BasicBlock.h"16#include "llvm/IR/Constants.h"17#include "llvm/IR/DataLayout.h"18#include "llvm/IR/Function.h"19#include "llvm/IR/IRBuilder.h"20#include "llvm/IR/InstIterator.h"21#include "llvm/IR/Instruction.h"22#include "llvm/IR/Instructions.h"23#include "llvm/IR/Intrinsics.h"24#include "llvm/IR/Value.h"25#include "llvm/Support/Casting.h"26#include "llvm/Support/CommandLine.h"27#include "llvm/Support/Debug.h"28#include "llvm/Support/raw_ostream.h"29#include <cstdint>30#include <utility>3132using namespace llvm;3334#define DEBUG_TYPE "bounds-checking"3536static cl::opt<bool> SingleTrapBB("bounds-checking-single-trap",37cl::desc("Use one trap block per function"));3839static cl::opt<bool> DebugTrapBB("bounds-checking-unique-traps",40cl::desc("Always use one trap per check"));4142STATISTIC(ChecksAdded, "Bounds checks added");43STATISTIC(ChecksSkipped, "Bounds checks skipped");44STATISTIC(ChecksUnable, "Bounds checks unable to add");4546using BuilderTy = IRBuilder<TargetFolder>;4748/// Gets the conditions under which memory accessing instructions will overflow.49///50/// \p Ptr is the pointer that will be read/written, and \p InstVal is either51/// the result from the load or the value being stored. It is used to determine52/// the size of memory block that is touched.53///54/// Returns the condition under which the access will overflow.55static Value *getBoundsCheckCond(Value *Ptr, Value *InstVal,56const DataLayout &DL, TargetLibraryInfo &TLI,57ObjectSizeOffsetEvaluator &ObjSizeEval,58BuilderTy &IRB, ScalarEvolution &SE) {59TypeSize NeededSize = DL.getTypeStoreSize(InstVal->getType());60LLVM_DEBUG(dbgs() << "Instrument " << *Ptr << " for " << Twine(NeededSize)61<< " bytes\n");6263SizeOffsetValue SizeOffset = ObjSizeEval.compute(Ptr);6465if (!SizeOffset.bothKnown()) {66++ChecksUnable;67return nullptr;68}6970Value *Size = SizeOffset.Size;71Value *Offset = SizeOffset.Offset;72ConstantInt *SizeCI = dyn_cast<ConstantInt>(Size);7374Type *IndexTy = DL.getIndexType(Ptr->getType());75Value *NeededSizeVal = IRB.CreateTypeSize(IndexTy, NeededSize);7677auto SizeRange = SE.getUnsignedRange(SE.getSCEV(Size));78auto OffsetRange = SE.getUnsignedRange(SE.getSCEV(Offset));79auto NeededSizeRange = SE.getUnsignedRange(SE.getSCEV(NeededSizeVal));8081// three checks are required to ensure safety:82// . Offset >= 0 (since the offset is given from the base ptr)83// . Size >= Offset (unsigned)84// . Size - Offset >= NeededSize (unsigned)85//86// optimization: if Size >= 0 (signed), skip 1st check87// FIXME: add NSW/NUW here? -- we dont care if the subtraction overflows88Value *ObjSize = IRB.CreateSub(Size, Offset);89Value *Cmp2 = SizeRange.getUnsignedMin().uge(OffsetRange.getUnsignedMax())90? ConstantInt::getFalse(Ptr->getContext())91: IRB.CreateICmpULT(Size, Offset);92Value *Cmp3 = SizeRange.sub(OffsetRange)93.getUnsignedMin()94.uge(NeededSizeRange.getUnsignedMax())95? ConstantInt::getFalse(Ptr->getContext())96: IRB.CreateICmpULT(ObjSize, NeededSizeVal);97Value *Or = IRB.CreateOr(Cmp2, Cmp3);98if ((!SizeCI || SizeCI->getValue().slt(0)) &&99!SizeRange.getSignedMin().isNonNegative()) {100Value *Cmp1 = IRB.CreateICmpSLT(Offset, ConstantInt::get(IndexTy, 0));101Or = IRB.CreateOr(Cmp1, Or);102}103104return Or;105}106107/// Adds run-time bounds checks to memory accessing instructions.108///109/// \p Or is the condition that should guard the trap.110///111/// \p GetTrapBB is a callable that returns the trap BB to use on failure.112template <typename GetTrapBBT>113static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) {114// check if the comparison is always false115ConstantInt *C = dyn_cast_or_null<ConstantInt>(Or);116if (C) {117++ChecksSkipped;118// If non-zero, nothing to do.119if (!C->getZExtValue())120return;121}122++ChecksAdded;123124BasicBlock::iterator SplitI = IRB.GetInsertPoint();125BasicBlock *OldBB = SplitI->getParent();126BasicBlock *Cont = OldBB->splitBasicBlock(SplitI);127OldBB->getTerminator()->eraseFromParent();128129if (C) {130// If we have a constant zero, unconditionally branch.131// FIXME: We should really handle this differently to bypass the splitting132// the block.133BranchInst::Create(GetTrapBB(IRB), OldBB);134return;135}136137// Create the conditional branch.138BranchInst::Create(GetTrapBB(IRB), Cont, Or, OldBB);139}140141static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,142ScalarEvolution &SE) {143if (F.hasFnAttribute(Attribute::NoSanitizeBounds))144return false;145146const DataLayout &DL = F.getDataLayout();147ObjectSizeOpts EvalOpts;148EvalOpts.RoundToAlign = true;149EvalOpts.EvalMode = ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset;150ObjectSizeOffsetEvaluator ObjSizeEval(DL, &TLI, F.getContext(), EvalOpts);151152// check HANDLE_MEMORY_INST in include/llvm/Instruction.def for memory153// touching instructions154SmallVector<std::pair<Instruction *, Value *>, 4> TrapInfo;155for (Instruction &I : instructions(F)) {156Value *Or = nullptr;157BuilderTy IRB(I.getParent(), BasicBlock::iterator(&I), TargetFolder(DL));158if (LoadInst *LI = dyn_cast<LoadInst>(&I)) {159if (!LI->isVolatile())160Or = getBoundsCheckCond(LI->getPointerOperand(), LI, DL, TLI,161ObjSizeEval, IRB, SE);162} else if (StoreInst *SI = dyn_cast<StoreInst>(&I)) {163if (!SI->isVolatile())164Or = getBoundsCheckCond(SI->getPointerOperand(), SI->getValueOperand(),165DL, TLI, ObjSizeEval, IRB, SE);166} else if (AtomicCmpXchgInst *AI = dyn_cast<AtomicCmpXchgInst>(&I)) {167if (!AI->isVolatile())168Or =169getBoundsCheckCond(AI->getPointerOperand(), AI->getCompareOperand(),170DL, TLI, ObjSizeEval, IRB, SE);171} else if (AtomicRMWInst *AI = dyn_cast<AtomicRMWInst>(&I)) {172if (!AI->isVolatile())173Or = getBoundsCheckCond(AI->getPointerOperand(), AI->getValOperand(),174DL, TLI, ObjSizeEval, IRB, SE);175}176if (Or)177TrapInfo.push_back(std::make_pair(&I, Or));178}179180// Create a trapping basic block on demand using a callback. Depending on181// flags, this will either create a single block for the entire function or182// will create a fresh block every time it is called.183BasicBlock *TrapBB = nullptr;184auto GetTrapBB = [&TrapBB](BuilderTy &IRB) {185Function *Fn = IRB.GetInsertBlock()->getParent();186auto DebugLoc = IRB.getCurrentDebugLocation();187IRBuilder<>::InsertPointGuard Guard(IRB);188189if (TrapBB && SingleTrapBB && !DebugTrapBB)190return TrapBB;191192TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn);193IRB.SetInsertPoint(TrapBB);194195Intrinsic::ID IntrID = DebugTrapBB ? Intrinsic::ubsantrap : Intrinsic::trap;196auto *F = Intrinsic::getDeclaration(Fn->getParent(), IntrID);197198CallInst *TrapCall;199if (DebugTrapBB) {200TrapCall =201IRB.CreateCall(F, ConstantInt::get(IRB.getInt8Ty(), Fn->size()));202} else {203TrapCall = IRB.CreateCall(F, {});204}205206TrapCall->setDoesNotReturn();207TrapCall->setDoesNotThrow();208TrapCall->setDebugLoc(DebugLoc);209IRB.CreateUnreachable();210211return TrapBB;212};213214// Add the checks.215for (const auto &Entry : TrapInfo) {216Instruction *Inst = Entry.first;217BuilderTy IRB(Inst->getParent(), BasicBlock::iterator(Inst), TargetFolder(DL));218insertBoundsCheck(Entry.second, IRB, GetTrapBB);219}220221return !TrapInfo.empty();222}223224PreservedAnalyses BoundsCheckingPass::run(Function &F, FunctionAnalysisManager &AM) {225auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);226auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);227228if (!addBoundsChecking(F, TLI, SE))229return PreservedAnalyses::all();230231return PreservedAnalyses::none();232}233234235