1/* 2 * Copyright (c) 2021-2024 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% array_first_funcs_names = [] 39% dispatch_table_hash = Hash.new() 40% Panda::instructions.uniq{|i| i.mnemonic}.each do |i| 41% combination_flags = "" 42% mnemonic = i.mnemonic.split('.').map { |p| p == '64' ? 'Wide' : p.capitalize }.join 43% value_dispatch = %(template <BytecodeInstructionSafe::Format FORMAT> \n) + 44% %(std::optional<VerificationStatus> Handle) + mnemonic + %(()) 45% combination_flags += "Valid_" 46% if !i.exceptions.include?('x_none') 47% combination_flags += "Except_" 48% else 49% combination_flags += "ExceptNo_" 50% end 51% if i.properties.include?('jump') 52% if i.properties.include?('conditional') 53% combination_flags += "CondJump_" 54% else 55% combination_flags += "NoncondJump_" 56% end 57% elsif i.stripped_mnemonic == 'throw' 58% combination_flags += "InstTypeThrow_" 59% elsif i.properties.include?('return') 60% combination_flags += "InstTypeReturn_" 61% else 62% combination_flags += "InstTypeNormal_" 63% end 64% if i.properties.include?('jump') 65% combination_flags += "HasJump_" 66% else 67% combination_flags += "HasNoJump_" 68% end 69% combination_flags += "GetNext_" 70% func_name = ("Handle_" + (combination_flags.sub("Valid_", "")).sub("GetNext_", "")).chomp("_") 71% flag = dispatch_table_hash.include?(combination_flags) 72% if flag == false 73% dispatch_table_hash[combination_flags] = [] 74% array_first_funcs_names.push(func_name) 75% end 76% dispatch_table_hash[combination_flags].push(value_dispatch) 77% end 78% 79% body_gen_parts = Hash.new() 80% body_gen_parts = { 81% "Valid_" => %( 82% if (!inst_.IsValid()) { 83% LOG(DEBUG, VERIFIER) << "Next instruction offset is out of bounds of method body."; 84% return VerificationStatus::ERROR; 85% } 86% const uint8_t* pc = inst_.GetAddress(); 87% const size_t sz = inst_.Size(FORMAT); 88% ), 89% "Except_" => %( 90% constexpr bool EXC_SRC = true; 91% ), 92% "ExceptNo_" => %( 93% constexpr bool EXC_SRC = false; 94% ), 95% "CondJump_" => %( 96% const InstructionType instType = InstructionType::COND_JUMP; 97% LOG_INST(); 98% ), 99% "NoncondJump_" => %( 100% const InstructionType instType = InstructionType::JUMP; 101% LOG_INST(); 102% ), 103% "InstTypeThrow_" => %( 104% const InstructionType instType = InstructionType::THROW; 105% LOG_INST(); 106% ), 107% "InstTypeReturn_" => %( 108% const InstructionType instType = InstructionType::RETURN; 109% LOG_INST(); 110% ), 111% "InstTypeNormal_" => %( 112% const InstructionType instType = InstructionType::NORMAL; 113% LOG_INST() << (EXC_SRC ? " (exception source)" : "" ); 114% ), 115% "HasJump_" => %( 116% auto imm = inst_.GetImm<FORMAT>(); 117% if (!inst_.IsValid()) { 118% LOG(ERROR, VERIFIER) << "Jump instruction imm field is out of bounds of method body. " 119% << "Jump instruction offset: " << std::hex << static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc) - reinterpret_cast<uintptr_t>(inst_.GetFrom())); 120% return VerificationStatus::ERROR; 121% } 122% auto targetInst = inst_.JumpTo(imm); 123% const uint8_t* jumpPc = targetInst.GetAddress(); 124% if (!targetInst.IsValid()) { 125% auto offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc) - reinterpret_cast<uintptr_t>(inst_.GetFrom())); 126% LOG(ERROR, VERIFIER) << "Jump offset is out of bounds of method body. " 127% << "Jump instruction offset: 0x" << std::hex << offset 128% << ". Jump target offset: 0x" << std::hex << (imm + offset); 129% return VerificationStatus::ERROR; 130% } 131% ), 132% "HasNoJump_" => %( 133% const uint8_t* jumpPc = nullptr; 134% ), 135% "GetNext_" => %( 136% inst_ = inst_.GetNext<FORMAT>(); 137% return handler_(instType, pc, sz, EXC_SRC, jumpPc); 138% ) 139% } 140% 141% full_code_hash = Hash.new("") 142% dispatch_table_hash.each { |key_comb, value_comb| 143% body_gen_parts.each { |key_parts, value_parts| 144% string_to_compare_with = key_comb.to_s 145% flag_compare = string_to_compare_with.include?(key_parts.to_s) 146% if flag_compare == true 147% full_code_hash[key_comb] += value_parts 148% end 149% } 150% } 151% 152% 153% array_first_funcs_names.zip(full_code_hash.values).each { |func_name, code| 154 template <BytecodeInstructionSafe::Format FORMAT> 155 // NOLINTNEXTLINE(readability-identifier-naming) 156 std::optional<VerificationStatus> <%= func_name %>() { 157 <%= code%> 158 } 159%} 160private: 161 BytecodeInstructionSafe inst_; 162 CflowHandler handler_; 163}; 164 165template <typename CflowHandler> 166// NOLINTNEXTLINE(readability-function-size) 167VerificationStatus IterateOverInstructions(const uint8_t* pc, const uint8_t* from, const uint8_t* to, CflowHandler cflowHandler) { 168#if defined(__clang__) 169#pragma clang diagnostic push 170#pragma clang diagnostic ignored "-Wvoid-ptr-dereference" 171#pragma clang diagnostic ignored "-Wgnu-label-as-value" 172#elif defined(__GNUC__) 173#pragma GCC diagnostic push 174#pragma GCC diagnostic ignored "-Wpedantic" 175#endif 176 177 // NOLINTNEXTLINE(readability-magic-numbers) 178 std::array<const void*, <%= Panda::dispatch_table.handler_names.size %>> dispatchTable{ 179% Panda::dispatch_table.handler_names.each do |name| 180 &&HANDLE_<%= name %>, 181% end 182 }; 183 184 CflowIterInstructionHandler handler(pc, from, to, std::move(cflowHandler)); 185 std::optional<VerificationStatus> status; 186 uint8_t secondaryOpcode; 187 188 if (!handler.IsPrimaryOpcodeValid()) { 189 LOG(ERROR, VERIFIER) << "Incorrect opcode"; 190 return VerificationStatus::ERROR; 191 } 192 // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto) 193 goto* dispatchTable[handler.GetPrimaryOpcode()]; 194 195% hash_handle_variants = Hash.new() 196% array_to_paste_zip = array_first_funcs_names.zip(dispatch_table_hash.values) 197% Panda::instructions.each do |i| 198% mnemonic = i.mnemonic.split('.').map { |p| p == '64' ? 'Wide' : p.capitalize }.join 199% array_to_paste_zip.each { | func, name_array| 200% flag = false 201% name_to_past = "" 202% name_array.each { |item | 203% flag = item.include?("Handle" + mnemonic.to_s) 204% if flag == true 205% name_to_past = func 206% break 207% end 208% } 209% if flag == true 210% status = "handler.template " + name_to_past + %(<BytecodeInstructionSafe::Format::) + i.format.pretty.upcase + %(>();) 211% flag_array_handle_variants = hash_handle_variants.include?(status) 212% if flag_array_handle_variants == false 213% hash_handle_variants[status] = [] 214% end 215% hash_handle_variants[status].push("HANDLE_" + i.handler_name) 216% break 217% end 218% } 219% end 220% hash_handle_variants.each { |key_status, labels_arr| 221% labels_arr.each { |item| 222<%= item%>:; 223% } 224 status = <%= key_status%> 225 // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto) 226 goto COMMON_BODY; 227% } 228 229COMMON_BODY: 230{ 231 if (status) { return *status; } 232 if (!handler.IsPrimaryOpcodeValid()) { 233 LOG(DEBUG, VERIFIER) << "Opcode value is out of range. " 234 << "Current value is: " << static_cast<int>(handler.GetPrimaryOpcode()) 235 << ". Allowed value is in the interval: [0, <%= Panda::dispatch_table.invalid_non_prefixed_interval.min - 1 %>] U " 236 << "[<%= Panda::dispatch_table.invalid_non_prefixed_interval.max + 1 %>, <%= Panda::dispatch_table.invalid_prefixes_interval.min + 1 %>] U " 237 << "[<%= Panda::dispatch_table.invalid_prefixes_interval.max + 1 %>, 255]"; 238 return VerificationStatus::ERROR; 239 } 240 // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto) 241 goto* dispatchTable[handler.GetPrimaryOpcode()]; 242} 243HANDLE_INVALID: 244 LOG(DEBUG, VERIFIER) << "Opcode value is out of range. " 245 << "Current value is: " << static_cast<int>(handler.GetPrimaryOpcode()) 246 << ". Allowed value is in the interval: [0, <%= Panda::dispatch_table.invalid_non_prefixed_interval.min - 1 %>] U " 247 << "[<%= Panda::dispatch_table.invalid_non_prefixed_interval.max + 1 %>, <%= Panda::dispatch_table.invalid_prefixes_interval.min + 1 %>] U " 248 << "[<%= Panda::dispatch_table.invalid_prefixes_interval.max + 1 %>, 255]"; 249 return VerificationStatus::ERROR; 250% Panda::prefixes.each do |p| 251HANDLE_<%= p.handler_name %>: 252 secondaryOpcode = handler.GetSecondaryOpcode(); 253 LOG(DEBUG, VERIFIER) << "CFLOW: Prefix subdispatch: " << "<%= p.name %>, " << static_cast<int32_t>(secondaryOpcode); 254 255 // NOLINTNEXTLINE(readability-magic-numbers) 256 if (secondaryOpcode > <%= Panda::dispatch_table.secondary_opcode_bound(p) %> ) { 257 LOG(ERROR, VERIFIER) << "Incorrect opcode"; 258 return VerificationStatus::ERROR; 259 } 260 // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto,readability-magic-numbers) 261 goto *dispatchTable[<%= Panda::dispatch_table.secondary_opcode_offset(p) %> + secondaryOpcode]; 262% end 263 264#if defined(__clang__) 265#pragma clang diagnostic pop 266#elif defined(__GNUC__) 267#pragma GCC diagnostic pop 268#endif 269} 270