1/*
2 * Copyright (c) 2023 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#ifndef VERIFIER_VERIFIER_H
17#define VERIFIER_VERIFIER_H
18
19#include <string>
20#include <unordered_map>
21#include <unordered_set>
22
23#include "bytecode_instruction_enum_gen.h"
24#include "bytecode_instruction-inl.h"
25#include "code_data_accessor-inl.h"
26#include "file.h"
27#include "file_items.h"
28#include "literal_data_accessor.h"
29#include "method_data_accessor-inl.h"
30#include "utils/bit_utils.h"
31#include "utils/utf.h"
32
33namespace panda::verifier {
34
35using Opcode = BytecodeInstruction::Opcode;
36using TaggedType = uint64_t;
37
38enum class ActionType {
39    CHECKCONSTPOOLCONTENT,
40    COLLECTINFOS,
41};
42
43struct MethodInfos {
44    const BytecodeInstruction &bc_ins_init;
45    BytecodeInstruction &bc_ins;
46    const BytecodeInstruction &bc_ins_last;
47    panda_file::MethodDataAccessor &method_accessor;
48    const panda_file::File::EntityId &method_id;
49    std::optional<uint64_t> &valid_regs_num;
50    const uint8_t *ins_arr;
51    uint32_t &ins_slot_num;
52    bool &has_slot;
53    bool &is_two_slot;
54};
55
56class Verifier {
57public:
58    explicit Verifier(const std::string &filename);
59    ~Verifier() = default;
60
61    bool Verify();
62    bool CollectIdInfos();
63    bool VerifyChecksum();
64    bool VerifyConstantPool();
65    bool VerifyRegisterIndex();
66    bool VerifyConstantPoolIndex();
67    bool VerifyConstantPoolContent();
68
69    bool include_literal_array_ids = true;
70    std::vector<uint32_t> literal_ids_;
71    std::unordered_map<uint32_t, uint32_t> inner_literal_map_;
72    std::unordered_map<uint32_t, uint32_t> inner_method_map_;
73
74    static constexpr size_t TAG_BITS_SIZE = 16;
75    static constexpr size_t TAG_BITS_SHIFT = BitNumbers<TaggedType>() - TAG_BITS_SIZE;
76    static_assert((TAG_BITS_SHIFT + TAG_BITS_SIZE) == sizeof(TaggedType) * CHAR_BIT, "Insufficient bits!");
77    static constexpr TaggedType TAG_MASK = ((1ULL << TAG_BITS_SIZE) - 1ULL) << TAG_BITS_SHIFT;
78    static constexpr TaggedType TAG_INT = TAG_MASK;
79    static constexpr size_t DOUBLE_ENCODE_OFFSET_BIT = 48;
80    static constexpr TaggedType DOUBLE_ENCODE_OFFSET = 1ULL << DOUBLE_ENCODE_OFFSET_BIT;
81
82private:
83    void GetLiteralIds();
84    template <typename T>
85    void PushToLiteralIds(T &ids);
86    void GetConstantPoolIds();
87    bool CollectIdInInstructions(const panda_file::File::EntityId &method_id);
88    void CollectModuleLiteralId(const panda_file::File::EntityId &field_id);
89    bool CheckConstantPool(const verifier::ActionType type);
90    size_t GetVRegCount(const BytecodeInstruction &bc_ins);
91    bool CheckConstantPoolActions(const verifier::ActionType type, panda_file::File::EntityId method_id);
92    bool VerifyMethodId(const uint32_t &method_id) const;
93    bool VerifyLiteralId(const uint32_t &literal_id) const;
94    bool VerifyStringId(const uint32_t &string_id) const;
95    bool IsRangeInstAndHasInvalidRegIdx(const BytecodeInstruction &bc_ins,
96                                        const size_t count, uint64_t valid_regs_num);
97    bool IsRegIdxOutOfBounds(uint64_t reg_idx, uint64_t valid_regs_num);
98    bool CheckVRegIdx(const BytecodeInstruction &bc_ins, const size_t count, uint64_t valid_regs_num);
99    std::optional<int64_t> GetFirstImmFromInstruction(const BytecodeInstruction &bc_ins);
100    std::optional<uint64_t> GetSlotNumberFromAnnotation(panda_file::MethodDataAccessor &method_accessor);
101    bool VerifyMethodIdInLiteralArray(const uint32_t &id);
102    bool VerifyStringIdInLiteralArray(const uint32_t &id);
103    bool VerifyLiteralIdInLiteralArray(const uint32_t &id);
104    bool IsModuleLiteralId(const panda_file::File::EntityId &id) const;
105    bool VerifySingleLiteralArray(const panda_file::File::EntityId &literal_id);
106    bool VerifyLiteralArrays();
107    bool VerifyJumpInstruction(const BytecodeInstruction &bc_ins, const BytecodeInstruction &bc_ins_last,
108                               const BytecodeInstruction &bc_ins_init, const uint8_t *ins_arr,
109                               panda_file::File::EntityId code_id);
110    bool GetIcSlotFromInstruction(const BytecodeInstruction &bc_ins, uint32_t &first_slot_index, bool &has_slot,
111                                  bool &is_two_slot);
112    bool VerifySlotNumber(panda_file::MethodDataAccessor &method_accessor, const uint32_t &slot_number,
113                          const panda_file::File::EntityId &method_id);
114    bool CheckConstantPoolMethodContent(const panda_file::File::EntityId &method_id);
115    bool CheckConstantPoolIndex() const;
116    std::optional<uint64_t> SafeAdd(uint64_t a, uint64_t b) const;
117    bool VerifyCatchBlocks(panda_file::CodeDataAccessor::TryBlock &try_block, const BytecodeInstruction &bc_ins,
118                           const BytecodeInstruction &bc_ins_last);
119    bool VerifyTryBlocks(panda_file::CodeDataAccessor &code_accessor, const BytecodeInstruction &bc_ins,
120                         const BytecodeInstruction &bc_ins_last);
121    bool PrecomputeInstructionIndices(const BytecodeInstruction &bc_ins_start, const BytecodeInstruction &bc_ins_last);
122    bool IsMethodBytecodeInstruction(const BytecodeInstruction &bc_ins_cur);
123    bool VerifyMethodRegisterIndex(panda_file::CodeDataAccessor &code_accessor,
124                                   std::optional<uint64_t> &max_reg_idx);
125    bool VerifyMethodInstructions(const MethodInfos &infos);
126
127    inline bool IsImpureNaN(double value)
128    {
129        // Tests if the double value would break tagged double encoding.
130        return bit_cast<TaggedType>(value) >= (Verifier::TAG_INT - Verifier::DOUBLE_ENCODE_OFFSET);
131    }
132
133    std::unique_ptr<const panda_file::File> file_;
134    std::vector<uint32_t> constant_pool_ids_;
135    std::vector<uint32_t> all_method_ids_;
136    std::unordered_set<uint32_t> ins_method_ids_;
137    std::unordered_set<uint32_t> ins_literal_ids_;
138    std::unordered_set<uint32_t> ins_string_ids_;
139    std::unordered_set<uint32_t> module_literals_;
140    static constexpr size_t DEFAULT_ARGUMENT_NUMBER = 3;
141    static constexpr uint32_t FILE_CONTENT_OFFSET = 12U;
142    static constexpr size_t FIRST_INDEX = 0;
143    static constexpr size_t SECOND_INDEX = 1;
144    static constexpr uint64_t  MAX_REGISTER_INDEX = 0xffff;
145    std::unordered_map<const uint8_t*, size_t> instruction_index_map_;
146};
147} // namespace panda::verifier
148#endif
149