1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "absint.h"
17 #include "exec_context.h"
18 #include "file_items.h"
19 #include "include/thread_scopes.h"
20 #include "plugins.h"
21 #include "verification_context.h"
22
23 #include "verification/jobs/job.h"
24
25 #include "value/abstract_typed_value.h"
26 #include "type/type_system.h"
27
28 #include "cflow/cflow_info.h"
29
30 #include "runtime/include/method.h"
31 #include "runtime/include/method-inl.h"
32
33 #include "macros.h"
34
35 #include <locale>
36 #include <optional>
37
38 #include "abs_int_inl.h"
39 #include "handle_gen.h"
40
41 #include "util/str.h"
42 #include "util/hash.h"
43
44 #include <utility>
45 #include <tuple>
46 #include <functional>
47
48 namespace panda::verifier {
49
50 #include "abs_int_inl_gen.h"
51
52 } // namespace panda::verifier
53
54 namespace panda::verifier {
55
56 using TryBlock = panda_file::CodeDataAccessor::TryBlock;
57 using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
58
PrepareVerificationContext(TypeSystem * typeSystem,Job const * job)59 VerificationContext PrepareVerificationContext(TypeSystem *typeSystem, Job const *job)
60 {
61 auto *method = job->JobMethod();
62
63 auto *klass = method->GetClass();
64
65 Type methodClassType {klass};
66
67 VerificationContext verifCtx {typeSystem, job, methodClassType};
68 // NOTE(vdyadov): ASSERT(cflow_info corresponds method)
69
70 auto &cflowInfo = verifCtx.CflowInfo();
71 auto &execCtx = verifCtx.ExecCtx();
72
73 LOG_VERIFIER_DEBUG_METHOD_VERIFICATION(method->GetFullName());
74
75 /*
76 1. build initial regContext for the method entry
77 */
78 RegContext ®Ctx = verifCtx.ExecCtx().CurrentRegContext();
79 regCtx.Clear();
80
81 auto numVregs = method->GetNumVregs();
82 regCtx[numVregs] = AbstractTypedValue {};
83
84 const auto &signature = typeSystem->GetMethodSignature(method);
85
86 for (size_t idx = 0; idx < signature->args.size(); ++idx) {
87 Type const &t = signature->args[idx];
88 regCtx[numVregs++] = AbstractTypedValue {t, verifCtx.NewVar(), AbstractTypedValue::Start {}, idx};
89 }
90 LOG_VERIFIER_DEBUG_REGISTERS("registers =", regCtx.DumpRegs(typeSystem));
91
92 verifCtx.SetReturnType(&signature->result);
93
94 LOG_VERIFIER_DEBUG_RESULT(signature->result.ToString(typeSystem));
95
96 /*
97 3. Add checkpoint for exc. handlers
98 */
99 method->EnumerateTryBlocks([&](TryBlock const &tryBlock) {
100 uint8_t const *code = method->GetInstructions();
101 uint8_t const *tryStartPc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(code) +
102 static_cast<uintptr_t>(tryBlock.GetStartPc()));
103 uint8_t const *tryEndPc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(tryStartPc) +
104 static_cast<uintptr_t>(tryBlock.GetLength()));
105 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
106 for (uint8_t const *pc = tryStartPc; pc < tryEndPc; pc++) {
107 if (cflowInfo.IsFlagSet(pc, CflowMethodInfo::EXCEPTION_SOURCE)) {
108 execCtx.SetCheckPoint(pc);
109 }
110 }
111 return true;
112 });
113
114 /*
115 3. add Start entry of method
116 */
117
118 const uint8_t *methodPcStartPtr = method->GetInstructions();
119
120 verifCtx.ExecCtx().AddEntryPoint(methodPcStartPtr, EntryPointType::METHOD_BODY);
121 verifCtx.ExecCtx().StoreCurrentRegContextForAddr(methodPcStartPtr);
122
123 return verifCtx;
124 }
125
126 namespace {
127
VerifyEntryPoints(VerificationContext & verifCtx,ExecContext & execCtx)128 VerificationStatus VerifyEntryPoints(VerificationContext &verifCtx, ExecContext &execCtx)
129 {
130 const auto &debugOpts = GetServiceConfig(verifCtx.GetJob()->GetService())->opts.debug;
131
132 /*
133 Start main loop: get entry point with context, process, repeat
134 */
135
136 uint8_t const *entryPoint = nullptr;
137 EntryPointType entryType;
138 VerificationStatus worstSoFar = VerificationStatus::OK;
139
140 while (execCtx.GetEntryPointForChecking(&entryPoint, &entryType) == ExecContext::Status::OK) {
141 #ifndef NDEBUG
142 const void *codeStart = verifCtx.GetJob()->JobMethod()->GetInstructions();
143 LOG_VERIFIER_DEBUG_CODE_BLOCK_VERIFICATION(
144 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(entryPoint) - reinterpret_cast<uintptr_t>(codeStart)),
145 (entryType == EntryPointType::METHOD_BODY ? "method body" : "exception handler"));
146 #endif
147 auto result = AbstractInterpret(verifCtx, entryPoint, entryType);
148 if (debugOpts.allow.errorInExceptionHandler && entryType == EntryPointType::EXCEPTION_HANDLER &&
149 result == VerificationStatus::ERROR) {
150 result = VerificationStatus::WARNING;
151 }
152 if (result == VerificationStatus::ERROR) {
153 return result;
154 }
155 worstSoFar = std::max(worstSoFar, result);
156 }
157
158 return worstSoFar;
159 }
160
ComputeRegContext(Method const * method,TryBlock const * tryBlock,VerificationContext & verifCtx,RegContext * regContext)161 bool ComputeRegContext(Method const *method, TryBlock const *tryBlock, VerificationContext &verifCtx,
162 RegContext *regContext)
163 {
164 auto &cflowInfo = verifCtx.CflowInfo();
165 auto &execCtx = verifCtx.ExecCtx();
166 auto *typeSystem = verifCtx.GetTypeSystem();
167 auto start = reinterpret_cast<uint8_t const *>(reinterpret_cast<uintptr_t>(method->GetInstructions()) +
168 tryBlock->GetStartPc());
169 auto end = reinterpret_cast<uint8_t const *>(reinterpret_cast<uintptr_t>(method->GetInstructions()) +
170 tryBlock->GetStartPc() + tryBlock->GetLength());
171
172 LOG_VERIFIER_DEBUG_TRY_BLOCK_COMMON_CONTEXT_COMPUTATION(tryBlock->GetStartPc(),
173 tryBlock->GetStartPc() + tryBlock->GetLength());
174
175 bool first = true;
176 execCtx.ForContextsOnCheckPointsInRange(start, end, [&](const uint8_t *pc, const RegContext &ctx) {
177 if (cflowInfo.IsFlagSet(pc, CflowMethodInfo::EXCEPTION_SOURCE)) {
178 LOG_VERIFIER_DEBUG_REGISTERS("+", ctx.DumpRegs(typeSystem));
179 if (first) {
180 first = false;
181 *regContext = ctx;
182 } else {
183 regContext->UnionWith(&ctx, typeSystem);
184 }
185 }
186 return true;
187 });
188 LOG_VERIFIER_DEBUG_REGISTERS("=", regContext->DumpRegs(typeSystem));
189
190 if (first) {
191 // return false when the try block does not have instructions to throw exception
192 return false;
193 }
194
195 regContext->RemoveInconsistentRegs();
196
197 #ifndef NDEBUG
198 if (regContext->HasInconsistentRegs()) {
199 LOG_VERIFIER_COMMON_CONTEXT_INCONSISTENT_REGISTER_HEADER();
200 for (int regNum : regContext->InconsistentRegsNums()) {
201 LOG(DEBUG, VERIFIER) << AbsIntInstructionHandler::RegisterName(regNum);
202 }
203 }
204 #endif
205
206 return true;
207 }
208
VerifyExcHandler(TryBlock const * tryBlock,CatchBlock const * catchBlock,VerificationContext * verifCtx,RegContext * regContext)209 VerificationStatus VerifyExcHandler([[maybe_unused]] TryBlock const *tryBlock, CatchBlock const *catchBlock,
210 VerificationContext *verifCtx, RegContext *regContext)
211 {
212 Method const *method = verifCtx->GetMethod();
213 auto &execCtx = verifCtx->ExecCtx();
214 auto exceptionIdx = catchBlock->GetTypeIdx();
215 auto langCtx = LanguageContext(plugins::GetLanguageContextBase(method->GetClass()->GetSourceLang()));
216 Class *exception = nullptr;
217 if (exceptionIdx != panda_file::INVALID_INDEX) {
218 ScopedChangeThreadStatus st {ManagedThread::GetCurrent(), ThreadStatus::RUNNING};
219 exception = verifCtx->GetJob()->GetService()->classLinker->GetExtension(langCtx)->GetClass(
220 *method->GetPandaFile(), method->GetClass()->ResolveClassIndex(exceptionIdx));
221 }
222
223 LOG(DEBUG, VERIFIER) << "Exception handler at " << std::hex << "0x" << catchBlock->GetHandlerPc()
224 << (exception != nullptr
225 ? PandaString {", for exception '"} + PandaString {exception->GetName()} + "' "
226 : PandaString {""})
227 << ", try block scope: [ "
228 << "0x" << tryBlock->GetStartPc() << ", "
229 << "0x" << (tryBlock->GetStartPc() + tryBlock->GetLength()) << " ]";
230
231 Type exceptionType {};
232 if (exception != nullptr) {
233 exceptionType = Type {exception};
234 } else {
235 exceptionType = verifCtx->GetTypeSystem()->Throwable();
236 }
237
238 if (exceptionType.IsConsistent()) {
239 const int acc = -1;
240 (*regContext)[acc] = AbstractTypedValue {exceptionType, verifCtx->NewVar()};
241 }
242
243 auto startPc = reinterpret_cast<uint8_t const *>(
244 reinterpret_cast<uintptr_t>(verifCtx->GetMethod()->GetInstructions()) + catchBlock->GetHandlerPc());
245
246 execCtx.CurrentRegContext() = *regContext;
247 execCtx.AddEntryPoint(startPc, EntryPointType::EXCEPTION_HANDLER);
248 execCtx.StoreCurrentRegContextForAddr(startPc);
249
250 return VerifyEntryPoints(*verifCtx, execCtx);
251 }
252
253 } // namespace
254
VerifyMethod(VerificationContext & verifCtx)255 VerificationStatus VerifyMethod(VerificationContext &verifCtx)
256 {
257 VerificationStatus worstSoFar = VerificationStatus::OK;
258 auto &execCtx = verifCtx.ExecCtx();
259
260 worstSoFar = std::max(worstSoFar, VerifyEntryPoints(verifCtx, execCtx));
261 if (worstSoFar == VerificationStatus::ERROR) {
262 return worstSoFar;
263 }
264
265 // Need to have the try blocks sorted!
266 verifCtx.GetMethod()->EnumerateTryBlocks([&](TryBlock &tryBlock) {
267 bool tryBlockCanThrow = true;
268 RegContext regContext;
269 tryBlockCanThrow = ComputeRegContext(verifCtx.GetMethod(), &tryBlock, verifCtx, ®Context);
270 if (!tryBlockCanThrow) {
271 // catch block is unreachable
272 } else {
273 tryBlock.EnumerateCatchBlocks([&](CatchBlock const &catchBlock) {
274 worstSoFar = std::max(worstSoFar, VerifyExcHandler(&tryBlock, &catchBlock, &verifCtx, ®Context));
275 return (worstSoFar != VerificationStatus::ERROR);
276 });
277 }
278 return true;
279 });
280
281 if (worstSoFar == VerificationStatus::ERROR) {
282 return worstSoFar;
283 }
284
285 // NOTE(vdyadov): account for dead code
286 const uint8_t *dummyEntryPoint;
287 EntryPointType dummyEntryType;
288
289 if (execCtx.GetEntryPointForChecking(&dummyEntryPoint, &dummyEntryType) ==
290 ExecContext::Status::NO_ENTRY_POINTS_WITH_CONTEXT) {
291 // NOTE(vdyadov): log remaining entry points as unreachable
292 worstSoFar = std::max(worstSoFar, VerificationStatus::WARNING);
293 }
294
295 return worstSoFar;
296 }
297
298 } // namespace panda::verifier
299