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