• 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 "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