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 "cflow_check.h"
17 #include "cflow_common.h"
18 #include "cflow_iterate_inl.h"
19 #include "runtime/include/method-inl.h"
20 #include "utils/logger.h"
21 #include "verification/util/str.h"
22 #include "verifier_messages.h"
23
24 #include <iomanip>
25 #include <optional>
26
27 namespace panda::verifier {
28
29 template <class F>
CheckCode(const CflowMethodInfo & cflow_info,const uint8_t * method_pc_end_ptr,const F & report_incorrect_jump)30 static bool CheckCode(const CflowMethodInfo &cflow_info, const uint8_t *method_pc_end_ptr,
31 const F &report_incorrect_jump)
32 {
33 // check method code jumps (body + exc handlers, i.e all code)
34 {
35 const uint8_t *pc_jump_ptr = nullptr;
36 const uint8_t *pc_target_ptr = nullptr;
37 if (cflow_info.JmpsMap().GetFirstConflictingJump<const uint8_t *>(cflow_info.InstMap(), &pc_jump_ptr,
38 &pc_target_ptr)) {
39 report_incorrect_jump(pc_jump_ptr, pc_target_ptr,
40 "Invalid jump in the method body into middle of instruction.");
41 return false;
42 }
43 }
44
45 // check method body last instruction, if body spans till the method end
46 {
47 const auto &last_body_block = cflow_info.BodyBlocks().back();
48 if (last_body_block.End == method_pc_end_ptr && last_body_block.LastInstType != InstructionType::RETURN &&
49 last_body_block.LastInstType != InstructionType::THROW &&
50 last_body_block.LastInstType != InstructionType::JUMP) {
51 LOG(ERROR, VERIFIER) << "Invalid last instruction in method, execution beyond the method code boundary.";
52 return false;
53 }
54 }
55
56 return true;
57 }
58
IsExceptionHanlderStart(const CflowMethodInfo & cflow_info,const uint8_t * inst)59 static bool IsExceptionHanlderStart(const CflowMethodInfo &cflow_info, const uint8_t *inst)
60 {
61 for (const CflowExcHandlerInfo &handler : cflow_info.ExcHandlers()) {
62 if (handler.Start == inst) {
63 return true;
64 }
65 }
66 return false;
67 }
68
69 /**
70 * Checking two statements:
71 * - jumps into body of exception handler from code is prohibited
72 * - fallthrough on beginning of exception handler is prohibited
73 * The statements are not declared by Java Spec, it is a demand of Panda compiler.
74 */
CheckInvalidCatchBlockEnter(const LibCache::CachedMethod & method,const CflowMethodInfo & cflow_info,const MethodOptions & options)75 static bool CheckInvalidCatchBlockEnter(const LibCache::CachedMethod &method, const CflowMethodInfo &cflow_info,
76 const MethodOptions &options)
77 {
78 const uint8_t *method_start = method.bytecode;
79 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
80 const uint8_t *method_end = &method_start[method.bytecode_size - 1];
81 bool status_ok = true;
82
83 auto cflow_handler = [&cflow_info, &method, &status_ok, &options](auto type, const uint8_t *pc, size_t sz,
84 [[maybe_unused]] bool exception_source,
85 auto target) -> std::optional<CflowStatus> {
86 if (type != InstructionType::JUMP && type != InstructionType::THROW && type != InstructionType::RETURN) {
87 const uint8_t *next_inst = &pc[sz]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
88 if (IsExceptionHanlderStart(cflow_info, next_inst)) {
89 LOG_VERIFIER_CFLOW_BODY_FALL_INTO_EXC_HANDLER(LibCache::GetName(method),
90 (OffsetAsHexStr(method.bytecode, pc)));
91 status_ok = false;
92 return CflowStatus::ERROR;
93 }
94 }
95
96 if (type != InstructionType::JUMP && type != InstructionType::COND_JUMP) {
97 return std::nullopt;
98 }
99
100 for (const CflowExcHandlerInfo &handler : cflow_info.ExcHandlers()) {
101 bool isInvalidJump = (target == handler.Start && handler.Size == 0) ||
102 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
103 (target >= handler.Start && target < handler.Start + handler.Size);
104
105 if (isInvalidJump && (options.Msg(VerifierMessage::CflowInvalidJumpIntoExcHandler).IsError())) {
106 status_ok = false;
107 }
108
109 if (isInvalidJump) {
110 LOG_VERIFIER_CFLOW_INVALID_JUMP_INTO_EXC_HANDLER(LibCache::GetName(method),
111 (OffsetAsHexStr(method.bytecode, pc)));
112 return CflowStatus::ERROR;
113 }
114 }
115 return std::nullopt;
116 };
117
118 IterateOverInstructions(method_start, method_start, method_end, cflow_handler);
119 return status_ok;
120 }
121
CheckCflow(const LibCache::CachedMethod & method,const MethodOptions & options,LibCache & cache)122 PandaUniquePtr<CflowMethodInfo> CheckCflow(const LibCache::CachedMethod &method, const MethodOptions &options,
123 LibCache &cache)
124 {
125 auto cflow_info = GetCflowMethodInfo(method, cache);
126 if (!cflow_info) {
127 return {};
128 }
129
130 const uint8_t *method_pc_start_ptr = method.bytecode;
131 const uint8_t *method_pc_end_ptr =
132 &method_pc_start_ptr[method.bytecode_size - 1]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
133
134 auto report_incorrect_jump = [&method, &method_pc_start_ptr](const uint8_t *jump_pc, const uint8_t *jump_target,
135 const char *msg) {
136 LOG_VERIFIER_CFLOW_INVALID_JUMP_TARGET(LibCache::GetName(method),
137 (OffsetAsHexStr(method_pc_start_ptr, jump_target)),
138 (OffsetAsHexStr(method_pc_start_ptr, jump_pc)), msg);
139 };
140
141 if (!CheckCode(*cflow_info, method_pc_end_ptr, report_incorrect_jump)) {
142 return {};
143 }
144
145 if (!CheckInvalidCatchBlockEnter(method, *cflow_info, options)) {
146 return {};
147 }
148
149 return cflow_info;
150 }
151
152 } // namespace panda::verifier
153