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
15namespace llvm::ubi {
16Frame::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
30void 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
39void 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
47std::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
81AnyValue 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
107void 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