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
30DiagnosticReporter ExecutorBase::reportImmediateUB() {
31 return DiagnosticReporter(*this, DiagnosticKind::ImmediateUB);
32}
33
34DiagnosticReporter ExecutorBase::reportError() {
35 return DiagnosticReporter(*this, DiagnosticKind::Error);
36}
37
38void ExecutorBase::reportImmediateUBString(StringRef Msg) {
39 // Check if we have already reported an immediate UB.
40 if (hasProgramExited())
41 return;
42 dumpStackTrace();
43 requestProgramExit(Kind: ProgramExitInfo::ProgramExitKind::Failed);
44 Handler.onImmediateUB(Msg);
45}
46
47void ExecutorBase::reportErrorString(StringRef Msg) {
48 // Check if we have already reported an error message.
49 if (hasProgramExited())
50 return;
51 dumpStackTrace();
52 requestProgramExit(Kind: ProgramExitInfo::ProgramExitKind::Failed);
53 Handler.onError(Msg);
54}
55
56std::pair<MemoryObject *, uint64_t>
57ExecutorBase::verifyMemAccess(const Pointer &Ptr, uint64_t AccessSize,
58 Align Alignment, bool IsStore) {
59 auto *MO = Ctx.checkProvenance(Ptr, Check: [](const Provenance &) {
60 // TODO: check provenance
61 // TODO: check inrange(S, E)
62 return true;
63 });
64 if (!MO) {
65 reportImmediateUB()
66 << "Invalid memory access via a pointer with nullary provenance.";
67 return {};
68 }
69 const APInt &Address = Ptr.address();
70 // Loading from a stack object outside its lifetime is not undefined
71 // behavior and returns a poison value instead. Storing to it is still
72 // undefined behavior.
73 if (IsStore ? MO->getState() != MemoryObjectState::Alive
74 : MO->getState() == MemoryObjectState::Freed) {
75 reportImmediateUB() << "Try to access a dead memory object at address 0x"
76 << Twine::utohexstr(Val: Address.getZExtValue()) << ".";
77 return {};
78 }
79
80 if (IsStore && MO->isConstant()) {
81 reportImmediateUB() << "Try to write to a constant memory object: " << Ptr
82 << ".";
83 return {};
84 }
85
86 if (Address.countr_zero() < Log2(A: Alignment)) {
87 reportImmediateUB() << "Misaligned memory access. Address: 0x"
88 << Twine::utohexstr(Val: Address.getZExtValue())
89 << ", Required alignment: " << Alignment.value() << ".";
90 return {};
91 }
92
93 if (AccessSize > MO->getSize() || Address.ult(RHS: MO->getAddress())) {
94 reportImmediateUB() << "Memory access is out of bounds. Accessed size: "
95 << AccessSize << ", Address: 0x"
96 << Twine::utohexstr(Val: Address.getZExtValue())
97 << ", Object base: 0x"
98 << Twine::utohexstr(Val: MO->getAddress())
99 << ", Object size: " << MO->getSize() << ".";
100 return {};
101 }
102
103 APInt Offset = Address - MO->getAddress();
104
105 if (Offset.ugt(RHS: MO->getSize() - AccessSize)) {
106 reportImmediateUB() << "Memory access is out of bounds. Accessed size: "
107 << AccessSize << ", Address: 0x"
108 << Twine::utohexstr(Val: Address.getZExtValue())
109 << ", Object base: 0x"
110 << Twine::utohexstr(Val: MO->getAddress())
111 << ", Object size: " << MO->getSize() << ".";
112 return {};
113 }
114
115 return {MO, Offset.getZExtValue()};
116}
117
118AnyValue ExecutorBase::load(const AnyValue &Ptr, Align Alignment, Type *ValTy,
119 bool NoUndef) {
120 if (Ptr.isPoison()) {
121 reportImmediateUB() << "Invalid memory access with a poison pointer.";
122 return AnyValue::getPoisonValue(Ctx, Ty: ValTy);
123 }
124 auto &PtrVal = Ptr.asPointer();
125 if (auto [MO, Offset] = verifyMemAccess(
126 Ptr: PtrVal, AccessSize: Ctx.getEffectiveTypeStoreSize(Ty: ValTy), Alignment,
127 /*IsStore=*/false);
128 MO) {
129 bool ContainsUndefinedBits = false;
130 AnyValue Res = Ctx.load(MO&: *MO, Offset, ValTy,
131 ContainsUndefinedBits: NoUndef ? &ContainsUndefinedBits : nullptr);
132 if (NoUndef && ContainsUndefinedBits)
133 reportImmediateUB() << "The value loaded contains undefined bits.";
134 return Res;
135 }
136 return AnyValue::getPoisonValue(Ctx, Ty: ValTy);
137}
138
139void ExecutorBase::store(const AnyValue &Ptr, Align Alignment,
140 const AnyValue &Val, Type *ValTy) {
141 if (Ptr.isPoison()) {
142 reportImmediateUB() << "Invalid memory access with a poison pointer.";
143 return;
144 }
145 auto &PtrVal = Ptr.asPointer();
146 if (auto [MO, Offset] = verifyMemAccess(
147 Ptr: PtrVal, AccessSize: Ctx.getEffectiveTypeStoreSize(Ty: ValTy), Alignment,
148 /*IsStore=*/true);
149 MO)
150 Ctx.store(MO&: *MO, Offset, Val, ValTy);
151}
152
153void ExecutorBase::requestProgramExit(ProgramExitInfo::ProgramExitKind Kind,
154 uint64_t ExitCode) {
155 ExitInfo.emplace(args&: Kind, args&: ExitCode);
156 Handler.onProgramExit(ExitInfo: *ExitInfo);
157}
158
159void ExecutorBase::setFailed() {
160 requestProgramExit(Kind: ProgramExitInfo::ProgramExitKind::Failed);
161}
162
163bool ExecutorBase::hasProgramExited() const { return ExitInfo.has_value(); }
164
165std::optional<ProgramExitInfo> ExecutorBase::getExitInfo() const {
166 return ExitInfo;
167}
168
169unsigned ExecutorBase::getIntSize() const {
170 return CurrentFrame->TLI.getIntSize();
171}
172
173void ExecutorBase::dumpStackTrace() const {
174 errs() << "Stacktrace:\n";
175 const Frame *TheFrame = CurrentFrame;
176 unsigned Index = 0;
177 const AsmParserContext *ParserContext = Ctx.getParserContext();
178 StringRef ModuleFileName = Ctx.getModule().getModuleIdentifier();
179 while (TheFrame != nullptr) {
180 if (TheFrame->BB) {
181 Instruction &Inst = *TheFrame->PC;
182 errs() << "#" << Index++ << " " << Inst << " at ";
183 Inst.getFunction()->printAsOperand(O&: errs(), /*PrintType=*/false);
184 if (ParserContext) {
185 if (auto Loc = ParserContext->getInstructionOrArgumentLocation(&Inst))
186 errs() << ' ' << ModuleFileName << ':' << Loc->Start.Line + 1;
187 }
188 errs() << "\n";
189 }
190 TheFrame = TheFrame->LastFrame;
191 }
192}
193} // namespace llvm::ubi
194