• 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 "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 &regCtx = 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, &regContext);
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, &regContext));
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