!2520 Refactor verifier based on ruby

Merge pull request !2520 from XHQ/refact_ruby
This commit is contained in:
openharmony_ci 2024-10-12 17:29:42 +00:00 committed by Gitee
commit a7d2952aea
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
11 changed files with 393 additions and 221 deletions

View File

@ -17,7 +17,7 @@
import os
import yaml
current_version = "13.0.0.0"
CURRENT_VERSION = "13.0.0.0"
script_dir = os.path.dirname(os.path.realpath(__file__))
@ -38,4 +38,4 @@ def check_version(yaml_file, cur_version):
except Exception as e:
print(f"Error reading file: {e}")
check_version(isa_file_path, current_version)
check_version(isa_file_path, CURRENT_VERSION)

View File

@ -165,6 +165,16 @@ properties:
description: The instruction occupies ic slots of 16 bit width.
- tag: eight_sixteen_bit_ic
description: The instruction occupies ic slots of both 8 and 16 bit width.
- tag: conditional_throw
description: The throw instruction can throw an excepton only if certain conditions are met.
- tag: range_0
description: Indicates that the instruction takes some consective registers as inputs.
The start register of these consective inputs is the last register of the instruction.
The number of consective inputs is the last immediate of the instruction.
- tag: range_1
description: Indicates that the instruction takes some consective registers as inputs.
The start register of these consective inputs is the last register of the instruction.
The number of consective inputs is the last immediate of the instruction plus 1.
exceptions:
- tag: x_none
@ -475,11 +485,13 @@ groups:
acc: out:top
opcode_idx: [0xb3]
format: [op_imm_8_v1_8_v2_8]
properties: [range_1]
- sig: wide.createobjectwithexcludedkeys imm:u16, v1:in:top, v2:in:top
acc: out:top
opcode_idx: [0x00]
format: [pref_op_imm_16_v1_8_v2_8]
prefix: wide
properties: [range_1]
- sig: createarraywithbuffer imm:u16, literalarray_id
acc: out:top
opcode_idx: [0x06, 0x81]
@ -514,12 +526,13 @@ groups:
acc: out:top
opcode_idx: [0x08, 0x83]
format: [op_imm1_8_imm2_8_v_8, op_imm1_16_imm2_8_v_8]
properties: [ic_slot, two_slot, eight_sixteen_bit_ic]
properties: [ic_slot, two_slot, eight_sixteen_bit_ic, range_0]
- sig: wide.newobjrange imm:u16, v:in:top
acc: out:top
opcode_idx: [0x01]
format: [pref_op_imm_16_v_8]
prefix: wide
properties: [range_0]
- sig: newlexenv imm:u8
acc: out:top
opcode_idx: [0x09]
@ -971,22 +984,25 @@ groups:
opcode_idx: [0x05]
format: [pref_op_v_8]
prefix: throw
properties: [conditional_throw]
- sig: throw.undefinedifhole v1:in:top, v2:in:top
acc: none
opcode_idx: [0x06]
format: [pref_op_v1_8_v2_8]
prefix: throw
properties: [conditional_throw]
- sig: throw.ifsupernotcorrectcall imm:u16
acc: in:top
opcode_idx: [0x07, 0x08]
format: [pref_op_imm_8, pref_op_imm_16]
prefix: throw
properties: [conditional_throw]
- sig: throw.undefinedifholewithname string_id
acc: in:top
opcode_idx: [0x09]
format: [pref_op_id_16]
prefix: throw
properties: [string_id]
properties: [string_id, conditional_throw]
- title: call instructions
description: call
@ -1047,17 +1063,19 @@ groups:
acc: inout:top
opcode_idx: [0x73]
format: [op_imm1_8_imm2_8_v_8]
properties: [jit_ic_slot, two_slot, eight_bit_ic]
properties: [jit_ic_slot, two_slot, eight_bit_ic, range_0]
- sig: wide.callrange imm:u16, v:in:top
acc: inout:top
opcode_idx: [0x04]
format: [pref_op_imm_16_v_8]
prefix: wide
properties: [range_0]
- sig: deprecated.callrange imm:u16, v:in:top
acc: out:top
opcode_idx: [0x0f]
format: [pref_op_imm_16_v_8]
prefix: deprecated
properties: [range_0]
- sig: supercallspread imm:u8, v:in:top
acc: inout:top
opcode_idx: [0xb9]
@ -1097,37 +1115,41 @@ groups:
acc: inout:top
opcode_idx: [0x31]
format: [op_imm1_8_imm2_8_v_8]
properties: [jit_ic_slot, two_slot, eight_bit_ic]
properties: [jit_ic_slot, two_slot, eight_bit_ic, range_0]
- sig: wide.callthisrange imm:u16, v:in:top
acc: inout:top
opcode_idx: [0x05]
format: [pref_op_imm_16_v_8]
prefix: wide
properties: [range_1]
- sig: deprecated.callthisrange imm:u16, v:in:top
acc: out:top
opcode_idx: [0x11]
format: [pref_op_imm_16_v_8]
prefix: deprecated
properties: [range_1]
- sig: supercallthisrange imm1:u8, imm2:u8, v:in:top
acc: out:top
opcode_idx: [0x32]
format: [op_imm1_8_imm2_8_v_8]
properties: [jit_ic_slot, two_slot, eight_bit_ic]
properties: [jit_ic_slot, two_slot, eight_bit_ic, range_0]
- sig: wide.supercallthisrange imm:u16, v:in:top
acc: out:top
opcode_idx: [0x06]
format: [pref_op_imm_16_v_8]
prefix: wide
properties: [range_0]
- sig: supercallarrowrange imm1:u8, imm2:u8, v:in:top
acc: inout:top
opcode_idx: [0xbb]
format: [op_imm1_8_imm2_8_v_8]
properties: [jit_ic_slot, two_slot, eight_bit_ic]
properties: [jit_ic_slot, two_slot, eight_bit_ic, range_0]
- sig: wide.supercallarrowrange imm:u16, v:in:top
acc: inout:top
opcode_idx: [0x07]
format: [pref_op_imm_16_v_8]
prefix: wide
properties: [range_0]
- title: definition instuctions
description: instructions which define object

View File

@ -264,6 +264,26 @@ class Instruction < SimpleDelegator
dig(:namespace) || 'core'
end
def is_range_0?
properties.include?('range_0')
end
def is_range_1?
properties.include?('range_1')
end
def is_range_instruction?
is_range_0? || is_range_1?
end
def is_return_instruction?
properties.include?('return')
end
def is_unconditional_throw_instruction?
dig(:prefix) == 'throw' && !properties.include?('conditional_throw')
end
include FreezeMixin
freeze_defined_methods
end

View File

@ -260,6 +260,8 @@ public:
// Read imm as actually signed / unsigned and cast it to int64 before return
auto GetImmData(size_t idx = 0) const;
auto GetImmCount() const;
/**
* Primary and Secondary Opcodes are used in interpreter/verifier instruction dispatch
* while full Opcode is typically used for various instruction property query.
@ -405,7 +407,25 @@ public:
return Size(GetFormat(opcode));
}
static std::optional<uint64_t> SafeAdd(uint64_t a, uint64_t b)
{
if (a > std::numeric_limits<uint64_t>::max() - b) {
return std::nullopt;
}
return a + b;
}
size_t GetLiteralIndex() const;
bool IsJumpInstruction() const;
bool IsReturnOrThrowInstruction() const;
bool IsRangeInstruction() const;
std::optional<uint64_t> GetRangeInsLastRegIdx() const;
std::optional<uint64_t> GetLastVReg() const;
};
template <const BytecodeInstMode Mode>

View File

@ -269,6 +269,34 @@ inline auto BytecodeInst<Mode>::GetImm() const { // NOLINTNEXTLINE(readability-
UNREACHABLE();
}
template <const BytecodeInstMode Mode>
inline auto BytecodeInst<Mode>::GetImmCount() const {
Format format = GetFormat();
auto idx = 0;
ASSERT_PRINT(HasImm(format, idx), "Instruction has no imm operand");
if (!HasImm(format, idx)) {
return static_cast<size_t>(0);
}
switch (format) {
% insns_uniq_sort_fmts.each do |i| # Panda::formats.each do |fmt|
% fmt = i.format
% n = i.operands.count(&:imm?)
% next if n == 0
case Format::<%= fmt.pretty.upcase %>: {
return static_cast<size_t>(<%= n %>);
}
% end
default: {
break;
}
}
UNREACHABLE();
}
template<const BytecodeInstMode Mode>
inline auto BytecodeInst<Mode>::GetImm64(size_t idx /* = 0 */) const {
Format format = GetFormat();
@ -567,4 +595,108 @@ template<const BytecodeInstMode Mode> inline size_t BytecodeInst<Mode>::GetLiter
return -1;
break;
}
}
template<const BytecodeInstMode Mode>
inline bool BytecodeInst<Mode>::IsJumpInstruction() const {
switch(GetOpcode()) {
% Panda::instructions.each do |i|
% if i.jump?
case BytecodeInst<Mode>::Opcode::<%= i.opcode.upcase %>:
return true;
% end
% end
default:
return false;
}
UNREACHABLE();
}
template<const BytecodeInstMode Mode>
inline bool BytecodeInst<Mode>::IsRangeInstruction() const {
switch (GetOpcode()) {
% Panda::instructions.each do |i|
% if i.is_range_instruction?
case BytecodeInst<Mode>::Opcode::<%= i.opcode.upcase %>:
return true;
% end
% end
default:
return false;
}
UNREACHABLE();
}
template <const BytecodeInstMode Mode>
inline std::optional<uint64_t> BytecodeInst<Mode>::GetLastVReg() const {
Format format = GetFormat();
ASSERT_PRINT(HasVReg(format, 0), "Instruction doesn't have VReg operand with such index");
if (!HasVReg(format, 0)) {
return std::nullopt;
}
switch (format) {
% insns_uniq_sort_fmts.each do |i| # Panda::formats.each do |fmt|
% fmt = i.format
% n = i.operands.count(&:reg?)
% next if n == 0
%
case Format::<%= fmt.pretty.upcase %>: {
return GetVReg<BytecodeInstruction::Format::<%=fmt.pretty.upcase%>, <%=n-1%>>();
}
% end
default: {
break;
}
}
UNREACHABLE();
}
template <const BytecodeInstMode Mode>
inline std::optional<uint64_t> BytecodeInst<Mode>::GetRangeInsLastRegIdx() const {
// For the range instruction, where A stores the number of registers
// The actual register index needs to handle 2 cases: B~B+A-1 / B~B+A
// range_0 is B~B+A-1
// range_1 is B~B+A
size_t count = GetImmCount();
if (count == 0) {
return std::nullopt;
}
int64_t range_reg_num = GetImmData(count - 1);
switch (GetOpcode()) {
% Panda::instructions.each do |i|
% if i.is_range_0?
case BytecodeInst<Mode>::Opcode::<%= i.opcode.upcase %>:
if (range_reg_num > 0) {
return SafeAdd(GetLastVReg().value(), range_reg_num - 1);
}
return GetLastVReg();
% end
% if i.is_range_1?
case BytecodeInst<Mode>::Opcode::<%= i.opcode.upcase %>:
return SafeAdd(GetLastVReg().value(), range_reg_num);
% end
% end
default:
return std::nullopt;
}
UNREACHABLE();
}
template<const BytecodeInstMode Mode>
inline bool BytecodeInst<Mode>::IsReturnOrThrowInstruction() const {
switch (GetOpcode()) {
% Panda::instructions.each do |i|
% if i.is_return_instruction? || i.is_unconditional_throw_instruction?
case BytecodeInst<Mode>::Opcode::<%= i.opcode.upcase %>:
return true;
% end
% end
default:
return false;
}
UNREACHABLE();
}

View File

@ -20,6 +20,8 @@ host_unittest_action("LibPandaFileTest") {
sources = [
"bytecode_emitter_tests.cpp",
"bytecode_imm_fetch_tests.cpp",
"bytecode_jump_range_tests.cpp",
"bytecode_rerturn_throw_tests.cpp",
"data_protect_test.cpp",
"debug_info_extractor_test.cpp",
"file_format_version_test.cpp",

View File

@ -33,6 +33,8 @@ TEST(BytecodeInstruction, Signed)
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8_V8_V8_V8, 0>()), static_cast<int8_t>(0x17));
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8_V8_V8_V8, 0, true>()),
static_cast<int8_t>(0x17));
EXPECT_EQ(inst.GetImmData(0), static_cast<int64_t>(0x17));
EXPECT_EQ(inst.GetImmCount(), 1);
}
{
@ -42,6 +44,8 @@ TEST(BytecodeInstruction, Signed)
EXPECT_EQ(static_cast<uint8_t>(inst.GetOpcode()), 0x4d);
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8, 0>()), static_cast<int8_t>(-22));
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8, 0, true>()), static_cast<int8_t>(-22));
EXPECT_EQ(inst.GetImmData(0), static_cast<int64_t>(-22));
EXPECT_EQ(inst.GetImmCount(), 1);
}
{
@ -52,6 +56,8 @@ TEST(BytecodeInstruction, Signed)
EXPECT_EQ(inst.GetFormat(), BytecodeInstruction::Format::IMM32);
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM32, 0>()), static_cast<int32_t>(0x1e));
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM32, 0, true>()), static_cast<int32_t>(0x1e));
EXPECT_EQ(inst.GetImmData(0), static_cast<int64_t>(0x1e));
EXPECT_EQ(inst.GetImmCount(), 1);
}
{
@ -60,6 +66,8 @@ TEST(BytecodeInstruction, Signed)
BytecodeInstruction inst(bytecode);
EXPECT_EQ(static_cast<uint8_t>(inst.GetOpcode()), 0x63);
EXPECT_EQ((bit_cast<double>(inst.GetImm<BytecodeInstruction::Format::IMM64, 0, true>())), 3.14);
EXPECT_EQ(inst.GetImmData(0), static_cast<int64_t>(0x40091eb851eb851f));
EXPECT_EQ(inst.GetImmCount(), 1);
}
}
@ -74,6 +82,8 @@ TEST(BytecodeInstruction, UnsignedOneImm)
EXPECT_NE((inst.GetImm<BytecodeInstruction::Format::IMM8_V8_V8_V8, 0>()), static_cast<uint8_t>(0x8e));
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8_V8_V8_V8, 0, false>()),
static_cast<uint8_t>(0x8e));
EXPECT_EQ(inst.GetImmData(0), static_cast<int64_t>(0x8e));
EXPECT_EQ(inst.GetImmCount(), 1);
}
{
@ -84,6 +94,8 @@ TEST(BytecodeInstruction, UnsignedOneImm)
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8, 0>()), static_cast<int8_t>(0x0d));
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8, 0, false>()),
static_cast<uint8_t>(0x0d));
EXPECT_EQ(inst.GetImmData(0), static_cast<int64_t>(0x0d));
EXPECT_EQ(inst.GetImmCount(), 1);
}
{
@ -94,6 +106,8 @@ TEST(BytecodeInstruction, UnsignedOneImm)
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM16_ID16, 0>()), static_cast<int16_t>(0x80));
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM16_ID16, 0, false>()),
static_cast<uint16_t>(0x80));
EXPECT_EQ(inst.GetImmData(0), static_cast<int64_t>(0x80));
EXPECT_EQ(inst.GetImmCount(), 1);
}
}
@ -109,6 +123,9 @@ TEST(BytecodeInstruction, UnsignedTwoImm)
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM4_IMM4, 1>()), 2);
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM4_IMM4, 0, false>()), 0);
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM4_IMM4, 1, false>()), 2);
EXPECT_EQ(inst.GetImmData(0), static_cast<int64_t>(0));
EXPECT_EQ(inst.GetImmData(1), static_cast<int64_t>(2));
EXPECT_EQ(inst.GetImmCount(), 2);
}
{
@ -121,6 +138,9 @@ TEST(BytecodeInstruction, UnsignedTwoImm)
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8_ID16_IMM8, 1>()), static_cast<int8_t>(1));
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8_ID16_IMM8, 0, false>()), static_cast<int8_t>(2));
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8_ID16_IMM8, 1, false>()), static_cast<int8_t>(1));
EXPECT_EQ(inst.GetImmData(0), static_cast<int64_t>(2));
EXPECT_EQ(inst.GetImmData(1), static_cast<int64_t>(1));
EXPECT_EQ(inst.GetImmCount(), 2);
}
{
@ -135,6 +155,9 @@ TEST(BytecodeInstruction, UnsignedTwoImm)
static_cast<uint8_t>(2));
EXPECT_EQ((inst.GetImm<BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 1, false>()),
static_cast<uint16_t>(2));
EXPECT_EQ(inst.GetImmData(0), static_cast<int64_t>(2));
EXPECT_EQ(inst.GetImmData(1), static_cast<int64_t>(2));
EXPECT_EQ(inst.GetImmCount(), 2);
}
}
@ -209,4 +232,13 @@ TEST(BytecodeInstruction, GetLiteralIndex)
}
}
TEST(BytecodeInstruction, GetLastVReg)
{
{
// newobjrange 0xb, 0x3, v6
const uint8_t bytecode[] = {0x08, 0x0b, 0x03, 0x06};
BytecodeInstruction inst(bytecode);
EXPECT_EQ(inst.GetLastVReg().value(), 6);
}
}
} // namespace panda::test

View File

@ -0,0 +1,72 @@
/**
* Copyright (c) 2024 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 <cstddef>
#include <cstdint>
#include <gtest/gtest.h>
#include <sstream>
#include <type_traits>
#include "bytecode_instruction-inl.h"
namespace panda::test {
TEST(BytecodeInstruction, IsJumpInstruction)
{
{
// jmp -22
const uint8_t bytecode[] = {0x4d, 0xea};
BytecodeInstruction inst(bytecode);
EXPECT_EQ(inst.IsJumpInstruction(), true);
}
{
// a non-jump instruction
const uint8_t bytecode[] = {0x08, 0x0b, 0x03, 0x06};
BytecodeInstruction inst(bytecode);
EXPECT_EQ(inst.IsJumpInstruction(), false);
}
}
TEST(BytecodeInstruction, IsRangeInstruction)
{
{
// newobjrange 0xb, 0x3, v6
const uint8_t bytecode[] = {0x08, 0x0b, 0x03, 0x06};
BytecodeInstruction inst(bytecode);
EXPECT_EQ(inst.IsRangeInstruction(), true);
}
{
// a non-range instruction
const uint8_t bytecode[] = {0x44, 0x58};
BytecodeInstruction inst(bytecode);
EXPECT_EQ(inst.IsRangeInstruction(), false);
}
}
TEST(BytecodeInstruction, GetRangeInsLastRegIdx) {
{
// a non-range instruction
const uint8_t bytecode[] = {0x3f, 0x0a, 0x00, 0x00};
BytecodeInstruction inst(bytecode);
EXPECT_EQ(inst.GetRangeInsLastRegIdx(), std::nullopt);
}
{
// newobjrange 0xb, 0x3, v6
const uint8_t bytecode[] = {0x08, 0x0b, 0x03, 0x06};
BytecodeInstruction inst(bytecode);
EXPECT_EQ(inst.GetRangeInsLastRegIdx().value(), 8);
}
}
}

View File

@ -0,0 +1,47 @@
/**
* Copyright (c) 2024 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 <cstddef>
#include <cstdint>
#include <gtest/gtest.h>
#include <sstream>
#include <type_traits>
#include "bytecode_instruction-inl.h"
namespace panda::test {
TEST(BytecodeInstruction, IsReturnOrThrowInstruction)
{
{
// return
const uint8_t bytecode[] = {0x65};
BytecodeInstruction inst(bytecode);
EXPECT_EQ(inst.IsReturnOrThrowInstruction(), true);
}
{
// throw
const uint8_t bytecode[] = {0xfe};
BytecodeInstruction inst(bytecode);
EXPECT_EQ(inst.IsReturnOrThrowInstruction(), true);
}
{
// not return, not throw
const uint8_t bytecode[] = {0x3f, 0x00, 0x0c, 0x00};
BytecodeInstruction inst(bytecode);
EXPECT_EQ(inst.IsReturnOrThrowInstruction(), false);
}
}
}

View File

@ -241,12 +241,7 @@ bool Verifier::CollectIdInInstructions(const panda_file::File::EntityId &method_
if (bc_ins.HasFlag(BytecodeInstruction::Flags::LITERALARRAY_ID)) {
// the idx of any instruction with a literal id is 0
// except defineclasswithbuffer/callruntime.definesendableclass
size_t idx = 0;
if (bc_ins.GetOpcode() == Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8 ||
bc_ins.GetOpcode() == Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8 ||
bc_ins.GetOpcode() == Opcode::CALLRUNTIME_DEFINESENDABLECLASS_PREF_IMM16_ID16_ID16_IMM16_V8) {
idx = 1;
}
size_t idx = bc_ins.GetLiteralIndex();
const auto arg_literal_idx = bc_ins.GetId(idx).AsIndex();
const auto literal_id = file_->ResolveMethodIndex(method_id, arg_literal_idx);
ins_literal_ids_.insert(literal_id.GetOffset());
@ -316,48 +311,17 @@ size_t Verifier::GetVRegCount(const BytecodeInstruction &bc_ins)
return idx;
}
bool Verifier::IsRangeInstruction(const Opcode &ins_opcode)
{
switch (ins_opcode) {
case Opcode::WIDE_NEWOBJRANGE_PREF_IMM16_V8:
case Opcode::WIDE_CALLRANGE_PREF_IMM16_V8:
case Opcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8:
case Opcode::WIDE_SUPERCALLARROWRANGE_PREF_IMM16_V8:
case Opcode::CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_V8_V8:
case Opcode::WIDE_CREATEOBJECTWITHEXCLUDEDKEYS_PREF_IMM16_V8_V8:
case Opcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8:
case Opcode::NEWOBJRANGE_IMM8_IMM8_V8:
case Opcode::NEWOBJRANGE_IMM16_IMM8_V8:
case Opcode::CALLTHISRANGE_IMM8_IMM8_V8:
case Opcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8:
case Opcode::CALLRANGE_IMM8_IMM8_V8:
case Opcode::SUPERCALLARROWRANGE_IMM8_IMM8_V8: {
return true;
}
default: {
return false;
}
}
}
bool Verifier::IsRangeInstAndHasInvalidRegIdx(const BytecodeInstruction &bc_ins,
const size_t count, uint64_t valid_regs_num)
{
Opcode ins_opcode = bc_ins.GetOpcode();
ASSERT(IsRangeInstruction(ins_opcode));
ASSERT(bc_ins.IsRangeInstruction());
uint64_t reg_idx = bc_ins.GetVReg(FIRST_INDEX);
if (IsRegIdxOutOfBounds(reg_idx, valid_regs_num)) { // for [format: +AA/+AAAA vBB vCC], vBB can be verified here
return true;
}
std::optional<uint64_t> range_reg_num = GetRangeRegNum(bc_ins, ins_opcode);
if (!range_reg_num.has_value()) {
LOG(ERROR, VERIFIER) << "Failed to get range register number!";
return true;
}
std::optional<uint64_t> max_ins_reg_idx_opt = CalculateMaxRegIdx(bc_ins, ins_opcode, range_reg_num.value());
std::optional<uint64_t> max_ins_reg_idx_opt = bc_ins.GetRangeInsLastRegIdx();
if (!max_ins_reg_idx_opt.has_value()) {
LOG(ERROR, VERIFIER) << "Integer overflow detected during register index calculation!";
return true;
@ -371,36 +335,6 @@ bool Verifier::IsRangeInstAndHasInvalidRegIdx(const BytecodeInstruction &bc_ins,
return false;
}
std::optional<uint64_t> Verifier::GetRangeRegNum(const BytecodeInstruction &bc_ins, Opcode ins_opcode)
{
// For the range instruction, where A stores the number of registers
// The actual register indexes are divided into three categories: 0~B+A-1 / 0~B+A / 0~C+A
switch (ins_opcode) {
// format: +AA vBB -> max register idx: B+A-1
case Opcode::WIDE_NEWOBJRANGE_PREF_IMM16_V8:
case Opcode::WIDE_CALLRANGE_PREF_IMM16_V8:
case Opcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8:
case Opcode::WIDE_SUPERCALLARROWRANGE_PREF_IMM16_V8:
// format: +AA/+AAAA vBB vCC -> max register idx: C+A
case Opcode::CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_V8_V8:
case Opcode::WIDE_CREATEOBJECTWITHEXCLUDEDKEYS_PREF_IMM16_V8_V8:
// format: +AAAA vBB -> max register idx: B+A
case Opcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8:
return bc_ins.GetImmData(FIRST_INDEX);
// format: ic +AA vBB -> max register idx: B+A-1
case Opcode::NEWOBJRANGE_IMM8_IMM8_V8:
case Opcode::NEWOBJRANGE_IMM16_IMM8_V8:
case Opcode::CALLTHISRANGE_IMM8_IMM8_V8:
case Opcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8:
case Opcode::CALLRANGE_IMM8_IMM8_V8:
case Opcode::SUPERCALLARROWRANGE_IMM8_IMM8_V8:
return bc_ins.GetImmData(SECOND_INDEX);
default:
LOG(ERROR, VERIFIER) << "Instruction processing error: There is an unprocessed range instruction!";
return std::nullopt;
}
}
bool Verifier::IsRegIdxOutOfBounds(uint64_t reg_idx, uint64_t valid_regs_num)
{
if (reg_idx >= valid_regs_num) {
@ -411,46 +345,9 @@ bool Verifier::IsRegIdxOutOfBounds(uint64_t reg_idx, uint64_t valid_regs_num)
return false;
}
std::optional<uint64_t> Verifier::CalculateMaxRegIdx(const BytecodeInstruction &bc_ins,
Opcode ins_opcode, uint64_t range_reg_num)
{
switch (ins_opcode) {
// format: +AA vBB -> max register idx: B+A-1
case Opcode::WIDE_NEWOBJRANGE_PREF_IMM16_V8:
case Opcode::WIDE_CALLRANGE_PREF_IMM16_V8:
case Opcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8:
case Opcode::WIDE_SUPERCALLARROWRANGE_PREF_IMM16_V8:
if (range_reg_num > 0) {
return SafeAdd(bc_ins.GetVReg(FIRST_INDEX), range_reg_num - 1);
}
return SafeAdd(bc_ins.GetVReg(FIRST_INDEX), range_reg_num);
// format: +AAAA vBB -> max register idx: B+A
case Opcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8:
return SafeAdd(bc_ins.GetVReg(FIRST_INDEX), range_reg_num);
// format: ic +AA vBB -> max register idx: B+A-1
case Opcode::NEWOBJRANGE_IMM8_IMM8_V8:
case Opcode::NEWOBJRANGE_IMM16_IMM8_V8:
case Opcode::CALLTHISRANGE_IMM8_IMM8_V8:
case Opcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8:
case Opcode::CALLRANGE_IMM8_IMM8_V8:
case Opcode::SUPERCALLARROWRANGE_IMM8_IMM8_V8:
if (range_reg_num > 0) {
return SafeAdd(bc_ins.GetVReg(FIRST_INDEX), range_reg_num - 1);
}
return SafeAdd(bc_ins.GetVReg(FIRST_INDEX), range_reg_num);
// format: +AA/+AAAA vBB vCC -> max register idx: C+A
case Opcode::CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_V8_V8:
case Opcode::WIDE_CREATEOBJECTWITHEXCLUDEDKEYS_PREF_IMM16_V8_V8:
return SafeAdd(bc_ins.GetVReg(SECOND_INDEX), range_reg_num);
default:
LOG(ERROR, VERIFIER) << "Instruction processing error: Unprocessed range instruction!";
return std::nullopt;
}
}
bool Verifier::CheckVRegIdx(const BytecodeInstruction &bc_ins, const size_t count, uint64_t valid_regs_num)
{
if (IsRangeInstruction(bc_ins.GetOpcode()) &&
if (bc_ins.IsRangeInstruction() &&
IsRangeInstAndHasInvalidRegIdx(bc_ins, count, valid_regs_num)) {
return false;
}
@ -669,77 +566,6 @@ bool Verifier::VerifyLiteralArrays()
return true;
}
bool Verifier::IsJumpInstruction(const Opcode &ins_opcode)
{
bool valid = true;
switch (ins_opcode) {
case Opcode::JMP_IMM8:
case Opcode::JMP_IMM16:
case Opcode::JEQZ_IMM8:
case Opcode::JEQZ_IMM16:
case Opcode::JNEZ_IMM8:
case Opcode::JSTRICTEQZ_IMM8:
case Opcode::JNSTRICTEQZ_IMM8:
case Opcode::JEQNULL_IMM8:
case Opcode::JNENULL_IMM8:
case Opcode::JSTRICTEQNULL_IMM8:
case Opcode::JNSTRICTEQNULL_IMM8:
case Opcode::JEQUNDEFINED_IMM8:
case Opcode::JNEUNDEFINED_IMM8:
case Opcode::JSTRICTEQUNDEFINED_IMM8:
case Opcode::JNSTRICTEQUNDEFINED_IMM8:
case Opcode::JEQ_V8_IMM8:
case Opcode::JNE_V8_IMM8:
case Opcode::JSTRICTEQ_V8_IMM8:
case Opcode::JNSTRICTEQ_V8_IMM8:
case Opcode::JMP_IMM32:
case Opcode::JEQZ_IMM32:
case Opcode::JNEZ_IMM16:
case Opcode::JNEZ_IMM32:
case Opcode::JSTRICTEQZ_IMM16:
case Opcode::JNSTRICTEQZ_IMM16:
case Opcode::JEQNULL_IMM16:
case Opcode::JNENULL_IMM16:
case Opcode::JSTRICTEQNULL_IMM16:
case Opcode::JNSTRICTEQNULL_IMM16:
case Opcode::JEQUNDEFINED_IMM16:
case Opcode::JNEUNDEFINED_IMM16:
case Opcode::JSTRICTEQUNDEFINED_IMM16:
case Opcode::JEQ_V8_IMM16:
case Opcode::JNE_V8_IMM16:
case Opcode::JSTRICTEQ_V8_IMM16:
case Opcode::JNSTRICTEQ_V8_IMM16: {
valid = true;
break;
}
default: {
valid = false;
break;
}
}
return valid;
}
bool Verifier::IsReturnAndThrowInstruction(const Opcode &ins_opcode)
{
switch (ins_opcode) {
case Opcode::RETURN:
case Opcode::RETURNUNDEFINED:
case Opcode::THROW_PREF_NONE:
case Opcode::THROW_NOTEXISTS_PREF_NONE:
case Opcode::THROW_PATTERNNONCOERCIBLE_PREF_NONE:
case Opcode::THROW_DELETESUPERPROPERTY_PREF_NONE:
case Opcode::THROW_CONSTASSIGNMENT_PREF_V8: {
return true;
}
default: {
break;
}
}
return false;
}
bool Verifier::PrecomputeInstructionIndices(const BytecodeInstruction &bc_ins_start,
const BytecodeInstruction &bc_ins_last)
{
@ -778,8 +604,7 @@ bool Verifier::VerifyJumpInstruction(const BytecodeInstruction &bc_ins, const By
// update maximum backward offset
const auto bc_ins_backward_size = bc_ins.GetAddress() - bc_ins_first.GetAddress();
Opcode ins_opcode = bc_ins.GetOpcode();
if (IsJumpInstruction(ins_opcode)) {
if (bc_ins.IsJumpInstruction()) {
std::optional<int64_t> immdata = GetFirstImmFromInstruction(bc_ins);
if (!immdata.has_value()) {
LOG(ERROR, VERIFIER) << "Fail to get immediate data!";
@ -971,49 +796,55 @@ bool Verifier::VerifyMethodRegisterIndex(panda_file::CodeDataAccessor &code_acce
return true;
}
bool Verifier::VerifyMethodInstructions(const VerifyMethodParams &params)
bool Verifier::VerifyMethodInstructions(const MethodInfos &infos)
{
while (params.bc_ins.GetAddress() != params.bc_ins_last.GetAddress()) {
if (params.bc_ins.GetAddress() > params.bc_ins_last.GetAddress()) {
LOG(ERROR, VERIFIER) << "> error encountered at " << params.method_accessor.GetCodeId().value()
<< " (0x" << std::hex << params.method_accessor.GetCodeId().value()
auto current_ins = infos.bc_ins;
auto last_ins = infos.bc_ins_last;
auto code_id = infos.method_accessor.GetCodeId().value();
auto method_id = infos.method_id;
auto valid_regs_num = infos.valid_regs_num.value();
auto ins_slot_num = infos.ins_slot_num;
auto has_slot = infos.has_slot;
auto is_two_slot = infos.is_two_slot;
while (current_ins.GetAddress() != last_ins.GetAddress()) {
if (current_ins.GetAddress() > last_ins.GetAddress()) {
LOG(ERROR, VERIFIER) << "> error encountered at " << code_id
<< " (0x" << std::hex << code_id
<< "). bytecode instructions sequence corrupted for method "
<< params.method_id
<< method_id
<< "! went out of bounds";
return false;
}
Opcode ins_opcode = params.bc_ins.GetOpcode();
if (!IsJumpInstruction(ins_opcode) && !IsReturnAndThrowInstruction(ins_opcode)
&& params.bc_ins.GetNext().GetAddress() == params.bc_ins_last.GetAddress()) {
LOG(ERROR, VERIFIER) << "> error encountered at " << params.method_accessor.GetCodeId().value()
<< " (0x" << std::hex << params.method_accessor.GetCodeId().value()
if (!current_ins.IsJumpInstruction() && !current_ins.IsReturnOrThrowInstruction()
&& current_ins.GetNext().GetAddress() == last_ins.GetAddress()) {
LOG(ERROR, VERIFIER) << "> error encountered at " << code_id
<< " (0x" << std::hex << code_id
<< "). bytecode instructions sequence corrupted for method "
<< params.method_id
<< method_id
<< "! went out of bounds";
return false;
}
const size_t count = GetVRegCount(params.bc_ins);
if (count != 0 && !CheckVRegIdx(params.bc_ins, count, params.valid_regs_num.value())) {
const size_t count = GetVRegCount(current_ins);
if (count != 0 && !CheckVRegIdx(current_ins, count, valid_regs_num)) {
return false;
}
if (!VerifyJumpInstruction(params.bc_ins, params.bc_ins_last,
params.bc_ins_init, params.ins_arr,
params.method_accessor.GetCodeId().value())) {
if (!VerifyJumpInstruction(current_ins, last_ins,
infos.bc_ins_init, infos.ins_arr,
code_id)) {
LOG(ERROR, VERIFIER) << "Invalid target position of jump instruction";
return false;
}
if (!GetIcSlotFromInstruction(params.bc_ins, params.ins_slot_num,
params.has_slot, params.is_two_slot)) {
if (!GetIcSlotFromInstruction(current_ins, ins_slot_num,
has_slot, is_two_slot)) {
LOG(ERROR, VERIFIER) << "Fail to get first slot index!";
return false;
}
params.bc_ins = params.bc_ins.GetNext();
current_ins = current_ins.GetNext();
}
return true;
}
bool Verifier::CheckConstantPoolMethodContent(const panda_file::File::EntityId &method_id)
{
panda_file::MethodDataAccessor method_accessor(*file_, method_id);
@ -1031,8 +862,8 @@ bool Verifier::CheckConstantPoolMethodContent(const panda_file::File::EntityId &
bool has_slot = false;
bool is_two_slot = false;
std::optional<uint64_t> valid_regs_num = 0;
VerifyMethodParams params = {bc_ins_init, bc_ins, bc_ins_last, method_accessor, method_id,
valid_regs_num, ins_arr, ins_slot_num, has_slot, is_two_slot};
MethodInfos infos = {bc_ins_init, bc_ins, bc_ins_last, method_accessor, method_id,
valid_regs_num, ins_arr, ins_slot_num, has_slot, is_two_slot};
if (ins_size <= 0) {
LOG(ERROR, VERIFIER) << "Fail to verify code size!";
return false;
@ -1052,7 +883,7 @@ bool Verifier::CheckConstantPoolMethodContent(const panda_file::File::EntityId &
LOG(ERROR, VERIFIER) << "Fail to verify try blocks or catch blocks!";
return false;
}
if (!VerifyMethodInstructions(params)) {
if (!VerifyMethodInstructions(infos)) {
LOG(ERROR, VERIFIER) << "Fail to verify method instructions!";
return false;
}

View File

@ -40,7 +40,7 @@ enum class ActionType {
COLLECTINFOS,
};
struct VerifyMethodParams {
struct MethodInfos {
const BytecodeInstruction &bc_ins_init;
BytecodeInstruction &bc_ins;
const BytecodeInstruction &bc_ins_last;
@ -92,13 +92,9 @@ private:
bool VerifyMethodId(const uint32_t &method_id) const;
bool VerifyLiteralId(const uint32_t &literal_id) const;
bool VerifyStringId(const uint32_t &string_id) const;
bool IsRangeInstruction(const Opcode &ins_opcode);
bool IsRangeInstAndHasInvalidRegIdx(const BytecodeInstruction &bc_ins,
const size_t count, uint64_t valid_regs_num);
std::optional<uint64_t> GetRangeRegNum(const BytecodeInstruction &bc_ins, Opcode ins_opcode);
bool IsRegIdxOutOfBounds(uint64_t reg_idx, uint64_t valid_regs_num);
std::optional<uint64_t> CalculateMaxRegIdx(const BytecodeInstruction &bc_ins,
Opcode ins_opcode, uint64_t range_reg_num);
bool CheckVRegIdx(const BytecodeInstruction &bc_ins, const size_t count, uint64_t valid_regs_num);
std::optional<int64_t> GetFirstImmFromInstruction(const BytecodeInstruction &bc_ins);
std::optional<uint64_t> GetSlotNumberFromAnnotation(panda_file::MethodDataAccessor &method_accessor);
@ -108,7 +104,6 @@ private:
bool IsModuleLiteralId(const panda_file::File::EntityId &id) const;
bool VerifySingleLiteralArray(const panda_file::File::EntityId &literal_id);
bool VerifyLiteralArrays();
bool IsJumpInstruction(const Opcode &ins_opcode);
bool VerifyJumpInstruction(const BytecodeInstruction &bc_ins, const BytecodeInstruction &bc_ins_last,
const BytecodeInstruction &bc_ins_init, const uint8_t *ins_arr,
panda_file::File::EntityId code_id);
@ -123,12 +118,11 @@ private:
const BytecodeInstruction &bc_ins_last);
bool VerifyTryBlocks(panda_file::CodeDataAccessor &code_accessor, const BytecodeInstruction &bc_ins,
const BytecodeInstruction &bc_ins_last);
bool IsReturnAndThrowInstruction(const Opcode &ins_opcode);
bool PrecomputeInstructionIndices(const BytecodeInstruction &bc_ins_start, const BytecodeInstruction &bc_ins_last);
bool IsMethodBytecodeInstruction(const BytecodeInstruction &bc_ins_cur);
bool VerifyMethodRegisterIndex(panda_file::CodeDataAccessor &code_accessor,
std::optional<uint64_t> &max_reg_idx);
bool VerifyMethodInstructions(const VerifyMethodParams &params);
bool VerifyMethodInstructions(const MethodInfos &infos);
inline bool IsImpureNaN(double value)
{