• 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
16template <typename CflowHandler>
17class CflowIterInstructionHandler {
18public:
19    CflowIterInstructionHandler(const uint8_t* pc, const uint8_t* from, const uint8_t* to, CflowHandler handler)
20        : inst_(pc, from, to), handler_(std::move(handler)) {
21    }
22
23    uint8_t GetPrimaryOpcode()
24    {
25        return inst_.GetPrimaryOpcode();
26    }
27
28    uint8_t GetSecondaryOpcode()
29    {
30        return inst_.GetSecondaryOpcode();
31    }
32
33    bool IsPrimaryOpcodeValid() const
34    {
35        return inst_.IsPrimaryOpcodeValid();
36    }
37
38% Panda::instructions.uniq{|i| i.mnemonic}.each do |i|
39%   mnemonic = i.mnemonic.split('.').map { |p| p == '64' ? 'Wide' : p.capitalize }.join
40    template <BytecodeInstructionSafe::Format format>
41    std::optional<CflowStatus> Handle<%= mnemonic %>() {
42        if (!inst_.IsValid()) {
43            LOG(DEBUG, VERIFIER) << "Next instruction offset is out of bounds of method body.";
44            return CflowStatus::ERROR;
45        }
46        const uint8_t* pc = inst_.GetAddress();
47        const size_t sz = inst_.Size(format);
48%   if !i.exceptions.include?('x_none')
49        bool const EXC_SRC = true;
50%   else
51        bool const EXC_SRC = false;
52%   end
53%   if i.properties.include?('jump')
54%       if i.properties.include?('conditional')
55        const InstructionType inst_type = InstructionType::COND_JUMP;
56        LOG_INST();
57%       else
58        const InstructionType inst_type = InstructionType::JUMP;
59        LOG_INST();
60%       end
61%   elsif i.stripped_mnemonic == 'throw'
62        const InstructionType inst_type = InstructionType::THROW;
63        LOG_INST();
64%   elsif i.properties.include?('return')
65        const InstructionType inst_type = InstructionType::RETURN;
66        LOG_INST();
67%   else
68        const InstructionType inst_type = InstructionType::NORMAL;
69        LOG_INST() << (EXC_SRC ? " (exception source)" : "" );
70%   end
71%   if i.properties.include?('jump')
72        auto imm = inst_.GetImm<format>();
73        if (!inst_.IsValid()) {
74            LOG(ERROR, VERIFIER) << "Jump instruction imm field is out of bounds of method body. "
75                                 << "Jump instruction offset: " << std::hex << static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc) - reinterpret_cast<uintptr_t>(inst_.GetFrom()));
76            return CflowStatus::ERROR;
77        }
78        auto target_inst = inst_.JumpTo(imm);
79        const uint8_t* jump_pc = target_inst.GetAddress();
80        if (!target_inst.IsValid()) {
81            auto offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc) - reinterpret_cast<uintptr_t>(inst_.GetFrom()));
82            LOG(ERROR, VERIFIER) << "Jump offset is out of bounds of method body. "
83                                 << "Jump instruction offset: 0x" << std::hex << offset
84                                 << ". Jump target offset: 0x" << std::hex << (imm + offset);
85            return CflowStatus::ERROR;
86        }
87%   else
88        const uint8_t* jump_pc = nullptr;
89%   end
90        inst_ = inst_.GetNext<format>();
91        return handler_(inst_type, pc, sz, EXC_SRC, jump_pc);
92    }
93% end
94
95private:
96    BytecodeInstructionSafe inst_;
97    CflowHandler handler_;
98};
99
100template <typename CflowHandler>
101// NOLINTNEXTLINE(readability-function-size)
102CflowStatus IterateOverInstructions(const uint8_t* pc, const uint8_t* from, const uint8_t* to, CflowHandler cflow_handler) {
103#if defined(__clang__)
104#pragma clang diagnostic push
105#pragma clang diagnostic ignored "-Wvoid-ptr-dereference"
106#pragma clang diagnostic ignored "-Wgnu-label-as-value"
107#elif defined(__GNUC__)
108#pragma GCC diagnostic push
109#pragma GCC diagnostic ignored "-Wpedantic"
110#endif
111
112    std::array<const void*, <%= Panda::dispatch_table.handler_names.size %>> dispatch_table{
113% Panda::dispatch_table.handler_names.each do |name|
114        &&HANDLE_<%= name %>,
115% end
116    };
117
118    CflowIterInstructionHandler handler(pc, from, to, std::move(cflow_handler));
119    std::optional<CflowStatus> status;
120    uint8_t secondary_opcode;
121
122    if (!handler.IsPrimaryOpcodeValid()) {
123        LOG(ERROR, VERIFIER) << "Incorrect opcode";
124        return CflowStatus::ERROR;
125    }
126    goto* dispatch_table[handler.GetPrimaryOpcode()];
127
128% Panda::instructions.each do |i|
129%   mnemonic = i.mnemonic.split('.').map { |p| p == '64' ? 'Wide' : p.capitalize }.join
130HANDLE_<%= i.handler_name %>:
131    status = handler.template Handle<%= mnemonic %><BytecodeInstructionSafe::Format::<%= i.format.pretty.upcase %>>();
132    if (status) { return *status; }
133    if (!handler.IsPrimaryOpcodeValid()) {
134        LOG(DEBUG, VERIFIER) << "Opcode value is out of range. "
135                             << "Current value is: " << static_cast<int>(handler.GetPrimaryOpcode())
136                             << ". Allowed value is in the interval: [0, <%= Panda::dispatch_table.invalid_non_prefixed_interval.min - 1 %>] U "
137                                 << "[<%= Panda::dispatch_table.invalid_non_prefixed_interval.max + 1 %>, <%= Panda::dispatch_table.invalid_prefixes_interval.min + 1 %>] U "
138                                 << "[<%= Panda::dispatch_table.invalid_prefixes_interval.max + 1 %>, 255]";
139        return CflowStatus::ERROR;
140    }
141    goto* dispatch_table[handler.GetPrimaryOpcode()];
142% end
143HANDLE_INVALID:
144    LOG(DEBUG, VERIFIER) << "Opcode value is out of range. "
145                         << "Current value is: " << static_cast<int>(handler.GetPrimaryOpcode())
146                         << ". Allowed value is in the interval: [0, <%= Panda::dispatch_table.invalid_non_prefixed_interval.min - 1 %>] U "
147                                 << "[<%= Panda::dispatch_table.invalid_non_prefixed_interval.max + 1 %>, <%= Panda::dispatch_table.invalid_prefixes_interval.min + 1 %>] U "
148                                 << "[<%= Panda::dispatch_table.invalid_prefixes_interval.max + 1 %>, 255]";
149    return CflowStatus::ERROR;
150% Panda::prefixes.each do |p|
151HANDLE_<%= p.handler_name %>:
152    secondary_opcode = handler.GetSecondaryOpcode();
153    LOG(DEBUG, VERIFIER) << "CFLOW: Prefix subdispatch: " << "<%= p.name %>, " << static_cast<int32_t>(secondary_opcode);
154
155    if (secondary_opcode > <%= Panda::dispatch_table.secondary_opcode_bound(p) %> ) {
156        LOG(ERROR, VERIFIER) << "Incorrect opcode";
157        return CflowStatus::ERROR;
158    }
159    goto *dispatch_table[<%= Panda::dispatch_table.secondary_opcode_offset(p) %> + secondary_opcode];
160% end
161
162#if defined(__clang__)
163#pragma clang diagnostic pop
164#elif defined(__GNUC__)
165#pragma GCC diagnostic pop
166#endif
167}
168