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 "zlib.h"
20 #include "class_data_accessor-inl.h"
21
22 namespace panda::verifier {
23
Verifier(const std::string & filename)24 Verifier::Verifier(const std::string &filename)
25 {
26 auto file_to_verify = panda_file::File::Open(filename);
27 file_.swap(file_to_verify);
28 }
29
Verify()30 bool Verifier::Verify()
31 {
32 CollectIdInfos();
33
34 if (!VerifyChecksum()) {
35 return false;
36 }
37
38 if (!VerifyConstantPool()) {
39 return false;
40 }
41
42 if (!VerifyRegisterIndex()) {
43 return false;
44 }
45
46 return true;
47 }
48
CollectIdInfos()49 void Verifier::CollectIdInfos()
50 {
51 if (file_ == nullptr) {
52 return;
53 }
54 GetConstantPoolIds();
55 GetLiteralIds();
56 CheckConstantPool(verifier::ActionType::COLLECTINFOS);
57 }
58
VerifyChecksum()59 bool Verifier::VerifyChecksum()
60 {
61 if (file_ == nullptr) {
62 return false;
63 }
64 uint32_t file_size = file_->GetHeader()->file_size;
65 ASSERT(file_size > FILE_CONTENT_OFFSET);
66 uint32_t cal_checksum = adler32(1, file_->GetBase() + FILE_CONTENT_OFFSET, file_size - FILE_CONTENT_OFFSET);
67 return file_->GetHeader()->checksum == cal_checksum;
68 }
69
VerifyConstantPool()70 bool Verifier::VerifyConstantPool()
71 {
72 if (file_ == nullptr) {
73 return false;
74 }
75
76 if (!CheckConstantPoolIndex()) {
77 return false;
78 }
79
80 if (!CheckConstantPool(verifier::ActionType::CHECKCONSTPOOLCONTENT)) {
81 return false;
82 }
83
84 if (!VerifyLiteralArrays()) {
85 return false;
86 }
87
88 return true;
89 }
90
VerifyRegisterIndex()91 bool Verifier::VerifyRegisterIndex()
92 {
93 if (file_ == nullptr) {
94 return false;
95 }
96
97 for (const auto id : all_method_ids_) {
98 const panda_file::File::EntityId method_id = panda_file::File::EntityId(id);
99 panda_file::MethodDataAccessor method_accessor {*file_, method_id};
100 if (!method_accessor.GetCodeId().has_value()) {
101 continue;
102 }
103 panda_file::CodeDataAccessor code_data(*file_, method_accessor.GetCodeId().value());
104 const uint32_t reg_nums = code_data.GetNumVregs();
105 const uint32_t arg_nums = code_data.GetNumArgs();
106 const uint32_t max_reg_idx = reg_nums + arg_nums;
107 auto bc_ins = BytecodeInstruction(code_data.GetInstructions());
108 const auto bc_ins_last = bc_ins.JumpTo(code_data.GetCodeSize());
109 ASSERT(arg_nums >= DEFAULT_ARGUMENT_NUMBER);
110 while (bc_ins.GetAddress() < bc_ins_last.GetAddress()) {
111 const size_t count = GetVRegCount(bc_ins);
112 if (count == 0) { // Skip instructions that do not use registers
113 bc_ins = bc_ins.GetNext();
114 continue;
115 }
116 if (!CheckVRegIdx(bc_ins, count, max_reg_idx)) {
117 return false;
118 }
119 bc_ins = bc_ins.GetNext();
120 }
121 }
122 return true;
123 }
124
VerifyConstantPoolIndex()125 bool Verifier::VerifyConstantPoolIndex()
126 {
127 if (file_ == nullptr) {
128 return false;
129 }
130
131 if (!CheckConstantPoolIndex()) {
132 return false;
133 }
134
135 return true;
136 }
137
VerifyConstantPoolContent()138 bool Verifier::VerifyConstantPoolContent()
139 {
140 if (file_ == nullptr) {
141 return false;
142 }
143
144 if (!CheckConstantPool(verifier::ActionType::CHECKCONSTPOOLCONTENT)) {
145 return false;
146 }
147
148 if (!VerifyLiteralArrays()) {
149 return false;
150 }
151
152 return true;
153 }
154
GetConstantPoolIds()155 void Verifier::GetConstantPoolIds()
156 {
157 if (constant_pool_ids_.size() != 0) {
158 return;
159 }
160 auto index_headers = file_->GetIndexHeaders();
161 for (const auto &index_header : index_headers) {
162 auto region_indexs = file_->GetMethodIndex(&index_header);
163 for (auto &index : region_indexs) {
164 constant_pool_ids_.push_back(index.GetOffset());
165 }
166 }
167 }
168
GetLiteralIds()169 void Verifier::GetLiteralIds()
170 {
171 if (literal_ids_.size() != 0) {
172 return;
173 }
174 const auto literal_arrays = file_->GetLiteralArrays();
175 for (const auto literal_id : literal_arrays) {
176 literal_ids_.push_back(literal_id);
177 }
178 }
179
CheckConstantPoolActions(const verifier::ActionType type,panda_file::File::EntityId method_id)180 bool Verifier::CheckConstantPoolActions(const verifier::ActionType type, panda_file::File::EntityId method_id)
181 {
182 switch (type) {
183 case verifier::ActionType::CHECKCONSTPOOLCONTENT: {
184 return CheckConstantPoolMethodContent(method_id);
185 }
186 case verifier::ActionType::COLLECTINFOS: {
187 all_method_ids_.push_back(method_id.GetOffset());
188 return CollectIdInInstructions(method_id);
189 }
190 default: {
191 return true;
192 }
193 }
194 }
195
CollectIdInInstructions(const panda_file::File::EntityId & method_id)196 bool Verifier::CollectIdInInstructions(const panda_file::File::EntityId &method_id)
197 {
198 panda_file::MethodDataAccessor method_accessor(*file_, method_id);
199 ASSERT(method_accessor.GetCodeId().has_value());
200 panda_file::CodeDataAccessor code_accessor(*file_, method_accessor.GetCodeId().value());
201 const auto ins_size = code_accessor.GetCodeSize();
202 const auto ins_arr = code_accessor.GetInstructions();
203
204 auto bc_ins = BytecodeInstruction(ins_arr);
205 const auto bc_ins_last = bc_ins.JumpTo(ins_size);
206
207 while (bc_ins.GetAddress() < bc_ins_last.GetAddress()) {
208 if (!bc_ins.IsPrimaryOpcodeValid()) {
209 LOG(ERROR, VERIFIER) << "Fail to verify primary opcode!";
210 return false;
211 }
212 if (bc_ins.HasFlag(BytecodeInstruction::Flags::LITERALARRAY_ID)) {
213 // the idx of any instruction with a literal id is 0 except defineclasswithbuffer
214 size_t idx = 0;
215 if (bc_ins.GetOpcode() == BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8 ||
216 bc_ins.GetOpcode() == BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8) {
217 idx = 1;
218 }
219 const auto arg_literal_idx = bc_ins.GetId(idx).AsIndex();
220 const auto literal_id = file_->ResolveMethodIndex(method_id, arg_literal_idx);
221 ins_literal_ids_.insert(literal_id.GetOffset());
222 }
223 if (bc_ins.HasFlag(BytecodeInstruction::Flags::METHOD_ID)) {
224 const auto arg_method_idx = bc_ins.GetId().AsIndex();
225 const auto arg_method_id = file_->ResolveMethodIndex(method_id, arg_method_idx);
226 ins_method_ids_.insert(arg_method_id.GetOffset());
227 }
228 if (bc_ins.HasFlag(BytecodeInstruction::Flags::STRING_ID)) {
229 const auto arg_string_idx = bc_ins.GetId().AsIndex();
230 const auto string_id = file_->ResolveOffsetByIndex(method_id, arg_string_idx);
231 ins_string_ids_.insert(string_id.GetOffset());
232 }
233 bc_ins = bc_ins.GetNext();
234 }
235 return true;
236 }
237
CollectModuleLiteralId(const panda_file::File::EntityId & field_id)238 void Verifier::CollectModuleLiteralId(const panda_file::File::EntityId &field_id)
239 {
240 panda_file::FieldDataAccessor field_accessor(*file_, field_id);
241 const auto literal_id = field_accessor.GetValue<uint32_t>().value();
242 if (std::find(literal_ids_.begin(), literal_ids_.end(), literal_id) != literal_ids_.end()) {
243 module_literals_.insert(literal_id);
244 }
245 }
246
CheckConstantPool(const verifier::ActionType type)247 bool Verifier::CheckConstantPool(const verifier::ActionType type)
248 {
249 const auto class_idx = file_->GetClasses();
250 for (size_t i = 0; i < class_idx.size(); i++) {
251 uint32_t class_id = class_idx[i];
252 if (class_id > file_->GetHeader()->file_size) {
253 LOG(ERROR, VERIFIER) << "Binary file corrupted. out of bounds (0x" << std::hex
254 << file_->GetHeader()->file_size;
255 return false;
256 }
257 const panda_file::File::EntityId record_id {class_id};
258 if (!file_->IsExternal(record_id)) {
259 panda_file::ClassDataAccessor class_accessor {*file_, record_id};
260 bool check_res = true;
261 class_accessor.EnumerateMethods([&](panda_file::MethodDataAccessor &method_accessor) -> void {
262 check_res = check_res && CheckConstantPoolActions(type, method_accessor.GetMethodId());
263 });
264 if (!check_res) {
265 return false;
266 }
267 if (type == verifier::ActionType::COLLECTINFOS) {
268 class_accessor.EnumerateFields([&](panda_file::FieldDataAccessor &field_accessor) -> void {
269 CollectModuleLiteralId(field_accessor.GetFieldId());
270 });
271 }
272 }
273 }
274
275 return true;
276 }
277
GetVRegCount(const BytecodeInstruction & bc_ins)278 size_t Verifier::GetVRegCount(const BytecodeInstruction &bc_ins)
279 {
280 size_t idx = 0; // Represents the idxTH register index in an instruction
281 BytecodeInstruction::Format format = bc_ins.GetFormat();
282 while (bc_ins.HasVReg(format, idx)) {
283 idx++;
284 }
285 return idx;
286 }
287
CheckVRegIdx(const BytecodeInstruction & bc_ins,const size_t count,const uint32_t max_reg_idx)288 bool Verifier::CheckVRegIdx(const BytecodeInstruction &bc_ins, const size_t count, const uint32_t max_reg_idx)
289 {
290 for (size_t idx = 0; idx < count; idx++) { // Represents the idxTH register index in an instruction
291 uint16_t reg_idx = bc_ins.GetVReg(idx);
292 if (reg_idx >= max_reg_idx) {
293 LOG(ERROR, VERIFIER) << "register index out of bounds. register index is (0x" << std::hex
294 << reg_idx << ")" << std::endl;
295 return false;
296 }
297 }
298 return true;
299 }
300
VerifyMethodId(const uint32_t & method_id) const301 bool Verifier::VerifyMethodId(const uint32_t &method_id) const
302 {
303 auto iter = std::find(constant_pool_ids_.begin(), constant_pool_ids_.end(), method_id);
304 if (iter == constant_pool_ids_.end() ||
305 (std::find(literal_ids_.begin(), literal_ids_.end(), method_id) != literal_ids_.end()) ||
306 ins_string_ids_.count(method_id)) {
307 LOG(ERROR, VERIFIER) << "Fail to verify method id. method_id(0x" << std::hex << method_id << ")!";
308 return false;
309 }
310 return true;
311 }
312
VerifyLiteralId(const uint32_t & literal_id) const313 bool Verifier::VerifyLiteralId(const uint32_t &literal_id) const
314 {
315 auto iter = std::find(literal_ids_.begin(), literal_ids_.end(), literal_id);
316 if (iter == literal_ids_.end()) {
317 LOG(ERROR, VERIFIER) << "Fail to verify literal id. literal_id(0x" << std::hex << literal_id << ")!";
318 return false;
319 }
320 return true;
321 }
322
VerifyStringId(const uint32_t & string_id) const323 bool Verifier::VerifyStringId(const uint32_t &string_id) const
324 {
325 auto iter = std::find(constant_pool_ids_.begin(), constant_pool_ids_.end(), string_id);
326 if (iter == constant_pool_ids_.end() ||
327 ins_method_ids_.count(string_id) ||
328 (std::find(literal_ids_.begin(), literal_ids_.end(), string_id) != literal_ids_.end())) {
329 LOG(ERROR, VERIFIER) << "Fail to verify string id. string_id(0x" << std::hex << string_id << ")!";
330 return false;
331 }
332 return true;
333 }
334
GetFirstImmFromInstruction(const BytecodeInstruction & bc_ins)335 std::optional<int64_t> Verifier::GetFirstImmFromInstruction(const BytecodeInstruction &bc_ins)
336 {
337 std::optional<int64_t> first_imm = std::optional<int64_t> {};
338 size_t index = 0;
339 const auto format = bc_ins.GetFormat();
340 if (bc_ins.HasImm(format, index)) {
341 first_imm = bc_ins.GetImm64(index);
342 }
343
344 return first_imm;
345 }
346
GetSlotNumberFromAnnotation(panda_file::MethodDataAccessor & method_accessor)347 std::optional<uint64_t> Verifier::GetSlotNumberFromAnnotation(panda_file::MethodDataAccessor &method_accessor)
348 {
349 std::optional<uint64_t> slot_number {};
350 method_accessor.EnumerateAnnotations([&](panda_file::File::EntityId annotation_id) {
351 panda_file::AnnotationDataAccessor ada(*file_, annotation_id);
352 auto *annotation_name = reinterpret_cast<const char *>(file_->GetStringData(ada.GetClassId()).data);
353 if (::strcmp("L_ESSlotNumberAnnotation;", annotation_name) == 0) {
354 uint32_t elem_count = ada.GetCount();
355 for (uint32_t i = 0; i < elem_count; i++) {
356 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i);
357 auto *elem_name = reinterpret_cast<const char *>(file_->GetStringData(adae.GetNameId()).data);
358 if (::strcmp("SlotNumber", elem_name) == 0) {
359 slot_number = adae.GetScalarValue().GetValue();
360 }
361 }
362 }
363 });
364 return slot_number;
365 }
366
VerifyMethodIdInLiteralArray(const uint32_t & id)367 bool Verifier::VerifyMethodIdInLiteralArray(const uint32_t &id)
368 {
369 const auto method_id = panda_file::File::EntityId(id).GetOffset();
370 auto iter = std::find(all_method_ids_.begin(), all_method_ids_.end(), method_id);
371 if (iter == all_method_ids_.end()) {
372 LOG(ERROR, VERIFIER) << "Invalid method id(0x" << id << ") in literal array";
373 return false;
374 }
375 return true;
376 }
377
VerifyStringIdInLiteralArray(const uint32_t & id)378 bool Verifier::VerifyStringIdInLiteralArray(const uint32_t &id)
379 {
380 auto string_data = file_->GetStringData(panda_file::File::EntityId(id));
381 if (string_data.data == nullptr) {
382 LOG(ERROR, VERIFIER) << "Invalid string_id. string_id(0x" << std::hex << id << ")!";
383 return false;
384 }
385 auto desc = std::string(utf::Mutf8AsCString(string_data.data));
386 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
387 std::wstring utf16_desc = converter.from_bytes(desc);
388 if (string_data.utf16_length != utf16_desc.length()) {
389 LOG(ERROR, VERIFIER) << "Invalid string value(0x" << id << ") in literal array";
390 return false;
391 }
392 return true;
393 }
394
VerifyLiteralIdInLiteralArray(const uint32_t & id)395 bool Verifier::VerifyLiteralIdInLiteralArray(const uint32_t &id)
396 {
397 auto iter = std::find(literal_ids_.begin(), literal_ids_.end(), id);
398 if (iter == literal_ids_.end()) {
399 LOG(ERROR, VERIFIER) << "Invalid literal id(0x" << id << ") in literal array";
400 return false;
401 }
402 return true;
403 }
404
VerifySingleLiteralArray(const panda_file::File::EntityId & literal_id)405 bool Verifier::VerifySingleLiteralArray(const panda_file::File::EntityId &literal_id)
406 {
407 auto sp = file_->GetSpanFromId(literal_id);
408 const auto literal_vals_num = panda_file::helpers::Read<sizeof(uint32_t)>(&sp);
409 for (size_t i = 0; i < literal_vals_num; i += 2U) { // 2u skip literal item
410 const auto tag = static_cast<panda_file::LiteralTag>(panda_file::helpers::Read<panda_file::TAG_SIZE>(&sp));
411 switch (tag) {
412 case panda_file::LiteralTag::TAGVALUE:
413 case panda_file::LiteralTag::BOOL:
414 case panda_file::LiteralTag::ACCESSOR:
415 case panda_file::LiteralTag::NULLVALUE:
416 case panda_file::LiteralTag::BUILTINTYPEINDEX: {
417 sp = sp.SubSpan(sizeof(uint8_t)); // run next sp
418 break;
419 }
420 case panda_file::LiteralTag::METHODAFFILIATE: {
421 sp = sp.SubSpan(sizeof(uint16_t));
422 break;
423 }
424 case panda_file::LiteralTag::INTEGER:
425 case panda_file::LiteralTag::FLOAT:
426 case panda_file::LiteralTag::GETTER:
427 case panda_file::LiteralTag::SETTER:
428 case panda_file::LiteralTag::GENERATORMETHOD:
429 case panda_file::LiteralTag::LITERALBUFFERINDEX:
430 case panda_file::LiteralTag::ASYNCGENERATORMETHOD: {
431 sp = sp.SubSpan(sizeof(uint32_t));
432 break;
433 }
434 case panda_file::LiteralTag::DOUBLE: {
435 sp = sp.SubSpan(sizeof(uint64_t));
436 break;
437 }
438 case panda_file::LiteralTag::ARRAY_U1:
439 case panda_file::LiteralTag::ARRAY_U8:
440 case panda_file::LiteralTag::ARRAY_I8:
441 case panda_file::LiteralTag::ARRAY_U16:
442 case panda_file::LiteralTag::ARRAY_I16:
443 case panda_file::LiteralTag::ARRAY_U32:
444 case panda_file::LiteralTag::ARRAY_I32:
445 case panda_file::LiteralTag::ARRAY_U64:
446 case panda_file::LiteralTag::ARRAY_I64:
447 case panda_file::LiteralTag::ARRAY_F32:
448 case panda_file::LiteralTag::ARRAY_F64:
449 case panda_file::LiteralTag::ARRAY_STRING: {
450 i = literal_vals_num;
451 break;
452 }
453 case panda_file::LiteralTag::STRING: {
454 panda_file::helpers::Read<sizeof(uint32_t)>(&sp);
455 break;
456 }
457 case panda_file::LiteralTag::METHOD: {
458 const auto value = static_cast<uint32_t>(panda_file::helpers::Read<sizeof(uint32_t)>(&sp));
459 inner_method_map_.emplace(literal_id.GetOffset(), value);
460 if (!VerifyMethodIdInLiteralArray(value)) {
461 return false;
462 }
463 break;
464 }
465 case panda_file::LiteralTag::LITERALARRAY: {
466 const auto value = static_cast<uint32_t>(panda_file::helpers::Read<sizeof(uint32_t)>(&sp));
467 inner_literal_map_.emplace(literal_id.GetOffset(), value);
468 if (!VerifyLiteralIdInLiteralArray(value)) {
469 return false;
470 }
471 break;
472 }
473 default: {
474 LOG(ERROR, VERIFIER) << "Invalid literal tag";
475 return false;
476 }
477 }
478 }
479 return true;
480 }
481
IsModuleLiteralId(const panda_file::File::EntityId & id) const482 bool Verifier::IsModuleLiteralId(const panda_file::File::EntityId &id) const
483 {
484 return module_literals_.find(id.GetOffset()) != module_literals_.end();
485 }
486
VerifyLiteralArrays()487 bool Verifier::VerifyLiteralArrays()
488 {
489 for (const auto &arg_literal_id : literal_ids_) {
490 const auto literal_id = panda_file::File::EntityId(arg_literal_id);
491 if (!IsModuleLiteralId(literal_id) && !VerifySingleLiteralArray(literal_id)) {
492 return false;
493 }
494 }
495 return true;
496 }
497
IsJumpInstruction(const Opcode & ins_opcode)498 bool Verifier::IsJumpInstruction(const Opcode &ins_opcode)
499 {
500 bool valid = true;
501
502 switch (ins_opcode) {
503 case Opcode::JMP_IMM8:
504 case Opcode::JMP_IMM16:
505 case Opcode::JEQZ_IMM8:
506 case Opcode::JEQZ_IMM16:
507 case Opcode::JNEZ_IMM8:
508 case Opcode::JSTRICTEQZ_IMM8:
509 case Opcode::JNSTRICTEQZ_IMM8:
510 case Opcode::JEQNULL_IMM8:
511 case Opcode::JNENULL_IMM8:
512 case Opcode::JSTRICTEQNULL_IMM8:
513 case Opcode::JNSTRICTEQNULL_IMM8:
514 case Opcode::JEQUNDEFINED_IMM8:
515 case Opcode::JNEUNDEFINED_IMM8:
516 case Opcode::JSTRICTEQUNDEFINED_IMM8:
517 case Opcode::JNSTRICTEQUNDEFINED_IMM8:
518 case Opcode::JEQ_V8_IMM8:
519 case Opcode::JNE_V8_IMM8:
520 case Opcode::JSTRICTEQ_V8_IMM8:
521 case Opcode::JNSTRICTEQ_V8_IMM8:
522 case Opcode::JMP_IMM32:
523 case Opcode::JEQZ_IMM32:
524 case Opcode::JNEZ_IMM16:
525 case Opcode::JNEZ_IMM32:
526 case Opcode::JSTRICTEQZ_IMM16:
527 case Opcode::JNSTRICTEQZ_IMM16:
528 case Opcode::JEQNULL_IMM16:
529 case Opcode::JNENULL_IMM16:
530 case Opcode::JSTRICTEQNULL_IMM16:
531 case Opcode::JNSTRICTEQNULL_IMM16:
532 case Opcode::JEQUNDEFINED_IMM16:
533 case Opcode::JNEUNDEFINED_IMM16:
534 case Opcode::JSTRICTEQUNDEFINED_IMM16:
535 case Opcode::JEQ_V8_IMM16:
536 case Opcode::JNE_V8_IMM16:
537 case Opcode::JSTRICTEQ_V8_IMM16:
538 case Opcode::JNSTRICTEQ_V8_IMM16: {
539 valid = true;
540 break;
541 }
542 default: {
543 valid = false;
544 break;
545 }
546 }
547 return valid;
548 }
549
VerifyJumpInstruction(const BytecodeInstruction & bc_ins,const BytecodeInstruction & bc_ins_last,const BytecodeInstruction & bc_ins_first)550 bool Verifier::VerifyJumpInstruction(const BytecodeInstruction &bc_ins, const BytecodeInstruction &bc_ins_last,
551 const BytecodeInstruction &bc_ins_first)
552 {
553 // update maximum forward offset
554 const auto bc_ins_forward_size = bc_ins_last.GetAddress() - bc_ins.GetAddress();
555 // update maximum backward offset
556 const auto bc_ins_backward_size = bc_ins.GetAddress() - bc_ins_first.GetAddress();
557 if (!bc_ins.IsPrimaryOpcodeValid()) {
558 LOG(ERROR, VERIFIER) << "Fail to verify primary opcode!";
559 return false;
560 }
561
562 Opcode ins_opcode = bc_ins.GetOpcode();
563 if (IsJumpInstruction(ins_opcode)) {
564 std::optional<int64_t> immdata = GetFirstImmFromInstruction(bc_ins);
565 if (!immdata.has_value()) {
566 LOG(ERROR, VERIFIER) << "Fail to get immediate data!";
567 return false;
568 }
569 if ((immdata.value() > 0) && (immdata.value() >= bc_ins_forward_size)) {
570 LOG(ERROR, VERIFIER) << "Jump forward out of boundary";
571 return false;
572 }
573 if ((immdata.value() < 0) && (bc_ins_backward_size + immdata.value() < 0)) {
574 LOG(ERROR, VERIFIER) << "Jump backward out of boundary";
575 return false;
576 }
577 }
578 return true;
579 }
580
GetIcSlotFromInstruction(const BytecodeInstruction & bc_ins,uint32_t & first_slot_index,bool & has_slot,bool & is_two_slot)581 bool Verifier::GetIcSlotFromInstruction(const BytecodeInstruction &bc_ins, uint32_t &first_slot_index,
582 bool &has_slot, bool &is_two_slot)
583 {
584 std::optional<uint64_t> first_imm = {};
585 if (bc_ins.HasFlag(BytecodeInstruction::Flags::ONE_SLOT)) {
586 first_imm = GetFirstImmFromInstruction(bc_ins);
587 if (!first_imm.has_value()) {
588 LOG(ERROR, VERIFIER) << "Fail to get first immediate data!";
589 return false;
590 }
591 first_slot_index = first_imm.value();
592 is_two_slot = false;
593 has_slot = true;
594 } else if (bc_ins.HasFlag(BytecodeInstruction::Flags::TWO_SLOT)) {
595 first_imm = GetFirstImmFromInstruction(bc_ins);
596 if (!first_imm.has_value()) {
597 LOG(ERROR, VERIFIER) << "Fail to get first immediate data!";
598 return false;
599 }
600 first_slot_index = first_imm.value();
601 has_slot = true;
602 is_two_slot = true;
603 }
604
605 return true;
606 }
607
VerifySlotNumber(panda_file::MethodDataAccessor & method_accessor,const uint32_t & slot_number,const panda_file::File::EntityId & method_id)608 bool Verifier::VerifySlotNumber(panda_file::MethodDataAccessor &method_accessor, const uint32_t &slot_number,
609 const panda_file::File::EntityId &method_id)
610 {
611 const auto ann_slot_number = GetSlotNumberFromAnnotation(method_accessor);
612 if (!ann_slot_number.has_value()) {
613 LOG(INFO, VERIFIER) << "There is no slot number information in annotaion.";
614 // To be compatible with old abc, slot number verification is not continued
615 return true;
616 }
617 if (slot_number == ann_slot_number.value()) {
618 return true;
619 }
620
621 LOG(ERROR, VERIFIER) << "Slot number has been falsified in method 0x" << method_id;
622 return false;
623 }
624
CheckConstantPoolMethodContent(const panda_file::File::EntityId & method_id)625 bool Verifier::CheckConstantPoolMethodContent(const panda_file::File::EntityId &method_id)
626 {
627 panda_file::MethodDataAccessor method_accessor(*file_, method_id);
628 ASSERT(method_accessor.GetCodeId().has_value());
629 panda_file::CodeDataAccessor code_accessor(*file_, method_accessor.GetCodeId().value());
630 const auto ins_size = code_accessor.GetCodeSize();
631 const auto ins_arr = code_accessor.GetInstructions();
632 auto bc_ins = BytecodeInstruction(ins_arr);
633 const auto bc_ins_last = bc_ins.JumpTo(ins_size);
634 const auto bc_ins_init = bc_ins; // initial PC value
635 uint32_t ins_slot_num = 0;
636 bool has_slot = false;
637 bool is_two_slot = false;
638
639 while (bc_ins.GetAddress() < bc_ins_last.GetAddress()) {
640 if (!VerifyJumpInstruction(bc_ins, bc_ins_last, bc_ins_init)) {
641 LOG(ERROR, VERIFIER) << "Invalid target position of jump instruction";
642 return false;
643 }
644 if (!GetIcSlotFromInstruction(bc_ins, ins_slot_num, has_slot, is_two_slot)) {
645 LOG(ERROR, VERIFIER) << "Fail to get first slot index!";
646 return false;
647 }
648 bc_ins = bc_ins.GetNext();
649 }
650
651 if (has_slot) {
652 if (is_two_slot) {
653 ins_slot_num += 1; // when there are two slots for the last instruction, the slot index increases
654 }
655 ins_slot_num += 1; // slot index starts with zero
656 }
657
658 return true;
659 }
660
CheckConstantPoolIndex() const661 bool Verifier::CheckConstantPoolIndex() const
662 {
663 for (auto &id : ins_method_ids_) {
664 if (!VerifyMethodId(id)) {
665 return false;
666 }
667 }
668
669 for (auto &id : ins_literal_ids_) {
670 if (!VerifyLiteralId(id)) {
671 return false;
672 }
673 }
674
675 for (auto &id : ins_string_ids_) {
676 if (!VerifyStringId(id)) {
677 return false;
678 }
679 }
680
681 return true;
682 }
683 } // namespace panda::verifier
684