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 SmallString<32> AddrStr;
76 Address.toStringUnsigned(Str&: AddrStr, Radix: 16);
77 reportImmediateUB() << "Try to access a dead memory object at address 0x"
78 << AddrStr << ".";
79 return {};
80 }
81
82 if (IsStore && MO->isConstant()) {
83 reportImmediateUB() << "Try to write to a constant memory object: " << Ptr
84 << ".";
85 return {};
86 }
87
88 if (Address.countr_zero() < Log2(A: Alignment)) {
89 SmallString<32> AddrStr;
90 Address.toStringUnsigned(Str&: AddrStr, Radix: 16);
91 reportImmediateUB() << "Misaligned memory access. Address: 0x" << AddrStr
92 << ", Required alignment: " << Alignment.value() << ".";
93 return {};
94 }
95
96 if (AccessSize > MO->getSize() || Address.ult(RHS: MO->getAddress())) {
97 SmallString<32> AddrStr;
98 Address.toStringUnsigned(Str&: AddrStr, Radix: 16);
99 reportImmediateUB() << "Memory access is out of bounds. Accessed size: "
100 << AccessSize << ", Address: 0x" << AddrStr
101 << ", Object base: 0x"
102 << Twine::utohexstr(Val: MO->getAddress())
103 << ", Object size: " << MO->getSize() << ".";
104 return {};
105 }
106
107 APInt Offset = Address - MO->getAddress();
108
109 if (Offset.ugt(RHS: MO->getSize() - AccessSize)) {
110 SmallString<32> AddrStr;
111 Address.toStringUnsigned(Str&: AddrStr, Radix: 16);
112 reportImmediateUB() << "Memory access is out of bounds. Accessed size: "
113 << AccessSize << ", Address: 0x" << AddrStr
114 << ", Object base: 0x"
115 << Twine::utohexstr(Val: MO->getAddress())
116 << ", Object size: " << MO->getSize() << ".";
117 return {};
118 }
119
120 return {MO, Offset.getZExtValue()};
121}
122
123AnyValue ExecutorBase::load(const AnyValue &Ptr, Align Alignment, Type *ValTy,
124 bool NoUndef) {
125 if (Ptr.isPoison()) {
126 reportImmediateUB() << "Invalid memory access with a poison pointer.";
127 return AnyValue::getPoisonValue(Ctx, Ty: ValTy);
128 }
129 auto &PtrVal = Ptr.asPointer();
130 if (auto [MO, Offset] = verifyMemAccess(
131 Ptr: PtrVal, AccessSize: Ctx.getEffectiveTypeStoreSize(Ty: ValTy), Alignment,
132 /*IsStore=*/false);
133 MO) {
134 bool ContainsUndefinedBits = false;
135 AnyValue Res = Ctx.load(MO&: *MO, Offset, ValTy,
136 ContainsUndefinedBits: NoUndef ? &ContainsUndefinedBits : nullptr);
137 if (NoUndef && ContainsUndefinedBits)
138 reportImmediateUB() << "The value loaded contains undefined bits.";
139 return Res;
140 }
141 return AnyValue::getPoisonValue(Ctx, Ty: ValTy);
142}
143
144void ExecutorBase::store(const AnyValue &Ptr, Align Alignment,
145 const AnyValue &Val, Type *ValTy) {
146 if (Ptr.isPoison()) {
147 reportImmediateUB() << "Invalid memory access with a poison pointer.";
148 return;
149 }
150 auto &PtrVal = Ptr.asPointer();
151 if (auto [MO, Offset] = verifyMemAccess(
152 Ptr: PtrVal, AccessSize: Ctx.getEffectiveTypeStoreSize(Ty: ValTy), Alignment,
153 /*IsStore=*/true);
154 MO)
155 Ctx.store(MO&: *MO, Offset, Val, ValTy);
156}
157
158void ExecutorBase::requestProgramExit(ProgramExitInfo::ProgramExitKind Kind,
159 uint64_t ExitCode) {
160 ExitInfo.emplace(args&: Kind, args&: ExitCode);
161 Handler.onProgramExit(ExitInfo: *ExitInfo);
162}
163
164void ExecutorBase::setFailed() {
165 requestProgramExit(Kind: ProgramExitInfo::ProgramExitKind::Failed);
166}
167
168bool ExecutorBase::hasProgramExited() const { return ExitInfo.has_value(); }
169
170std::optional<ProgramExitInfo> ExecutorBase::getExitInfo() const {
171 return ExitInfo;
172}
173
174unsigned ExecutorBase::getIntSize() const {
175 return CurrentFrame->TLI.getIntSize();
176}
177
178void ExecutorBase::dumpStackTrace() const {
179 errs() << "Stacktrace:\n";
180 const Frame *TheFrame = CurrentFrame;
181 unsigned Index = 0;
182 const AsmParserContext *ParserContext = Ctx.getParserContext();
183 StringRef ModuleFileName = Ctx.getModule().getModuleIdentifier();
184 while (TheFrame != nullptr) {
185 if (TheFrame->BB) {
186 Instruction &Inst = *TheFrame->PC;
187 errs() << "#" << Index++ << " " << Inst << " at ";
188 Inst.getFunction()->printAsOperand(O&: errs(), /*PrintType=*/false);
189 if (ParserContext) {
190 if (auto Loc = ParserContext->getInstructionOrArgumentLocation(&Inst))
191 errs() << ' ' << ModuleFileName << ':' << Loc->Start.Line + 1;
192 }
193 errs() << "\n";
194 }
195 TheFrame = TheFrame->LastFrame;
196 }
197}
198} // namespace llvm::ubi
199