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 "verification_context.h"
19
20 #include "verification/jobs/job.h"
21 #include "verification/jobs/cache.h"
22
23 #include "value/abstract_typed_value.h"
24 #include "type/type_system.h"
25
26 #include "cflow/cflow_info.h"
27
28 #include "runtime/include/method.h"
29
30 #include "macros.h"
31
32 #include <optional>
33
34 #include "abs_int_inl.h"
35
36 #include "util/str.h"
37 #include "util/hash.h"
38
39 #include <utility>
40 #include <tuple>
41 #include <functional>
42
43 namespace panda::verifier {
44
45 #include "abs_int_inl_gen.h"
46
47 } // namespace panda::verifier
48
49 namespace panda::verifier {
50
PrepareVerificationContext(PandaTypes & pandaTypes,const Job & job)51 VerificationContext PrepareVerificationContext(PandaTypes &pandaTypes, const Job &job)
52 {
53 auto &cached_method = job.JobCachedMethod();
54
55 auto &klass = cached_method.klass;
56
57 Type method_class_type = pandaTypes.TypeOf(klass);
58
59 VerificationContext verifCtx {pandaTypes, job, method_class_type};
60 // TODO(vdyadov): ASSERT(cflow_info corresponds method)
61
62 auto &cflow_info = verifCtx.CflowInfo();
63 auto &exec_ctx = verifCtx.ExecCtx();
64
65 LOG_VERIFIER_DEBUG_METHOD_VERIFICATION(cached_method.GetName());
66
67 /*
68 1. build initial reg_context for the method entry
69 */
70 RegContext ®_ctx = verifCtx.ExecCtx().CurrentRegContext();
71 reg_ctx.Clear();
72
73 auto num_vregs = cached_method.num_vregs;
74
75 const auto &signature = verifCtx.Types().MethodSignature(cached_method);
76
77 for (size_t idx = 0; idx < signature.size() - 1; ++idx) {
78 const Type &t = pandaTypes.TypeOf(signature[idx]);
79 reg_ctx[num_vregs++] = AbstractTypedValue {t, verifCtx.NewVar(), AbstractTypedValue::Start {}, idx};
80 }
81 LOG_VERIFIER_DEBUG_REGISTERS("registers =",
82 reg_ctx.DumpRegs([&pandaTypes](const auto &t) { return pandaTypes.ImageOf(t); }));
83
84 verifCtx.SetReturnType(pandaTypes.TypeOf(signature.back()));
85
86 LOG_VERIFIER_DEBUG_RESULT(pandaTypes.ImageOf(verifCtx.ReturnType()));
87
88 /*
89 3. Add checkpoint for exc. handlers
90 */
91
92 for (const auto &exc_handler : cflow_info.ExcHandlers()) {
93 auto &&handler = [&exec_ctx](const uint8_t *pc) {
94 exec_ctx.SetCheckPoint(pc);
95 return true;
96 };
97 cflow_info.ExcSrcMap().ForSourcesInRange(exc_handler.TryBlock.Start, exc_handler.TryBlock.End, handler);
98 }
99
100 /*
101 3. add start entry of method
102 */
103
104 const uint8_t *method_pc_start_ptr = cached_method.bytecode;
105
106 verifCtx.ExecCtx().AddEntryPoint(method_pc_start_ptr, EntryPointType::METHOD_BODY);
107 verifCtx.ExecCtx().StoreCurrentRegContextForAddr(method_pc_start_ptr);
108
109 return verifCtx;
110 }
111
112 namespace {
113
VerifyEntryPoints(VerificationContext & verifCtx,ExecContext & exec_ctx)114 VerificationStatus VerifyEntryPoints(VerificationContext &verifCtx, ExecContext &exec_ctx)
115 {
116 const auto &debug_opts = Runtime::GetCurrent()->GetVerificationOptions().Debug;
117
118 /*
119 start main loop: get entry point with context, process, repeat
120 */
121
122 uint8_t const *entry_point = nullptr;
123 EntryPointType entry_type;
124 VerificationStatus worstSoFar = VerificationStatus::OK;
125
126 while (exec_ctx.GetEntryPointForChecking(&entry_point, &entry_type) == ExecContext::Status::OK) {
127 #ifndef NDEBUG
128 auto &cflow_info = verifCtx.CflowInfo();
129 const void *codeStart = cflow_info.InstMap().AddrStart<const void *>();
130 LOG_VERIFIER_DEBUG_CODE_BLOCK_VERIFICATION(
131 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(entry_point) - reinterpret_cast<uintptr_t>(codeStart)),
132 (entry_type == EntryPointType::METHOD_BODY ? "method body" : "exception handler"));
133 #endif
134 auto result = AbstractInterpret(verifCtx, entry_point, entry_type);
135 if (debug_opts.Allow.ErrorInExceptionHandler && entry_type == EntryPointType::EXCEPTION_HANDLER &&
136 result == VerificationStatus::ERROR) {
137 result = VerificationStatus::WARNING;
138 }
139 if (result == VerificationStatus::ERROR) {
140 return result;
141 }
142 worstSoFar = std::max(worstSoFar, result);
143 }
144
145 return worstSoFar;
146 }
147
ComputeRegContext(CflowExcHandlerInfo const & exc_handler,VerificationContext & verifCtx,RegContext * reg_context)148 void ComputeRegContext(CflowExcHandlerInfo const &exc_handler, VerificationContext &verifCtx, RegContext *reg_context)
149 {
150 auto &cflow_info = verifCtx.CflowInfo();
151 auto &exec_ctx = verifCtx.ExecCtx();
152
153 #ifndef NDEBUG
154 const void *codeStart = cflow_info.InstMap().AddrStart<const void *>();
155 auto takeAddress = [&](const void *ptr) {
156 return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(codeStart));
157 };
158
159 LOG_VERIFIER_DEBUG_EXCEPTION_HANDLER_COMMON_CONTEXT_COMPUTATION(takeAddress(exc_handler.Start), "",
160 takeAddress(exc_handler.TryBlock.Start),
161 takeAddress(exc_handler.TryBlock.End));
162 #endif
163
164 #ifndef NDEBUG
165 auto image_of = [&verifCtx](const auto &t) { return verifCtx.Types().ImageOf(t); };
166 #endif
167
168 bool first = true;
169 exec_ctx.ForContextsOnCheckPointsInRange(
170 #ifndef NDEBUG
171 exc_handler.TryBlock.Start, exc_handler.TryBlock.End,
172 [&cflow_info, ®_context, &image_of, &first](const uint8_t *pc, const RegContext &ctx) {
173 #else
174 exc_handler.TryBlock.Start, exc_handler.TryBlock.End,
175 [&cflow_info, ®_context, &first](const uint8_t *pc, const RegContext &ctx) {
176 #endif
177 if (cflow_info.ExcSrcMap().IsExceptionSource(pc)) {
178 #ifndef NDEBUG
179 LOG_VERIFIER_DEBUG_REGISTERS("+", ctx.DumpRegs(image_of));
180 #endif
181 if (first) {
182 first = false;
183 *reg_context = ctx;
184 } else {
185 *reg_context &= ctx;
186 }
187 }
188 return true;
189 });
190 #ifndef NDEBUG
191 LOG_VERIFIER_DEBUG_REGISTERS("=", reg_context->DumpRegs(image_of));
192 #endif
193
194 reg_context->RemoveInconsistentRegs();
195
196 #ifndef NDEBUG
197 if (reg_context->HasInconsistentRegs()) {
198 LOG_VERIFIER_COMMON_CONTEXT_INCONSISTENT_REGISTER_HEADER();
199 for (int reg_num : reg_context->InconsistentRegsNums()) {
200 LOG(DEBUG, VERIFIER) << AbsIntInstructionHandler::RegisterName(reg_num);
201 }
202 }
203 #endif
204 }
205
206 VerificationStatus VerifyExcHandler(CflowExcHandlerInfo const &exc_handler, VerificationContext &verifCtx,
207 RegContext ®_context)
208 {
209 auto &exec_ctx = verifCtx.ExecCtx();
210 auto exception = exc_handler.CachedException;
211
212 #ifndef NDEBUG
213 auto &cflow_info = verifCtx.CflowInfo();
214 const void *codeStart = cflow_info.InstMap().AddrStart<const void *>();
215 auto takeAddress = [&](const void *ptr) {
216 return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(codeStart));
217 };
218
219 LOG(DEBUG, VERIFIER) << "Exception handler at " << std::hex << "0x" << takeAddress(exc_handler.Start)
220 << (exception.HasRef() ? PandaString {", for exception '"} + exception->GetName() + "' "
221 : PandaString {""})
222 << ", try block scope: [ "
223 << "0x" << takeAddress(exc_handler.TryBlock.Start) << ", "
224 << "0x" << takeAddress(exc_handler.TryBlock.End) << " ]";
225 #endif
226
227 Type exception_type;
228 if (exception.HasRef()) {
229 exception_type = verifCtx.Types().TypeOf(exception.Get());
230 } else {
231 auto lang = verifCtx.GetJob().JobCachedMethod().GetSourceLang();
232 exception_type = verifCtx.Types().Throwable(lang);
233 }
234
235 if (exception_type.IsValid()) {
236 const int ACC = -1;
237 reg_context[ACC] = AbstractTypedValue {exception_type, verifCtx.NewVar()};
238 }
239
240 exec_ctx.CurrentRegContext() = reg_context;
241 exec_ctx.AddEntryPoint(exc_handler.Start, EntryPointType::EXCEPTION_HANDLER);
242 exec_ctx.StoreCurrentRegContextForAddr(exc_handler.Start);
243
244 return VerifyEntryPoints(verifCtx, exec_ctx);
245 }
246
247 } // namespace
248
VerifyMethod(VerificationContext & verifCtx)249 VerificationStatus VerifyMethod(VerificationContext &verifCtx)
250 {
251 VerificationStatus worstSoFar = VerificationStatus::OK;
252 auto &cflow_info = verifCtx.CflowInfo();
253 auto &exec_ctx = verifCtx.ExecCtx();
254
255 worstSoFar = std::max(worstSoFar, VerifyEntryPoints(verifCtx, exec_ctx));
256 if (worstSoFar == VerificationStatus::ERROR) {
257 return worstSoFar;
258 }
259
260 std::vector<CflowExcHandlerInfo const *> sorted_handlers;
261 for (const auto &exc_handler : cflow_info.ExcHandlers()) {
262 sorted_handlers.push_back(&exc_handler);
263 }
264 std::sort(sorted_handlers.begin(), sorted_handlers.end(), [](auto h1, auto h2) { return h1->Start < h2->Start; });
265
266 PandaUnorderedMap<std::pair<const uint8_t *, const uint8_t *>, RegContext> scope_reg_context;
267
268 for (const auto exc_handler : sorted_handlers) {
269 // calculate contexts for exception handlers scopes
270 auto scope = std::make_pair(exc_handler->TryBlock.Start, exc_handler->TryBlock.End);
271 if (scope_reg_context.count(scope) == 0) {
272 RegContext reg_context;
273 ComputeRegContext(*exc_handler, verifCtx, ®_context);
274 scope_reg_context[scope] = reg_context;
275 }
276
277 RegContext ®_context = scope_reg_context[scope];
278
279 worstSoFar = std::max(worstSoFar, VerifyExcHandler(*exc_handler, verifCtx, reg_context));
280 if (worstSoFar == VerificationStatus::ERROR) {
281 return worstSoFar;
282 }
283 }
284
285 // TODO(vdyadov): account for dead code
286 // add marking at Sync() and calc unmarked blocks at the end
287 const uint8_t *dummy_entry_point;
288 EntryPointType dummy_entry_type;
289
290 if (exec_ctx.GetEntryPointForChecking(&dummy_entry_point, &dummy_entry_type) ==
291 ExecContext::Status::NO_ENTRY_POINTS_WITH_CONTEXT) {
292 // TODO(vdyadov): log remaining entry points as unreachable
293 worstSoFar = std::max(worstSoFar, VerificationStatus::WARNING);
294 }
295
296 return worstSoFar;
297 }
298
299 } // namespace panda::verifier
300