• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &reg_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, &reg_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, &reg_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 &reg_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, &reg_context);
274             scope_reg_context[scope] = reg_context;
275         }
276 
277         RegContext &reg_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