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#include <codecvt> 16#include <locale> 17 18#include "verifier.h" 19#include "class_data_accessor-inl.h" 20#include "libpandafile/util/collect_util.h" 21#include "zlib.h" 22 23namespace panda::verifier { 24 25Verifier::Verifier(const std::string &filename) 26{ 27 auto file_to_verify = panda_file::File::Open(filename); 28 file_.swap(file_to_verify); 29} 30 31bool Verifier::Verify() 32{ 33 if (!VerifyChecksum()) { 34 return false; 35 } 36 37 if (!CollectIdInfos()) { 38 return false; 39 } 40 41 if (!VerifyConstantPool()) { 42 return false; 43 } 44 45 return true; 46} 47 48bool Verifier::CollectIdInfos() 49{ 50 if (file_ == nullptr) { 51 LOG(ERROR, VERIFIER) << "Failed to verify empty abc file!"; 52 return false; 53 } 54 GetConstantPoolIds(); 55 if (include_literal_array_ids) { 56 GetLiteralIds(); 57 } 58 return CheckConstantPool(verifier::ActionType::COLLECTINFOS); 59} 60 61bool Verifier::VerifyChecksum() 62{ 63 if (file_ == nullptr) { 64 LOG(ERROR, VERIFIER) << "Failed to verify empty abc file!"; 65 return false; 66 } 67 uint32_t file_size = file_->GetHeader()->file_size; 68 ASSERT(file_size > FILE_CONTENT_OFFSET); 69 uint32_t cal_checksum = adler32(1, file_->GetBase() + FILE_CONTENT_OFFSET, file_size - FILE_CONTENT_OFFSET); 70 return file_->GetHeader()->checksum == cal_checksum; 71} 72 73bool Verifier::VerifyConstantPool() 74{ 75 if (file_ == nullptr) { 76 LOG(ERROR, VERIFIER) << "Failed to verify empty abc file!"; 77 return false; 78 } 79 80 if (!CheckConstantPoolIndex()) { 81 return false; 82 } 83 84 if (!CheckConstantPool(verifier::ActionType::CHECKCONSTPOOLCONTENT)) { 85 return false; 86 } 87 88 if (!VerifyLiteralArrays()) { 89 return false; 90 } 91 92 return true; 93} 94 95bool Verifier::VerifyRegisterIndex() 96{ 97 if (file_ == nullptr) { 98 LOG(ERROR, VERIFIER) << "Failed to verify empty abc file!"; 99 return false; 100 } 101 102 for (const auto id : all_method_ids_) { 103 const panda_file::File::EntityId method_id = panda_file::File::EntityId(id); 104 panda_file::MethodDataAccessor method_accessor {*file_, method_id}; 105 if (!method_accessor.GetCodeId().has_value()) { 106 continue; 107 } 108 panda_file::CodeDataAccessor code_data(*file_, method_accessor.GetCodeId().value()); 109 const uint64_t reg_nums = code_data.GetNumVregs(); 110 const uint64_t arg_nums = code_data.GetNumArgs(); 111 const std::optional<uint64_t> valid_regs_num = SafeAdd(reg_nums, arg_nums); 112 if (!valid_regs_num.has_value()) { 113 LOG(ERROR, VERIFIER) << "Integer overflow detected during register index calculation!"; 114 return false; 115 } 116 if (valid_regs_num.value() > MAX_REGISTER_INDEX + 1) { 117 LOG(ERROR, VERIFIER) << "Register index exceeds the maximum allowable value (0xffff)!"; 118 return false; 119 } 120 auto bc_ins = BytecodeInstruction(code_data.GetInstructions()); 121 const auto bc_ins_last = bc_ins.JumpTo(code_data.GetCodeSize()); 122 ASSERT(arg_nums >= DEFAULT_ARGUMENT_NUMBER); 123 while (bc_ins.GetAddress() < bc_ins_last.GetAddress()) { 124 const size_t count = GetVRegCount(bc_ins); 125 if (count == 0) { // Skip instructions that do not use registers 126 bc_ins = bc_ins.GetNext(); 127 continue; 128 } 129 if (!CheckVRegIdx(bc_ins, count, valid_regs_num.value())) { 130 return false; 131 } 132 bc_ins = bc_ins.GetNext(); 133 } 134 } 135 return true; 136} 137 138bool Verifier::VerifyConstantPoolIndex() 139{ 140 if (file_ == nullptr) { 141 LOG(ERROR, VERIFIER) << "Failed to verify empty abc file!"; 142 return false; 143 } 144 145 if (!CheckConstantPoolIndex()) { 146 return false; 147 } 148 149 return true; 150} 151 152bool Verifier::VerifyConstantPoolContent() 153{ 154 if (file_ == nullptr) { 155 LOG(ERROR, VERIFIER) << "Failed to verify empty abc file!"; 156 return false; 157 } 158 159 if (!CheckConstantPool(verifier::ActionType::CHECKCONSTPOOLCONTENT)) { 160 return false; 161 } 162 163 if (!VerifyLiteralArrays()) { 164 return false; 165 } 166 167 return true; 168} 169 170void Verifier::GetConstantPoolIds() 171{ 172 if (constant_pool_ids_.size() != 0) { 173 return; 174 } 175 auto index_headers = file_->GetIndexHeaders(); 176 for (const auto &index_header : index_headers) { 177 auto region_indexs = file_->GetMethodIndex(&index_header); 178 for (auto &index : region_indexs) { 179 constant_pool_ids_.push_back(index.GetOffset()); 180 } 181 } 182} 183 184void Verifier::GetLiteralIds() 185{ 186 if (literal_ids_.size() != 0) { 187 return; 188 } 189 190 if (panda_file::ContainsLiteralArrayInHeader(file_->GetHeader()->version)) { 191 const auto literal_arrays = file_->GetLiteralArrays(); 192 PushToLiteralIds(literal_arrays); 193 } else { 194 panda::libpandafile::CollectUtil collect_util; 195 std::unordered_set<uint32_t> literal_array_ids; 196 collect_util.CollectLiteralArray(*file_, literal_array_ids); 197 PushToLiteralIds(literal_array_ids); 198 } 199} 200 201template <typename T> 202void Verifier::PushToLiteralIds(T &ids) 203{ 204 for (const auto id : ids) { 205 literal_ids_.push_back(id); 206 } 207} 208 209bool Verifier::CheckConstantPoolActions(const verifier::ActionType type, panda_file::File::EntityId method_id) 210{ 211 switch (type) { 212 case verifier::ActionType::CHECKCONSTPOOLCONTENT: { 213 return CheckConstantPoolMethodContent(method_id); 214 } 215 case verifier::ActionType::COLLECTINFOS: { 216 all_method_ids_.push_back(method_id.GetOffset()); 217 return CollectIdInInstructions(method_id); 218 } 219 default: { 220 return true; 221 } 222 } 223} 224 225bool Verifier::CollectIdInInstructions(const panda_file::File::EntityId &method_id) 226{ 227 panda_file::MethodDataAccessor method_accessor(*file_, method_id); 228 ASSERT(method_accessor.GetCodeId().has_value()); 229 panda_file::CodeDataAccessor code_accessor(*file_, method_accessor.GetCodeId().value()); 230 const auto ins_size = code_accessor.GetCodeSize(); 231 const auto ins_arr = code_accessor.GetInstructions(); 232 233 auto bc_ins = BytecodeInstruction(ins_arr); 234 const auto bc_ins_last = bc_ins.JumpTo(ins_size); 235 236 while (bc_ins.GetAddress() < bc_ins_last.GetAddress()) { 237 if (!bc_ins.IsPrimaryOpcodeValid()) { 238 LOG(ERROR, VERIFIER) << "Fail to verify primary opcode!"; 239 return false; 240 } 241 if (bc_ins.HasFlag(BytecodeInstruction::Flags::LITERALARRAY_ID)) { 242 // the idx of any instruction with a literal id is 0 243 // except defineclasswithbuffer/callruntime.definesendableclass 244 size_t idx = bc_ins.GetLiteralIndex(); 245 const auto arg_literal_idx = bc_ins.GetId(idx).AsIndex(); 246 const auto literal_id = file_->ResolveMethodIndex(method_id, arg_literal_idx); 247 ins_literal_ids_.insert(literal_id.GetOffset()); 248 } 249 if (bc_ins.HasFlag(BytecodeInstruction::Flags::METHOD_ID)) { 250 const auto arg_method_idx = bc_ins.GetId().AsIndex(); 251 const auto arg_method_id = file_->ResolveMethodIndex(method_id, arg_method_idx); 252 ins_method_ids_.insert(arg_method_id.GetOffset()); 253 } 254 if (bc_ins.HasFlag(BytecodeInstruction::Flags::STRING_ID)) { 255 const auto arg_string_idx = bc_ins.GetId().AsIndex(); 256 const auto string_id = file_->ResolveOffsetByIndex(method_id, arg_string_idx); 257 ins_string_ids_.insert(string_id.GetOffset()); 258 } 259 bc_ins = bc_ins.GetNext(); 260 } 261 return true; 262} 263 264void Verifier::CollectModuleLiteralId(const panda_file::File::EntityId &field_id) 265{ 266 panda_file::FieldDataAccessor field_accessor(*file_, field_id); 267 const auto literal_id = field_accessor.GetValue<uint32_t>().value(); 268 if (std::find(literal_ids_.begin(), literal_ids_.end(), literal_id) != literal_ids_.end()) { 269 module_literals_.insert(literal_id); 270 } 271} 272 273bool Verifier::CheckConstantPool(const verifier::ActionType type) 274{ 275 const auto class_idx = file_->GetClasses(); 276 for (size_t i = 0; i < class_idx.size(); i++) { 277 uint32_t class_id = class_idx[i]; 278 if (class_id > file_->GetHeader()->file_size) { 279 LOG(ERROR, VERIFIER) << "Binary file corrupted. out of bounds (0x" << std::hex 280 << file_->GetHeader()->file_size; 281 return false; 282 } 283 const panda_file::File::EntityId record_id {class_id}; 284 if (!file_->IsExternal(record_id)) { 285 panda_file::ClassDataAccessor class_accessor {*file_, record_id}; 286 bool check_res = true; 287 class_accessor.EnumerateMethods([&](panda_file::MethodDataAccessor &method_accessor) -> void { 288 check_res = check_res && CheckConstantPoolActions(type, method_accessor.GetMethodId()); 289 }); 290 if (!check_res) { 291 return false; 292 } 293 if (type == verifier::ActionType::COLLECTINFOS) { 294 class_accessor.EnumerateFields([&](panda_file::FieldDataAccessor &field_accessor) -> void { 295 CollectModuleLiteralId(field_accessor.GetFieldId()); 296 }); 297 } 298 } 299 } 300 301 return true; 302} 303 304size_t Verifier::GetVRegCount(const BytecodeInstruction &bc_ins) 305{ 306 size_t idx = 0; // Represents the idxTH register index in an instruction 307 BytecodeInstruction::Format format = bc_ins.GetFormat(); 308 while (bc_ins.HasVReg(format, idx)) { 309 idx++; 310 } 311 return idx; 312} 313 314bool Verifier::IsRangeInstAndHasInvalidRegIdx(const BytecodeInstruction &bc_ins, 315 const size_t count, uint64_t valid_regs_num) 316{ 317 ASSERT(bc_ins.IsRangeInstruction()); 318 319 uint64_t reg_idx = bc_ins.GetVReg(FIRST_INDEX); 320 if (IsRegIdxOutOfBounds(reg_idx, valid_regs_num)) { // for [format: +AA/+AAAA vBB vCC], vBB can be verified here 321 return true; 322 } 323 324 std::optional<uint64_t> max_ins_reg_idx_opt = bc_ins.GetRangeInsLastRegIdx(); 325 if (!max_ins_reg_idx_opt.has_value()) { 326 LOG(ERROR, VERIFIER) << "Integer overflow detected during register index calculation!"; 327 return true; 328 } 329 330 reg_idx = max_ins_reg_idx_opt.value(); 331 if (IsRegIdxOutOfBounds(reg_idx, valid_regs_num)) { 332 return true; 333 } 334 335 return false; 336} 337 338bool Verifier::IsRegIdxOutOfBounds(uint64_t reg_idx, uint64_t valid_regs_num) 339{ 340 if (reg_idx >= valid_regs_num) { 341 LOG(ERROR, VERIFIER) << "Register index out of bounds: 0x" << std::hex 342 << reg_idx << ", Max allowed: 0x" << std::hex << valid_regs_num; 343 return true; 344 } 345 return false; 346} 347 348bool Verifier::CheckVRegIdx(const BytecodeInstruction &bc_ins, const size_t count, uint64_t valid_regs_num) 349{ 350 if (bc_ins.IsRangeInstruction() && 351 IsRangeInstAndHasInvalidRegIdx(bc_ins, count, valid_regs_num)) { 352 return false; 353 } 354 for (size_t idx = 0; idx < count; idx++) { // Represents the idxTH register index in an instruction 355 uint16_t reg_idx = bc_ins.GetVReg(idx); 356 if (reg_idx >= valid_regs_num) { 357 LOG(ERROR, VERIFIER) << "Register index out of bounds: 0x" << std::hex 358 << reg_idx << ", Max allowed: 0x" << std::hex << valid_regs_num; 359 return false; 360 } 361 } 362 return true; 363} 364 365bool Verifier::VerifyMethodId(const uint32_t &method_id) const 366{ 367 auto iter = std::find(constant_pool_ids_.begin(), constant_pool_ids_.end(), method_id); 368 if (iter == constant_pool_ids_.end() || 369 (std::find(literal_ids_.begin(), literal_ids_.end(), method_id) != literal_ids_.end()) || 370 ins_string_ids_.count(method_id)) { 371 LOG(ERROR, VERIFIER) << "Fail to verify method id. method_id(0x" << std::hex << method_id << ")!"; 372 return false; 373 } 374 return true; 375} 376 377bool Verifier::VerifyLiteralId(const uint32_t &literal_id) const 378{ 379 auto iter = std::find(constant_pool_ids_.begin(), constant_pool_ids_.end(), literal_id); 380 if (iter == constant_pool_ids_.end() || 381 (std::find(all_method_ids_.begin(), all_method_ids_.end(), literal_id) != all_method_ids_.end()) || 382 ins_string_ids_.count(literal_id)) { 383 LOG(ERROR, VERIFIER) << "Fail to verify literal id. literal_id(0x" << std::hex << literal_id << ")!"; 384 return false; 385 } 386 return true; 387} 388 389bool Verifier::VerifyStringId(const uint32_t &string_id) const 390{ 391 auto iter = std::find(constant_pool_ids_.begin(), constant_pool_ids_.end(), string_id); 392 if (iter == constant_pool_ids_.end() || 393 ins_method_ids_.count(string_id) || 394 (std::find(literal_ids_.begin(), literal_ids_.end(), string_id) != literal_ids_.end())) { 395 LOG(ERROR, VERIFIER) << "Fail to verify string id. string_id(0x" << std::hex << string_id << ")!"; 396 return false; 397 } 398 return true; 399} 400 401std::optional<int64_t> Verifier::GetFirstImmFromInstruction(const BytecodeInstruction &bc_ins) 402{ 403 std::optional<int64_t> first_imm = std::optional<int64_t> {}; 404 size_t index = 0; 405 const auto format = bc_ins.GetFormat(); 406 if (bc_ins.HasImm(format, index)) { 407 first_imm = bc_ins.GetImm64(index); 408 } 409 410 return first_imm; 411} 412 413std::optional<uint64_t> Verifier::GetSlotNumberFromAnnotation(panda_file::MethodDataAccessor &method_accessor) 414{ 415 std::optional<uint64_t> slot_number {}; 416 method_accessor.EnumerateAnnotations([&](panda_file::File::EntityId annotation_id) { 417 panda_file::AnnotationDataAccessor ada(*file_, annotation_id); 418 auto *annotation_name = reinterpret_cast<const char *>(file_->GetStringData(ada.GetClassId()).data); 419 if (::strcmp("L_ESSlotNumberAnnotation;", annotation_name) == 0) { 420 uint32_t elem_count = ada.GetCount(); 421 for (uint32_t i = 0; i < elem_count; i++) { 422 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i); 423 auto *elem_name = reinterpret_cast<const char *>(file_->GetStringData(adae.GetNameId()).data); 424 if (::strcmp("SlotNumber", elem_name) == 0) { 425 slot_number = adae.GetScalarValue().GetValue(); 426 } 427 } 428 } 429 }); 430 return slot_number; 431} 432 433bool Verifier::VerifyMethodIdInLiteralArray(const uint32_t &id) 434{ 435 const auto method_id = panda_file::File::EntityId(id).GetOffset(); 436 auto iter = std::find(all_method_ids_.begin(), all_method_ids_.end(), method_id); 437 if (iter == all_method_ids_.end()) { 438 LOG(ERROR, VERIFIER) << "Invalid method id(0x" << id << ") in literal array"; 439 return false; 440 } 441 return true; 442} 443 444bool Verifier::VerifyStringIdInLiteralArray(const uint32_t &id) 445{ 446 auto string_data = file_->GetStringData(panda_file::File::EntityId(id)); 447 if (string_data.data == nullptr) { 448 LOG(ERROR, VERIFIER) << "Invalid string_id. string_id(0x" << std::hex << id << ")!"; 449 return false; 450 } 451 auto desc = std::string(utf::Mutf8AsCString(string_data.data)); 452 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; 453 std::wstring utf16_desc = converter.from_bytes(desc); 454 if (string_data.utf16_length != utf16_desc.length()) { 455 LOG(ERROR, VERIFIER) << "Invalid string value(0x" << id << ") in literal array"; 456 return false; 457 } 458 return true; 459} 460 461bool Verifier::VerifyLiteralIdInLiteralArray(const uint32_t &id) 462{ 463 auto iter = std::find(literal_ids_.begin(), literal_ids_.end(), id); 464 if (iter == literal_ids_.end()) { 465 LOG(ERROR, VERIFIER) << "Invalid literal id(0x" << id << ") in literal array"; 466 return false; 467 } 468 return true; 469} 470 471bool Verifier::VerifySingleLiteralArray(const panda_file::File::EntityId &literal_id) 472{ 473 auto sp = file_->GetSpanFromId(literal_id); 474 const auto literal_vals_num = panda_file::helpers::Read<sizeof(uint32_t)>(&sp); 475 for (size_t i = 0; i < literal_vals_num; i += 2U) { // 2u skip literal item 476 const auto tag = static_cast<panda_file::LiteralTag>(panda_file::helpers::Read<panda_file::TAG_SIZE>(&sp)); 477 switch (tag) { 478 case panda_file::LiteralTag::TAGVALUE: 479 case panda_file::LiteralTag::BOOL: 480 case panda_file::LiteralTag::ACCESSOR: 481 case panda_file::LiteralTag::NULLVALUE: 482 case panda_file::LiteralTag::BUILTINTYPEINDEX: { 483 sp = sp.SubSpan(sizeof(uint8_t)); // run next sp 484 break; 485 } 486 case panda_file::LiteralTag::METHODAFFILIATE: { 487 sp = sp.SubSpan(sizeof(uint16_t)); 488 break; 489 } 490 case panda_file::LiteralTag::INTEGER: 491 case panda_file::LiteralTag::FLOAT: 492 case panda_file::LiteralTag::GETTER: 493 case panda_file::LiteralTag::SETTER: 494 case panda_file::LiteralTag::GENERATORMETHOD: 495 case panda_file::LiteralTag::LITERALBUFFERINDEX: 496 case panda_file::LiteralTag::ASYNCGENERATORMETHOD: { 497 sp = sp.SubSpan(sizeof(uint32_t)); 498 break; 499 } 500 case panda_file::LiteralTag::DOUBLE: { 501 const auto value = bit_cast<double>(panda_file::helpers::Read<sizeof(uint64_t)>(&sp)); 502 // true: High 16-bit of double value >= 0xffff 503 if (IsImpureNaN(value)) { 504 LOG(ERROR, VERIFIER) << "Fail to verify double value " << value << " in literal array"; 505 return false; 506 } 507 break; 508 } 509 case panda_file::LiteralTag::ARRAY_U1: 510 case panda_file::LiteralTag::ARRAY_U8: 511 case panda_file::LiteralTag::ARRAY_I8: 512 case panda_file::LiteralTag::ARRAY_U16: 513 case panda_file::LiteralTag::ARRAY_I16: 514 case panda_file::LiteralTag::ARRAY_U32: 515 case panda_file::LiteralTag::ARRAY_I32: 516 case panda_file::LiteralTag::ARRAY_U64: 517 case panda_file::LiteralTag::ARRAY_I64: 518 case panda_file::LiteralTag::ARRAY_F32: 519 case panda_file::LiteralTag::ARRAY_F64: 520 case panda_file::LiteralTag::ARRAY_STRING: { 521 i = literal_vals_num; 522 break; 523 } 524 case panda_file::LiteralTag::STRING: { 525 panda_file::helpers::Read<sizeof(uint32_t)>(&sp); 526 break; 527 } 528 case panda_file::LiteralTag::METHOD: { 529 const auto value = static_cast<uint32_t>(panda_file::helpers::Read<sizeof(uint32_t)>(&sp)); 530 inner_method_map_.emplace(literal_id.GetOffset(), value); 531 if (!VerifyMethodIdInLiteralArray(value)) { 532 return false; 533 } 534 break; 535 } 536 case panda_file::LiteralTag::LITERALARRAY: { 537 const auto value = static_cast<uint32_t>(panda_file::helpers::Read<sizeof(uint32_t)>(&sp)); 538 inner_literal_map_.emplace(literal_id.GetOffset(), value); 539 if (!VerifyLiteralIdInLiteralArray(value)) { 540 return false; 541 } 542 break; 543 } 544 default: { 545 LOG(ERROR, VERIFIER) << "Invalid literal tag"; 546 return false; 547 } 548 } 549 } 550 return true; 551} 552 553bool Verifier::IsModuleLiteralId(const panda_file::File::EntityId &id) const 554{ 555 return module_literals_.find(id.GetOffset()) != module_literals_.end(); 556} 557 558bool Verifier::VerifyLiteralArrays() 559{ 560 for (const auto &arg_literal_id : literal_ids_) { 561 const auto literal_id = panda_file::File::EntityId(arg_literal_id); 562 if (!IsModuleLiteralId(literal_id) && !VerifySingleLiteralArray(literal_id)) { 563 return false; 564 } 565 } 566 return true; 567} 568 569bool Verifier::PrecomputeInstructionIndices(const BytecodeInstruction &bc_ins_start, 570 const BytecodeInstruction &bc_ins_last) 571{ 572 instruction_index_map_.clear(); 573 size_t index = 0; 574 auto current_ins = bc_ins_start; 575 instruction_index_map_[current_ins.GetAddress()] = index; 576 577 while (current_ins.GetAddress() < bc_ins_last.GetAddress()) { 578 //Must keep IsPrimaryOpcodeValid is the first check item 579 if (!current_ins.IsPrimaryOpcodeValid()) { 580 LOG(ERROR, VERIFIER) << "Fail to verify primary opcode!"; 581 return false; 582 } 583 current_ins = current_ins.GetNext(); 584 index++; 585 instruction_index_map_[current_ins.GetAddress()] = index; 586 } 587 return true; 588} 589 590bool Verifier::IsMethodBytecodeInstruction(const BytecodeInstruction &bc_ins_cur) 591{ 592 if (instruction_index_map_.find(bc_ins_cur.GetAddress()) != instruction_index_map_.end()) { 593 return true; 594 } 595 return false; 596} 597 598bool Verifier::VerifyJumpInstruction(const BytecodeInstruction &bc_ins, const BytecodeInstruction &bc_ins_last, 599 const BytecodeInstruction &bc_ins_first, const uint8_t *ins_arr, 600 panda_file::File::EntityId code_id) 601{ 602 // update maximum forward offset 603 const auto bc_ins_forward_size = bc_ins_last.GetAddress() - bc_ins.GetAddress(); 604 // update maximum backward offset 605 const auto bc_ins_backward_size = bc_ins.GetAddress() - bc_ins_first.GetAddress(); 606 607 if (bc_ins.IsJumpInstruction()) { 608 std::optional<int64_t> immdata = GetFirstImmFromInstruction(bc_ins); 609 if (!immdata.has_value()) { 610 LOG(ERROR, VERIFIER) << "Fail to get immediate data!"; 611 return false; 612 } 613 if ((immdata.value() > 0) && (immdata.value() >= bc_ins_forward_size)) { 614 LOG(ERROR, VERIFIER) << "Jump forward out of boundary"; 615 return false; 616 } 617 if ((immdata.value() < 0) && (bc_ins_backward_size + immdata.value() < 0)) { 618 LOG(ERROR, VERIFIER) << "Jump backward out of boundary"; 619 return false; 620 } 621 622 const auto bc_ins_dest = bc_ins.JumpTo(immdata.value()); 623 if (!bc_ins_dest.IsPrimaryOpcodeValid()) { 624 LOG(ERROR, VERIFIER) << "Fail to verify target jump primary opcode!"; 625 return false; 626 } 627 if (!IsMethodBytecodeInstruction(bc_ins_dest)) { 628 LOG(ERROR, VERIFIER) << "> error encountered at " << code_id << " (0x" << std::hex << code_id 629 << "). incorrect instruction at offset: 0x" << (bc_ins.GetAddress() - ins_arr) 630 << ": invalid jump offset 0x" << immdata.value() 631 << " - jumping in the middle of another instruction!"; 632 return false; 633 } 634 } 635 636 return true; 637} 638 639bool Verifier::GetIcSlotFromInstruction(const BytecodeInstruction &bc_ins, uint32_t &first_slot_index, 640 bool &has_slot, bool &is_two_slot) 641{ 642 std::optional<uint64_t> first_imm = {}; 643 if (bc_ins.HasFlag(BytecodeInstruction::Flags::ONE_SLOT)) { 644 first_imm = GetFirstImmFromInstruction(bc_ins); 645 if (!first_imm.has_value()) { 646 LOG(ERROR, VERIFIER) << "Fail to get first immediate data!"; 647 return false; 648 } 649 first_slot_index = first_imm.value(); 650 is_two_slot = false; 651 has_slot = true; 652 } else if (bc_ins.HasFlag(BytecodeInstruction::Flags::TWO_SLOT)) { 653 first_imm = GetFirstImmFromInstruction(bc_ins); 654 if (!first_imm.has_value()) { 655 LOG(ERROR, VERIFIER) << "Fail to get first immediate data!"; 656 return false; 657 } 658 first_slot_index = first_imm.value(); 659 has_slot = true; 660 is_two_slot = true; 661 } 662 663 return true; 664} 665 666bool Verifier::VerifyCatchBlocks(panda_file::CodeDataAccessor::TryBlock &try_block, const BytecodeInstruction &bc_ins, 667 const BytecodeInstruction &bc_ins_last) 668{ 669 bool result = true; 670 671 try_block.EnumerateCatchBlocks([&](panda_file::CodeDataAccessor::CatchBlock &catch_block) { 672 const auto handler_begin_offset = catch_block.GetHandlerPc(); 673 // GetCodeSize() returns a unsigned long value, which is always >= 0, 674 // so handler_end_offset is guaranteed to be >= handler_begin_offset 675 const auto handler_end_offset = handler_begin_offset + catch_block.GetCodeSize(); 676 677 const auto handler_begin_bc_ins = bc_ins.JumpTo(handler_begin_offset); 678 const auto handler_end_bc_ins = bc_ins.JumpTo(handler_end_offset); 679 680 const bool handler_begin_offset_in_range = bc_ins_last.GetAddress() > handler_begin_bc_ins.GetAddress(); 681 const bool handler_end_offset_in_range = bc_ins_last.GetAddress() >= handler_end_bc_ins.GetAddress(); 682 683 if (!handler_begin_offset_in_range) { 684 LOG(ERROR, VERIFIER) << "> Invalid catch block begin offset range! address is: 0x" << std::hex 685 << handler_begin_bc_ins.GetAddress(); 686 result = false; 687 return false; 688 } 689 if (!IsMethodBytecodeInstruction(handler_begin_bc_ins)) { 690 LOG(ERROR, VERIFIER) << "> Invalid catch block begin offset validity! address is: 0x" << std::hex 691 << handler_begin_bc_ins.GetAddress(); 692 result = false; 693 return false; 694 } 695 if (!handler_end_offset_in_range) { 696 LOG(ERROR, VERIFIER) << "> Invalid catch block end offset range! address is: 0x" << std::hex 697 << handler_end_bc_ins.GetAddress(); 698 result = false; 699 return false; 700 } 701 if (!IsMethodBytecodeInstruction(handler_end_bc_ins)) { 702 LOG(ERROR, VERIFIER) << "> Invalid catch block end offset validity! address is: 0x" << std::hex 703 << handler_end_bc_ins.GetAddress(); 704 result = false; 705 return false; 706 } 707 708 return true; 709 }); 710 711 return result; 712} 713 714bool Verifier::VerifyTryBlocks(panda_file::CodeDataAccessor &code_accessor, const BytecodeInstruction &bc_ins, 715 const BytecodeInstruction &bc_ins_last) 716{ 717 bool result = true; 718 719 code_accessor.EnumerateTryBlocks([&](panda_file::CodeDataAccessor::TryBlock &try_block) { 720 const auto try_begin_bc_ins = bc_ins.JumpTo(try_block.GetStartPc()); 721 // GetLength() returns a uint32 value, which is always >= 0, 722 // so try_end_bc_ins is guaranteed to be >= try_begin_bc_ins 723 const auto try_end_bc_ins = bc_ins.JumpTo(try_block.GetStartPc() + try_block.GetLength()); 724 725 const bool try_begin_offset_in_range = bc_ins_last.GetAddress() > try_begin_bc_ins.GetAddress(); 726 const bool try_end_offset_in_range = bc_ins_last.GetAddress() >= try_end_bc_ins.GetAddress(); 727 728 if (!try_begin_offset_in_range) { 729 LOG(ERROR, VERIFIER) << "> Invalid try block begin offset range! address is: 0x" << std::hex 730 << try_begin_bc_ins.GetAddress(); 731 result = false; 732 return false; 733 } 734 if (!IsMethodBytecodeInstruction(try_begin_bc_ins)) { 735 LOG(ERROR, VERIFIER) << "> Invalid try block begin offset validity! address is: 0x" << std::hex 736 << try_begin_bc_ins.GetAddress(); 737 result = false; 738 return false; 739 } 740 if (!try_end_offset_in_range) { 741 LOG(ERROR, VERIFIER) << "> Invalid try block end offset range! address is: 0x" << std::hex 742 << try_end_bc_ins.GetAddress(); 743 result = false; 744 return false; 745 } 746 if (!IsMethodBytecodeInstruction(try_end_bc_ins)) { 747 LOG(ERROR, VERIFIER) << "> Invalid try block end offset validity! address is: 0x" << std::hex 748 << try_end_bc_ins.GetAddress(); 749 result = false; 750 return false; 751 } 752 if (!VerifyCatchBlocks(try_block, bc_ins, bc_ins_last)) { 753 LOG(ERROR, VERIFIER) << "Catch block validation failed!"; 754 result = false; 755 return false; 756 } 757 758 return true; 759 }); 760 761 return result; 762} 763 764 765bool Verifier::VerifySlotNumber(panda_file::MethodDataAccessor &method_accessor, const uint32_t &slot_number, 766 const panda_file::File::EntityId &method_id) 767{ 768 const auto ann_slot_number = GetSlotNumberFromAnnotation(method_accessor); 769 if (!ann_slot_number.has_value()) { 770 LOG(INFO, VERIFIER) << "There is no slot number information in annotaion."; 771 // To be compatible with old abc, slot number verification is not continued 772 return true; 773 } 774 if (slot_number == ann_slot_number.value()) { 775 return true; 776 } 777 778 LOG(ERROR, VERIFIER) << "Slot number has been falsified in method 0x" << method_id; 779 return false; 780} 781 782bool Verifier::VerifyMethodRegisterIndex(panda_file::CodeDataAccessor &code_accessor, 783 std::optional<uint64_t> &valid_regs_num) 784{ 785 const uint64_t reg_nums = code_accessor.GetNumVregs(); 786 const uint64_t arg_nums = code_accessor.GetNumArgs(); 787 valid_regs_num = SafeAdd(reg_nums, arg_nums); 788 if (!valid_regs_num.has_value()) { 789 LOG(ERROR, VERIFIER) << "Integer overflow detected during register index calculation!"; 790 return false; 791 } 792 if (valid_regs_num.value() > MAX_REGISTER_INDEX + 1) { 793 LOG(ERROR, VERIFIER) << "Register index exceeds the maximum allowable value (0xffff)!"; 794 return false; 795 } 796 return true; 797} 798 799bool Verifier::VerifyMethodInstructions(const MethodInfos &infos) 800{ 801 auto current_ins = infos.bc_ins; 802 auto last_ins = infos.bc_ins_last; 803 auto code_id = infos.method_accessor.GetCodeId().value(); 804 auto method_id = infos.method_id; 805 auto valid_regs_num = infos.valid_regs_num.value(); 806 auto ins_slot_num = infos.ins_slot_num; 807 auto has_slot = infos.has_slot; 808 auto is_two_slot = infos.is_two_slot; 809 810 while (current_ins.GetAddress() != last_ins.GetAddress()) { 811 if (current_ins.GetAddress() > last_ins.GetAddress()) { 812 LOG(ERROR, VERIFIER) << "> error encountered at " << code_id 813 << " (0x" << std::hex << code_id 814 << "). bytecode instructions sequence corrupted for method " 815 << method_id 816 << "! went out of bounds"; 817 return false; 818 } 819 if (!current_ins.IsJumpInstruction() && !current_ins.IsReturnOrThrowInstruction() 820 && current_ins.GetNext().GetAddress() == last_ins.GetAddress()) { 821 LOG(ERROR, VERIFIER) << "> error encountered at " << code_id 822 << " (0x" << std::hex << code_id 823 << "). bytecode instructions sequence corrupted for method " 824 << method_id 825 << "! went out of bounds"; 826 return false; 827 } 828 const size_t count = GetVRegCount(current_ins); 829 if (count != 0 && !CheckVRegIdx(current_ins, count, valid_regs_num)) { 830 return false; 831 } 832 if (!VerifyJumpInstruction(current_ins, last_ins, 833 infos.bc_ins_init, infos.ins_arr, 834 code_id)) { 835 LOG(ERROR, VERIFIER) << "Invalid target position of jump instruction"; 836 return false; 837 } 838 if (!GetIcSlotFromInstruction(current_ins, ins_slot_num, 839 has_slot, is_two_slot)) { 840 LOG(ERROR, VERIFIER) << "Fail to get first slot index!"; 841 return false; 842 } 843 current_ins = current_ins.GetNext(); 844 } 845 return true; 846} 847 848bool Verifier::CheckConstantPoolMethodContent(const panda_file::File::EntityId &method_id) 849{ 850 panda_file::MethodDataAccessor method_accessor(*file_, method_id); 851 if (!method_accessor.GetCodeId().has_value()) { 852 LOG(ERROR, VERIFIER) << "Fail to get code id!"; 853 return false; 854 } 855 panda_file::CodeDataAccessor code_accessor(*file_, method_accessor.GetCodeId().value()); 856 const auto ins_size = code_accessor.GetCodeSize(); 857 const auto ins_arr = code_accessor.GetInstructions(); 858 auto bc_ins = BytecodeInstruction(ins_arr); 859 const auto bc_ins_last = bc_ins.JumpTo(ins_size); 860 const auto bc_ins_init = bc_ins; // initial PC value 861 uint32_t ins_slot_num = 0; // For ic slot index verification 862 bool has_slot = false; 863 bool is_two_slot = false; 864 std::optional<uint64_t> valid_regs_num = 0; 865 MethodInfos infos = {bc_ins_init, bc_ins, bc_ins_last, method_accessor, method_id, 866 valid_regs_num, ins_arr, ins_slot_num, has_slot, is_two_slot}; 867 if (ins_size <= 0) { 868 LOG(ERROR, VERIFIER) << "Fail to verify code size!"; 869 return false; 870 } 871 if (!VerifyMethodRegisterIndex(code_accessor, valid_regs_num)) { 872 LOG(ERROR, VERIFIER) << "Fail to verify method register index!"; 873 return false; 874 } 875 if (!PrecomputeInstructionIndices(bc_ins, bc_ins_last)) { 876 LOG(ERROR, VERIFIER) << "Fail to precompute instruction indices!"; 877 return false; 878 } 879 if (!IsMethodBytecodeInstruction(bc_ins)) { 880 LOG(ERROR, VERIFIER) << "Fail to verify method first bytecode instruction!"; 881 } 882 if (!VerifyTryBlocks(code_accessor, bc_ins, bc_ins_last)) { 883 LOG(ERROR, VERIFIER) << "Fail to verify try blocks or catch blocks!"; 884 return false; 885 } 886 if (!VerifyMethodInstructions(infos)) { 887 LOG(ERROR, VERIFIER) << "Fail to verify method instructions!"; 888 return false; 889 } 890 if (has_slot) { 891 if (is_two_slot) { 892 ins_slot_num += 1; // when there are two slots for the last instruction, the slot index increases 893 } 894 ins_slot_num += 1; // slot index starts with zero 895 } 896 return true; 897} 898 899bool Verifier::CheckConstantPoolIndex() const 900{ 901 for (auto &id : ins_method_ids_) { 902 if (!VerifyMethodId(id)) { 903 return false; 904 } 905 } 906 907 for (auto &id : ins_literal_ids_) { 908 if (!VerifyLiteralId(id)) { 909 return false; 910 } 911 } 912 913 for (auto &id : ins_string_ids_) { 914 if (!VerifyStringId(id)) { 915 return false; 916 } 917 } 918 919 return true; 920} 921 922std::optional<uint64_t> Verifier::SafeAdd(uint64_t a, uint64_t b) const 923{ 924 if (a > std::numeric_limits<uint64_t>::max() - b) { 925 return std::nullopt; 926 } 927 return a + b; 928} 929} // namespace panda::verifier 930