| 1 | //===- ExecutorBase.cpp - Non-visitor methods of InstExecutor -------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file implements non-visitor methods of InstExecutor for code reuse. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "ExecutorBase.h" |
| 14 | |
| 15 | namespace llvm::ubi { |
| 16 | Frame::Frame(Function &F, CallBase *CallSite, Frame *LastFrame, |
| 17 | ArrayRef<AnyValue> Args, AnyValue &RetVal, |
| 18 | const TargetLibraryInfoImpl &TLIImpl) |
| 19 | : Func(F), LastFrame(LastFrame), CallSite(CallSite), Args(Args), |
| 20 | RetVal(RetVal), TLI(TLIImpl, &F) { |
| 21 | assert((Args.size() == F.arg_size() || |
| 22 | (F.isVarArg() && Args.size() >= F.arg_size())) && |
| 23 | "Expected enough arguments to call the function." ); |
| 24 | BB = &Func.getEntryBlock(); |
| 25 | PC = BB->begin(); |
| 26 | for (Argument &Arg : F.args()) |
| 27 | ValueMap[&Arg] = Args[Arg.getArgNo()]; |
| 28 | } |
| 29 | |
| 30 | void ExecutorBase::reportImmediateUB(StringRef Msg) { |
| 31 | // Check if we have already reported an immediate UB. |
| 32 | if (!Status) |
| 33 | return; |
| 34 | Status = false; |
| 35 | // TODO: Provide stack trace information. |
| 36 | Handler.onImmediateUB(Msg); |
| 37 | } |
| 38 | |
| 39 | void ExecutorBase::reportError(StringRef Msg) { |
| 40 | // Check if we have already reported an error message. |
| 41 | if (!Status) |
| 42 | return; |
| 43 | Status = false; |
| 44 | Handler.onError(Msg); |
| 45 | } |
| 46 | |
| 47 | std::optional<uint64_t> ExecutorBase::verifyMemAccess(const MemoryObject &MO, |
| 48 | const APInt &Address, |
| 49 | uint64_t AccessSize, |
| 50 | Align Alignment, |
| 51 | bool IsStore) { |
| 52 | // Loading from a stack object outside its lifetime is not undefined |
| 53 | // behavior and returns a poison value instead. Storing to it is still |
| 54 | // undefined behavior. |
| 55 | if (IsStore ? MO.getState() != MemoryObjectState::Alive |
| 56 | : MO.getState() == MemoryObjectState::Freed) { |
| 57 | reportImmediateUB(Msg: "Try to access a dead memory object." ); |
| 58 | return std::nullopt; |
| 59 | } |
| 60 | |
| 61 | if (Address.countr_zero() < Log2(A: Alignment)) { |
| 62 | reportImmediateUB(Msg: "Misaligned memory access." ); |
| 63 | return std::nullopt; |
| 64 | } |
| 65 | |
| 66 | if (AccessSize > MO.getSize() || Address.ult(RHS: MO.getAddress())) { |
| 67 | reportImmediateUB(Msg: "Memory access is out of bounds." ); |
| 68 | return std::nullopt; |
| 69 | } |
| 70 | |
| 71 | APInt Offset = Address - MO.getAddress(); |
| 72 | |
| 73 | if (Offset.ugt(RHS: MO.getSize() - AccessSize)) { |
| 74 | reportImmediateUB(Msg: "Memory access is out of bounds." ); |
| 75 | return std::nullopt; |
| 76 | } |
| 77 | |
| 78 | return Offset.getZExtValue(); |
| 79 | } |
| 80 | |
| 81 | AnyValue ExecutorBase::load(const AnyValue &Ptr, Align Alignment, Type *ValTy) { |
| 82 | if (Ptr.isPoison()) { |
| 83 | reportImmediateUB(Msg: "Invalid memory access with a poison pointer." ); |
| 84 | return AnyValue::getPoisonValue(Ctx, Ty: ValTy); |
| 85 | } |
| 86 | auto &PtrVal = Ptr.asPointer(); |
| 87 | auto *MO = PtrVal.getMemoryObject(); |
| 88 | if (!MO) { |
| 89 | reportImmediateUB( |
| 90 | Msg: "Invalid memory access via a pointer with nullary provenance." ); |
| 91 | return AnyValue::getPoisonValue(Ctx, Ty: ValTy); |
| 92 | } |
| 93 | // TODO: pointer capability check |
| 94 | if (auto Offset = |
| 95 | verifyMemAccess(MO: *MO, Address: PtrVal.address(), |
| 96 | AccessSize: Ctx.getEffectiveTypeStoreSize(Ty: ValTy), Alignment, |
| 97 | /*IsStore=*/false)) { |
| 98 | // Load from a dead stack object yields poison value. |
| 99 | if (MO->getState() == MemoryObjectState::Dead) |
| 100 | return AnyValue::getPoisonValue(Ctx, Ty: ValTy); |
| 101 | |
| 102 | return Ctx.load(MO&: *MO, Offset: *Offset, ValTy); |
| 103 | } |
| 104 | return AnyValue::getPoisonValue(Ctx, Ty: ValTy); |
| 105 | } |
| 106 | |
| 107 | void ExecutorBase::store(const AnyValue &Ptr, Align Alignment, |
| 108 | const AnyValue &Val, Type *ValTy) { |
| 109 | if (Ptr.isPoison()) { |
| 110 | reportImmediateUB(Msg: "Invalid memory access with a poison pointer." ); |
| 111 | return; |
| 112 | } |
| 113 | auto &PtrVal = Ptr.asPointer(); |
| 114 | auto *MO = PtrVal.getMemoryObject(); |
| 115 | if (!MO) { |
| 116 | reportImmediateUB( |
| 117 | Msg: "Invalid memory access via a pointer with nullary provenance." ); |
| 118 | return; |
| 119 | } |
| 120 | // TODO: pointer capability check |
| 121 | if (auto Offset = |
| 122 | verifyMemAccess(MO: *MO, Address: PtrVal.address(), |
| 123 | AccessSize: Ctx.getEffectiveTypeStoreSize(Ty: ValTy), Alignment, |
| 124 | /*IsStore=*/true)) |
| 125 | Ctx.store(MO&: *MO, Offset: *Offset, Val, ValTy); |
| 126 | } |
| 127 | } // namespace llvm::ubi |
| 128 | |