Files
ark_runtime_core/verification/cflow/cflow_check.cpp
T
wanyanglan 14c9021696 add ark runtime_core
Signed-off-by: wanyanglan <wanyanglan1@huawei.com>
Change-Id: I2564094cef9c6c41263e37faf9ffbbec14223dc7
2021-09-05 20:53:43 +08:00

266 lines
11 KiB
C++

/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cflow_check.h"
#include "cflow_common.h"
#include "instructions_map.h"
#include "runtime/include/method-inl.h"
#include "utils/logger.h"
#include "verification/util/str.h"
#include "verifier_messages.h"
#include <optional>
#include <iomanip>
namespace panda::verifier {
template <class F>
static bool CheckCode(const CflowMethodInfo &cflow_info, const uint8_t *method_pc_end_ptr,
const F &report_incorrect_jump)
{
// check method code jumps (body + exc handlers, i.e all code)
{
const uint8_t *pc_jump_ptr = nullptr;
const uint8_t *pc_target_ptr = nullptr;
if (cflow_info.JmpsMap().GetFirstConflictingJump<const uint8_t *>(cflow_info.InstMap(), &pc_jump_ptr,
&pc_target_ptr)) {
report_incorrect_jump(pc_jump_ptr, pc_target_ptr,
"Invalid jump in the method body into middle of instruction.");
return false;
}
}
// check method body last instruction, if body spans till the method end
{
const auto &last_body_block = cflow_info.BodyInfo().back();
if (last_body_block.End == method_pc_end_ptr && last_body_block.LastInstType != InstructionType::RETURN &&
last_body_block.LastInstType != InstructionType::THROW &&
last_body_block.LastInstType != InstructionType::JUMP) {
LOG(DEBUG, VERIFIER) << "Invalid last instruction in method, execution beyond the method code boundary.";
return false;
}
}
return true;
}
static bool CheckFallthroughFromBlock(CflowCheckFlags options, const CflowMethodInfo &cflow_info,
const CacheOfRuntimeThings::CachedMethod &method)
{
if (!options[CflowCheckOptions::ALLOW_JMP_BODY_INTO_HANDLER] &&
!options[CflowCheckOptions::ALLOW_JMP_BODY_TO_HANDLER]) {
// fall through into exception handlers from body is disallowed
// iterate all blocks and check last instruction
for (const auto &block_info : cflow_info.BodyInfo()) {
if (block_info.LastInstType != InstructionType::RETURN &&
block_info.LastInstType != InstructionType::THROW && block_info.LastInstType != InstructionType::JUMP) {
LOG_VERIFIER_CFLOW_BODY_FALL_INTO_EXC_HANDLER(CacheOfRuntimeThings::GetName(method),
(OffsetAsHexStr(method.bytecode, block_info.End)));
return false;
}
}
}
return true;
}
template <class F>
static bool CheckJmpIntoExcHandler(CflowCheckFlags options, const CflowMethodInfo &cflow_info,
const F &report_incorrect_jump)
{
// check body jumps in/into exception handlers
if (!options[CflowCheckOptions::ALLOW_JMP_BODY_INTO_HANDLER]) {
InstructionsMap inst_map {cflow_info.InstMap()};
if (!options[CflowCheckOptions::ALLOW_JMP_BODY_TO_HANDLER]) {
// jumps from code to any place in handlers are prohibited
for (const auto &handler : cflow_info.ExcHandlers()) {
inst_map.MarkCodeBlock(handler.Info.Start, handler.Info.End);
}
} else {
// allow jumps to start of any handler
for (const auto &handler : cflow_info.ExcHandlers()) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
inst_map.MarkCodeBlock(handler.Info.Start + 1, handler.Info.End);
}
}
for (const auto &block_info : cflow_info.BodyInfo()) {
if (block_info.JmpsMap.IsConflictingWith(inst_map)) {
const uint8_t *jmp_pc = nullptr;
const uint8_t *tgt_pc = nullptr;
if (!block_info.JmpsMap.GetFirstConflictingJump(inst_map, &jmp_pc, &tgt_pc)) {
LOG_VERIFIER_CFLOW_INTERNAL_ERROR();
return false;
}
report_incorrect_jump(jmp_pc, tgt_pc, "Prohibited jump from method body to/into exception handler.");
return false;
}
}
}
return true;
}
static bool CheckFallthroughFromExcHandler(CflowCheckFlags options, const CflowMethodInfo &cflow_info,
const CacheOfRuntimeThings::CachedMethod &method)
{
// Temporarily mark all method mody, and check first mark after handler end
// check exception handlers fallthrough to exception handlers
if (!options[CflowCheckOptions::ALLOW_JMP_HANDLER_INTO_HANDLER] &&
!options[CflowCheckOptions::ALLOW_JMP_HANDLER_TO_HANDLER]) {
// falling through is prohibited, so last instruction in each handler should be
// linear cflow terminating
for (const auto &handler_info : cflow_info.ExcHandlers()) {
if (handler_info.Info.LastInstType != InstructionType::RETURN &&
handler_info.Info.LastInstType != InstructionType::THROW &&
handler_info.Info.LastInstType != InstructionType::JUMP) {
LOG_VERIFIER_CFLOW_INVALID_LAST_INST_OF_EXC_HANDLER_FALL_INTO_OTHER_EXC_HANDLER(
CacheOfRuntimeThings::GetName(method), (OffsetAsHexStr(method.bytecode, handler_info.Info.Start)),
(OffsetAsHexStr(method.bytecode, handler_info.Info.End)));
return false;
}
}
}
return true;
}
template <class F>
static bool CheckJmpOutExcHandler(CflowCheckFlags options, const CflowMethodInfo &cflow_info,
const F &report_incorrect_jump)
{
// check jumps out from handlers
InstructionsMap inst_map {cflow_info.InstMap()};
if (!options[CflowCheckOptions::ALLOW_JMP_HANDLER_INTO_BODY]) {
// prohibit jumps from handler to body
for (const auto &block_info : cflow_info.BodyInfo()) {
inst_map.MarkCodeBlock(block_info.Start, block_info.End);
}
}
if (!options[CflowCheckOptions::ALLOW_JMP_HANDLER_INTO_HANDLER]) {
// mark all handlers
// since we already checked jumps for correctness wrt instructions,
// we may just clear block, w/o bothering with exact instructions map
for (const auto &each_handler_info : cflow_info.ExcHandlers()) {
inst_map.MarkCodeBlock(each_handler_info.Info.Start, each_handler_info.Info.End);
}
if (options[CflowCheckOptions::ALLOW_JMP_HANDLER_TO_HANDLER]) {
// jumps from handler to start of other handlers are allowed, so
// prepare mask accordingly
// [....handler1...]......
// .....[....handler2....]
// allow or not jumps to the start of handler2 ?
// currently such jumps are allowed, i.e. priority of allowance is higher
// remove marks at starts
for (const auto &h_info : cflow_info.ExcHandlers()) {
inst_map.ClearCodeBlock(h_info.Info.Start, 1);
}
}
}
const CflowExcHandlerInfo *prev_handler_info = nullptr;
for (const auto &handler_info : cflow_info.ExcHandlers()) {
if (!options[CflowCheckOptions::ALLOW_JMP_HANDLER_INTO_HANDLER]) {
if (prev_handler_info != nullptr) {
inst_map.MarkCodeBlock(prev_handler_info->Info.Start, prev_handler_info->Info.End);
if (options[CflowCheckOptions::ALLOW_JMP_HANDLER_TO_HANDLER]) {
inst_map.ClearCodeBlock(prev_handler_info->Info.Start, 1);
}
}
inst_map.ClearCodeBlock(handler_info.Info.Start, handler_info.Info.End);
prev_handler_info = &handler_info;
}
// finally check jumps in the hanlder with built inst_map
if (handler_info.Info.JmpsMap.IsConflictingWith(inst_map)) {
const uint8_t *jmp_pc = nullptr;
const uint8_t *tgt_pc = nullptr;
if (!handler_info.Info.JmpsMap.GetFirstConflictingJump(inst_map, &jmp_pc, &tgt_pc)) {
LOG_VERIFIER_CFLOW_INTERNAL_ERROR();
return {};
}
report_incorrect_jump(jmp_pc, tgt_pc, "Prohibited jump out from exception handler detected.");
return {};
}
}
return true;
}
template <class F>
static bool CheckExcHandlers(CflowCheckFlags options, const CacheOfRuntimeThings::CachedMethod &method,
const CflowMethodInfo &cflow_info, const F &report_incorrect_jump)
{
if (!CheckFallthroughFromBlock(options, cflow_info, method)) {
return false;
}
// no handlers - no problems :)
if (cflow_info.ExcHandlers().empty()) {
return true;
}
if (!CheckJmpIntoExcHandler(options, cflow_info, report_incorrect_jump)) {
return false;
}
if (!CheckFallthroughFromExcHandler(options, cflow_info, method)) {
return false;
}
return CheckJmpOutExcHandler(options, cflow_info, report_incorrect_jump);
}
PandaUniquePtr<CflowMethodInfo> CheckCflow(CflowCheckFlags options, const CacheOfRuntimeThings::CachedMethod &method)
{
bool sizeless_handlers_present = false;
auto cflow_info = GetCflowMethodInfo(method, &sizeless_handlers_present);
if (!cflow_info) {
return {};
}
const uint8_t *method_pc_start_ptr = method.bytecode;
const uint8_t *method_pc_end_ptr =
&method_pc_start_ptr[method.bytecode_size - 1]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
auto report_incorrect_jump = [&method, &method_pc_start_ptr](const uint8_t *jump_pc, const uint8_t *jump_target,
const char *msg) {
LOG_VERIFIER_CFLOW_INVALID_JUMP_TARGET(CacheOfRuntimeThings::GetName(method),
(OffsetAsHexStr(method_pc_start_ptr, jump_target)),
(OffsetAsHexStr(method_pc_start_ptr, jump_pc)), msg);
};
if (!CheckCode(*cflow_info, method_pc_end_ptr, report_incorrect_jump)) {
return {};
}
// stop checks if there are any exception handlers w/o size
if (sizeless_handlers_present) {
return cflow_info;
}
if (!CheckExcHandlers(options, method, *cflow_info, report_incorrect_jump)) {
return {};
}
return cflow_info;
}
} // namespace panda::verifier