1 /**
2 * Copyright (c) 2021-2025 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 "bytecode_instruction-inl.h"
17 #include "file_items.h"
18 #include "macros.h"
19 #include "include/runtime.h"
20 #include "runtime/include/thread_scopes.h"
21 #include "runtime/include/method-inl.h"
22
23 #include "utils/logger.h"
24
25 #include "util/str.h"
26
27 #include "cflow_info.h"
28
29 #include <iomanip>
30
31 #include "cflow_iterate_inl.h"
32
33 #include "verification/jobs/job.h"
34 #include "verification/cflow/cflow_common.h"
35 #include "verification/public_internal.h"
36 #include "verification/verification_status.h"
37
38 #include "verifier_messages.h"
39
40 namespace ark::verifier {
41
FillCodeMaps(Method const * method)42 VerificationStatus CflowMethodInfo::FillCodeMaps(Method const *method)
43 {
44 auto status = IterateOverInstructions(
45 addrStart_, addrStart_, addrEnd_,
46 [this, method]([[maybe_unused]] auto typ, uint8_t const *pc, size_t sz, bool exceptionSource,
47 [[maybe_unused]] auto tgt) -> std::optional<VerificationStatus> {
48 SetFlag(pc, INSTRUCTION);
49 if (exceptionSource) {
50 SetFlag(pc, EXCEPTION_SOURCE);
51 }
52 if (tgt != nullptr) { // a jump
53 if (!IsAddrValid(tgt)) {
54 LOG_VERIFIER_CFLOW_INVALID_JUMP_OUTSIDE_METHOD_BODY(
55 method->GetFullName(), OffsetAsHexStr(addrStart_, tgt), OffsetAsHexStr(addrStart_, pc));
56 return VerificationStatus::ERROR;
57 }
58 SetFlag(tgt, JUMP_TARGET);
59 }
60 uint8_t const *nextInstPc = &pc[sz]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
61 if (nextInstPc == addrEnd_) {
62 return VerificationStatus::OK;
63 }
64 if (nextInstPc > addrEnd_) {
65 LOG_VERIFIER_CFLOW_INVALID_INSTRUCTION(OffsetAsHexStr(addrStart_, pc));
66 return VerificationStatus::ERROR;
67 }
68 return std::nullopt;
69 });
70 return status;
71 }
72
ProcessCatchBlocks(Method const * method)73 VerificationStatus CflowMethodInfo::ProcessCatchBlocks(Method const *method)
74 {
75 using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
76
77 LOG(DEBUG, VERIFIER) << "Tracing exception handlers.";
78
79 auto status = VerificationStatus::OK;
80 method->EnumerateCatchBlocks([&]([[maybe_unused]] uint8_t const *tryStartPc,
81 [[maybe_unused]] uint8_t const *tryEndPc, CatchBlock const &catchBlock) {
82 auto catchBlockStart = reinterpret_cast<uint8_t const *>(reinterpret_cast<uintptr_t>(addrStart_) +
83 static_cast<uintptr_t>(catchBlock.GetHandlerPc()));
84 auto catchBlockEnd =
85 &catchBlockStart[catchBlock.GetCodeSize()]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
86 if (catchBlockStart > catchBlockEnd || catchBlockStart >= addrEnd_ || catchBlockEnd < addrStart_) {
87 LOG_VERIFIER_CFLOW_BAD_CATCH_BLOCK_BOUNDARIES(OffsetAsHexStr(addrStart_, catchBlockStart),
88 OffsetAsHexStr(addrStart_, catchBlockEnd));
89 status = VerificationStatus::ERROR;
90 return false;
91 }
92 if (catchBlockEnd == catchBlockStart) {
93 // special case, no need to iterate over instructions.
94 return true;
95 }
96
97 handlerStartAddresses_.push_back(catchBlockStart);
98
99 auto result = IterateOverInstructions(
100 catchBlockStart, addrStart_, addrEnd_,
101 [this, catchBlockEnd]([[maybe_unused]] auto typ, uint8_t const *pc, size_t sz,
102 [[maybe_unused]] bool exceptionSource,
103 [[maybe_unused]] auto tgt) -> std::optional<VerificationStatus> {
104 SetFlag(pc, EXCEPTION_HANDLER);
105 uint8_t const *nextInstPc = &pc[sz]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
106 if (nextInstPc == catchBlockEnd) {
107 return VerificationStatus::OK;
108 }
109 if (nextInstPc > catchBlockEnd) {
110 LOG_VERIFIER_CFLOW_INVALID_INSTRUCTION(OffsetAsHexStr(addrStart_, pc));
111 return VerificationStatus::ERROR;
112 }
113 return std::nullopt;
114 });
115 status = std::max(status, result);
116 return true;
117 });
118
119 // Serves as a barrier
120 auto endCode = reinterpret_cast<uint8_t const *>(reinterpret_cast<uintptr_t>(method->GetInstructions()) +
121 method->GetCodeSize());
122 handlerStartAddresses_.push_back(endCode);
123 std::sort(handlerStartAddresses_.begin(), handlerStartAddresses_.end());
124
125 return status;
126 }
127
GetCflowMethodInfo(Method const * method)128 PandaUniquePtr<CflowMethodInfo> GetCflowMethodInfo(Method const *method)
129 {
130 const uint8_t *methodPcStartPtr = method->GetInstructions();
131 size_t codeSize = method->GetCodeSize();
132
133 auto cflowInfo = MakePandaUnique<CflowMethodInfo>(methodPcStartPtr, codeSize);
134
135 LOG(DEBUG, VERIFIER) << "Build control flow info for method " << method->GetFullName();
136
137 // 1. fill instructions map
138 LOG(DEBUG, VERIFIER) << "Build instructions map.";
139 if (cflowInfo == nullptr) {
140 LOG(ERROR, VERIFIER) << "cflowInfo is nullptr ";
141 return {};
142 }
143 if (cflowInfo->FillCodeMaps(method) == VerificationStatus::ERROR) {
144 return {};
145 }
146
147 // 2. Mark exception handlers.
148 if (cflowInfo->ProcessCatchBlocks(method) == VerificationStatus::ERROR) {
149 return {};
150 }
151
152 return cflowInfo;
153 }
154
155 } // namespace ark::verifier
156