1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /* 18 * Dalvik classfile verification. This file contains the verifier entry 19 * points and the static constraint checks. 20 */ 21 #include "Dalvik.h" 22 #include "analysis/CodeVerify.h" 23 24 25 /* fwd */ 26 static bool verifyMethod(Method* meth, int verifyFlags); 27 static bool verifyInstructions(const Method* meth, InsnFlags* insnFlags, 28 int verifyFlags); 29 30 31 /* 32 * Initialize some things we need for verification. 33 */ 34 bool dvmVerificationStartup(void) 35 { 36 gDvm.instrWidth = dexCreateInstrWidthTable(); 37 gDvm.instrFormat = dexCreateInstrFormatTable(); 38 gDvm.instrFlags = dexCreateInstrFlagsTable(); 39 if (gDvm.instrWidth == NULL || gDvm.instrFormat == NULL || 40 gDvm.instrFlags == NULL) 41 { 42 LOGE("Unable to create instruction tables\n"); 43 return false; 44 } 45 46 return true; 47 } 48 49 /* 50 * Free up some things we needed for verification. 51 */ 52 void dvmVerificationShutdown(void) 53 { 54 free(gDvm.instrWidth); 55 free(gDvm.instrFormat); 56 free(gDvm.instrFlags); 57 } 58 59 /* 60 * Induce verification on all classes loaded from this DEX file as part 61 * of pre-verification and optimization. This is never called from a 62 * normally running VM. 63 * 64 * Returns "true" when all classes have been processed. 65 */ 66 bool dvmVerifyAllClasses(DexFile* pDexFile) 67 { 68 u4 count = pDexFile->pHeader->classDefsSize; 69 u4 idx; 70 71 assert(gDvm.optimizing); 72 73 if (gDvm.classVerifyMode == VERIFY_MODE_NONE) { 74 LOGV("+++ verification is disabled, skipping all classes\n"); 75 return true; 76 } 77 if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE && 78 gDvm.optimizingBootstrapClass) 79 { 80 LOGV("+++ verification disabled for bootstrap classes\n"); 81 return true; 82 } 83 84 for (idx = 0; idx < count; idx++) { 85 const DexClassDef* pClassDef; 86 const char* classDescriptor; 87 ClassObject* clazz; 88 89 pClassDef = dexGetClassDef(pDexFile, idx); 90 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx); 91 92 /* all classes are loaded into the bootstrap class loader */ 93 clazz = dvmLookupClass(classDescriptor, NULL, false); 94 if (clazz != NULL) { 95 if (clazz->pDvmDex->pDexFile != pDexFile) { 96 LOGD("DexOpt: not verifying '%s': multiple definitions\n", 97 classDescriptor); 98 } else { 99 if (dvmVerifyClass(clazz, VERIFY_DEFAULT)) { 100 assert((clazz->accessFlags & JAVA_FLAGS_MASK) == 101 pClassDef->accessFlags); 102 ((DexClassDef*)pClassDef)->accessFlags |= 103 CLASS_ISPREVERIFIED; 104 } 105 /* keep going even if one fails */ 106 } 107 } else { 108 LOGV("DexOpt: +++ not verifying '%s'\n", classDescriptor); 109 } 110 } 111 112 return true; 113 } 114 115 /* 116 * Verify a class. 117 * 118 * By the time we get here, the value of gDvm.classVerifyMode should already 119 * have been factored in. If you want to call into the verifier even 120 * though verification is disabled, that's your business. 121 * 122 * Returns "true" on success. 123 */ 124 bool dvmVerifyClass(ClassObject* clazz, int verifyFlags) 125 { 126 int i; 127 128 if (dvmIsClassVerified(clazz)) { 129 LOGD("Ignoring duplicate verify attempt on %s\n", clazz->descriptor); 130 return true; 131 } 132 133 //LOGI("Verify1 '%s'\n", clazz->descriptor); 134 135 // TODO - verify class structure in DEX? 136 137 for (i = 0; i < clazz->directMethodCount; i++) { 138 if (!verifyMethod(&clazz->directMethods[i], verifyFlags)) { 139 LOG_VFY("Verifier rejected class %s\n", clazz->descriptor); 140 return false; 141 } 142 } 143 for (i = 0; i < clazz->virtualMethodCount; i++) { 144 if (!verifyMethod(&clazz->virtualMethods[i], verifyFlags)) { 145 LOG_VFY("Verifier rejected class %s\n", clazz->descriptor); 146 return false; 147 } 148 } 149 150 return true; 151 } 152 153 154 /* 155 * Perform verification on a single method. 156 * 157 * We do this in three passes: 158 * (1) Walk through all code units, determining instruction lengths. 159 * (2) Do static checks, including branch target and operand validation. 160 * (3) Do structural checks, including data-flow analysis. 161 * 162 * Some checks may be bypassed depending on the verification mode. We can't 163 * turn this stuff off completely if we want to do "exact" GC. 164 * 165 * - operands of getfield, putfield, getstatic, putstatic must be valid 166 * - operands of method invocation instructions must be valid 167 * 168 * - code array must not be empty 169 * - (N/A) code_length must be less than 65536 170 * - opcode of first instruction begins at index 0 171 * - only documented instructions may appear 172 * - each instruction follows the last 173 * - (below) last byte of last instruction is at (code_length-1) 174 */ 175 static bool verifyMethod(Method* meth, int verifyFlags) 176 { 177 bool result = false; 178 UninitInstanceMap* uninitMap = NULL; 179 InsnFlags* insnFlags = NULL; 180 int i, newInstanceCount; 181 182 /* 183 * If there aren't any instructions, make sure that's expected, then 184 * exit successfully. Note: meth->insns gets set to a native function 185 * pointer on first call. 186 */ 187 if (dvmGetMethodInsnsSize(meth) == 0) { 188 if (!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth)) { 189 LOG_VFY_METH(meth, 190 "VFY: zero-length code in concrete non-native method\n"); 191 goto bail; 192 } 193 194 goto success; 195 } 196 197 /* 198 * Sanity-check the register counts. ins + locals = registers, so make 199 * sure that ins <= registers. 200 */ 201 if (meth->insSize > meth->registersSize) { 202 LOG_VFY_METH(meth, "VFY: bad register counts (ins=%d regs=%d)\n", 203 meth->insSize, meth->registersSize); 204 goto bail; 205 } 206 207 /* 208 * Allocate and populate an array to hold instruction data. 209 * 210 * TODO: Consider keeping a reusable pre-allocated array sitting 211 * around for smaller methods. 212 */ 213 insnFlags = (InsnFlags*) 214 calloc(dvmGetMethodInsnsSize(meth), sizeof(InsnFlags)); 215 if (insnFlags == NULL) 216 goto bail; 217 218 /* 219 * Compute the width of each instruction and store the result in insnFlags. 220 * Count up the #of occurrences of new-instance instructions while we're 221 * at it. 222 */ 223 if (!dvmComputeCodeWidths(meth, insnFlags, &newInstanceCount)) 224 goto bail; 225 226 /* 227 * Allocate a map to hold the classes of uninitialized instances. 228 */ 229 uninitMap = dvmCreateUninitInstanceMap(meth, insnFlags, newInstanceCount); 230 if (uninitMap == NULL) 231 goto bail; 232 233 /* 234 * Set the "in try" flags for all instructions guarded by a "try" block. 235 */ 236 if (!dvmSetTryFlags(meth, insnFlags)) 237 goto bail; 238 239 /* 240 * Perform static instruction verification. 241 */ 242 if (!verifyInstructions(meth, insnFlags, verifyFlags)) 243 goto bail; 244 245 /* 246 * Do code-flow analysis. Do this after verifying the branch targets 247 * so we don't need to worry about it here. 248 * 249 * If there are no registers, we don't need to do much in the way of 250 * analysis, but we still need to verify that nothing actually tries 251 * to use a register. 252 */ 253 if (!dvmVerifyCodeFlow(meth, insnFlags, uninitMap)) { 254 //LOGD("+++ %s failed code flow\n", meth->name); 255 goto bail; 256 } 257 258 success: 259 result = true; 260 261 bail: 262 dvmFreeUninitInstanceMap(uninitMap); 263 free(insnFlags); 264 return result; 265 } 266 267 268 /* 269 * Verify an array data table. "curOffset" is the offset of the fill-array-data 270 * instruction. 271 */ 272 static bool checkArrayData(const Method* meth, int curOffset) 273 { 274 const int insnCount = dvmGetMethodInsnsSize(meth); 275 const u2* insns = meth->insns + curOffset; 276 const u2* arrayData; 277 int valueCount, valueWidth, tableSize; 278 int offsetToArrayData; 279 280 assert(curOffset >= 0 && curOffset < insnCount); 281 282 /* make sure the start of the array data table is in range */ 283 offsetToArrayData = insns[1] | (((s4)insns[2]) << 16); 284 if (curOffset + offsetToArrayData < 0 || 285 curOffset + offsetToArrayData + 2 >= insnCount) 286 { 287 LOG_VFY_METH(meth, 288 "VFY: invalid array data start: at %d, data offset %d, count %d\n", 289 curOffset, offsetToArrayData, insnCount); 290 return false; 291 } 292 293 /* offset to array data table is a relative branch-style offset */ 294 arrayData = insns + offsetToArrayData; 295 296 /* make sure the table is 32-bit aligned */ 297 if ((((u4) arrayData) & 0x03) != 0) { 298 LOG_VFY_METH(meth, 299 "VFY: unaligned array data table: at %d, data offset %d\n", 300 curOffset, offsetToArrayData); 301 return false; 302 } 303 304 valueWidth = arrayData[1]; 305 valueCount = *(u4*)(&arrayData[2]); 306 307 tableSize = 4 + (valueWidth * valueCount + 1) / 2; 308 309 /* make sure the end of the switch is in range */ 310 if (curOffset + offsetToArrayData + tableSize > insnCount) { 311 LOG_VFY_METH(meth, 312 "VFY: invalid array data end: at %d, data offset %d, end %d, " 313 "count %d\n", 314 curOffset, offsetToArrayData, 315 curOffset + offsetToArrayData + tableSize, 316 insnCount); 317 return false; 318 } 319 320 return true; 321 } 322 323 324 /* 325 * Decode the current instruction. 326 */ 327 static void decodeInstruction(const Method* meth, int insnIdx, 328 DecodedInstruction* pDecInsn) 329 { 330 dexDecodeInstruction(gDvm.instrFormat, meth->insns + insnIdx, pDecInsn); 331 } 332 333 334 /* 335 * Perform static checks on a "new-instance" instruction. Specifically, 336 * make sure the class reference isn't for an array class. 337 * 338 * We don't need the actual class, just a pointer to the class name. 339 */ 340 static bool checkNewInstance(const Method* meth, int insnIdx) 341 { 342 DvmDex* pDvmDex = meth->clazz->pDvmDex; 343 DecodedInstruction decInsn; 344 const char* classDescriptor; 345 u4 idx; 346 347 decodeInstruction(meth, insnIdx, &decInsn); 348 idx = decInsn.vB; // 2nd item 349 if (idx >= pDvmDex->pHeader->typeIdsSize) { 350 LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n", 351 idx, pDvmDex->pHeader->typeIdsSize); 352 return false; 353 } 354 355 classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx); 356 if (classDescriptor[0] != 'L') { 357 LOG_VFY_METH(meth, "VFY: can't call new-instance on type '%s'\n", 358 classDescriptor); 359 return false; 360 } 361 362 return true; 363 } 364 365 /* 366 * Perform static checks on a "new-array" instruction. Specifically, make 367 * sure they aren't creating an array of arrays that causes the number of 368 * dimensions to exceed 255. 369 */ 370 static bool checkNewArray(const Method* meth, int insnIdx) 371 { 372 DvmDex* pDvmDex = meth->clazz->pDvmDex; 373 DecodedInstruction decInsn; 374 const char* classDescriptor; 375 u4 idx; 376 377 decodeInstruction(meth, insnIdx, &decInsn); 378 idx = decInsn.vC; // 3rd item 379 if (idx >= pDvmDex->pHeader->typeIdsSize) { 380 LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n", 381 idx, pDvmDex->pHeader->typeIdsSize); 382 return false; 383 } 384 385 classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx); 386 387 int bracketCount = 0; 388 const char* cp = classDescriptor; 389 while (*cp++ == '[') 390 bracketCount++; 391 392 if (bracketCount == 0) { 393 /* The given class must be an array type. */ 394 LOG_VFY_METH(meth, "VFY: can't new-array class '%s' (not an array)\n", 395 classDescriptor); 396 return false; 397 } else if (bracketCount > 255) { 398 /* It is illegal to create an array of more than 255 dimensions. */ 399 LOG_VFY_METH(meth, "VFY: can't new-array class '%s' (exceeds limit)\n", 400 classDescriptor); 401 return false; 402 } 403 404 return true; 405 } 406 407 /* 408 * Perform static checks on an instruction that takes a class constant. 409 * Ensure that the class index is in the valid range. 410 */ 411 static bool checkTypeIndex(const Method* meth, int insnIdx, bool useB) 412 { 413 DvmDex* pDvmDex = meth->clazz->pDvmDex; 414 DecodedInstruction decInsn; 415 u4 idx; 416 417 decodeInstruction(meth, insnIdx, &decInsn); 418 if (useB) 419 idx = decInsn.vB; 420 else 421 idx = decInsn.vC; 422 if (idx >= pDvmDex->pHeader->typeIdsSize) { 423 LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n", 424 idx, pDvmDex->pHeader->typeIdsSize); 425 return false; 426 } 427 428 return true; 429 } 430 431 /* 432 * Perform static checks on a field get or set instruction. All we do 433 * here is ensure that the field index is in the valid range. 434 */ 435 static bool checkFieldIndex(const Method* meth, int insnIdx, bool useB) 436 { 437 DvmDex* pDvmDex = meth->clazz->pDvmDex; 438 DecodedInstruction decInsn; 439 u4 idx; 440 441 decodeInstruction(meth, insnIdx, &decInsn); 442 if (useB) 443 idx = decInsn.vB; 444 else 445 idx = decInsn.vC; 446 if (idx >= pDvmDex->pHeader->fieldIdsSize) { 447 LOG_VFY_METH(meth, 448 "VFY: bad field index %d (max %d) at offset 0x%04x\n", 449 idx, pDvmDex->pHeader->fieldIdsSize, insnIdx); 450 return false; 451 } 452 453 return true; 454 } 455 456 /* 457 * Perform static checks on a method invocation instruction. All we do 458 * here is ensure that the method index is in the valid range. 459 */ 460 static bool checkMethodIndex(const Method* meth, int insnIdx) 461 { 462 DvmDex* pDvmDex = meth->clazz->pDvmDex; 463 DecodedInstruction decInsn; 464 465 decodeInstruction(meth, insnIdx, &decInsn); 466 if (decInsn.vB >= pDvmDex->pHeader->methodIdsSize) { 467 LOG_VFY_METH(meth, "VFY: bad method index %d (max %d)\n", 468 decInsn.vB, pDvmDex->pHeader->methodIdsSize); 469 return false; 470 } 471 472 return true; 473 } 474 475 /* 476 * Perform static checks on a string constant instruction. All we do 477 * here is ensure that the string index is in the valid range. 478 */ 479 static bool checkStringIndex(const Method* meth, int insnIdx) 480 { 481 DvmDex* pDvmDex = meth->clazz->pDvmDex; 482 DecodedInstruction decInsn; 483 484 decodeInstruction(meth, insnIdx, &decInsn); 485 if (decInsn.vB >= pDvmDex->pHeader->stringIdsSize) { 486 LOG_VFY_METH(meth, "VFY: bad string index %d (max %d)\n", 487 decInsn.vB, pDvmDex->pHeader->stringIdsSize); 488 return false; 489 } 490 491 return true; 492 } 493 494 /* 495 * Perform static verification on instructions. 496 * 497 * As a side effect, this sets the "branch target" flags in InsnFlags. 498 * 499 * "(CF)" items are handled during code-flow analysis. 500 * 501 * v3 4.10.1 502 * - target of each jump and branch instruction must be valid 503 * - targets of switch statements must be valid 504 * - (CF) operands referencing constant pool entries must be valid 505 * - (CF) operands of getfield, putfield, getstatic, putstatic must be valid 506 * - (new) verify operands of "quick" field ops 507 * - (CF) operands of method invocation instructions must be valid 508 * - (new) verify operands of "quick" method invoke ops 509 * - (CF) only invoke-direct can call a method starting with '<' 510 * - (CF) <clinit> must never be called explicitly 511 * - (CF) operands of instanceof, checkcast, new (and variants) must be valid 512 * - new-array[-type] limited to 255 dimensions 513 * - can't use "new" on an array class 514 * - (?) limit dimensions in multi-array creation 515 * - (CF) local variable load/store register values must be in valid range 516 * 517 * v3 4.11.1.2 518 * - branches must be within the bounds of the code array 519 * - targets of all control-flow instructions are the start of an instruction 520 * - (CF) register accesses fall within range of allocated registers 521 * - (N/A) access to constant pool must be of appropriate type 522 * - (CF) code does not end in the middle of an instruction 523 * - (CF) execution cannot fall off the end of the code 524 * - (earlier) for each exception handler, the "try" area must begin and 525 * end at the start of an instruction (end can be at the end of the code) 526 * - (earlier) for each exception handler, the handler must start at a valid 527 * instruction 528 * 529 * TODO: move some of the "CF" items in here for better performance (the 530 * code-flow analysis sometimes has to process the same instruction several 531 * times). 532 */ 533 static bool verifyInstructions(const Method* meth, InsnFlags* insnFlags, 534 int verifyFlags) 535 { 536 const int insnCount = dvmGetMethodInsnsSize(meth); 537 const u2* insns = meth->insns; 538 int i; 539 540 /* the start of the method is a "branch target" */ 541 dvmInsnSetBranchTarget(insnFlags, 0, true); 542 543 for (i = 0; i < insnCount; /**/) { 544 /* 545 * These types of instructions can be GC points. To support precise 546 * GC, all such instructions must export the PC in the interpreter, 547 * or the GC won't be able to identify the current PC for the thread. 548 */ 549 static const int gcMask = kInstrCanBranch | kInstrCanSwitch | 550 kInstrCanThrow | kInstrCanReturn; 551 552 int width = dvmInsnGetWidth(insnFlags, i); 553 OpCode opcode = *insns & 0xff; 554 InstructionFlags opFlags = dexGetInstrFlags(gDvm.instrFlags, opcode); 555 int offset, absOffset; 556 557 if ((opFlags & gcMask) != 0) { 558 /* 559 * This instruction is probably a GC point. Branch instructions 560 * only qualify if they go backward, so we need to check the 561 * offset. 562 */ 563 int offset = -1; 564 bool unused; 565 if (dvmGetBranchTarget(meth, insnFlags, i, &offset, &unused)) { 566 if (offset < 0) { 567 dvmInsnSetGcPoint(insnFlags, i, true); 568 } 569 } else { 570 /* not a branch target */ 571 dvmInsnSetGcPoint(insnFlags, i, true); 572 } 573 } 574 575 switch (opcode) { 576 case OP_NOP: 577 /* plain no-op or switch table data; nothing to do here */ 578 break; 579 580 case OP_CONST_STRING: 581 case OP_CONST_STRING_JUMBO: 582 if (!checkStringIndex(meth, i)) 583 return false; 584 break; 585 586 case OP_CONST_CLASS: 587 case OP_CHECK_CAST: 588 if (!checkTypeIndex(meth, i, true)) 589 return false; 590 break; 591 case OP_INSTANCE_OF: 592 if (!checkTypeIndex(meth, i, false)) 593 return false; 594 break; 595 596 case OP_PACKED_SWITCH: 597 case OP_SPARSE_SWITCH: 598 /* verify the associated table */ 599 if (!dvmCheckSwitchTargets(meth, insnFlags, i)) 600 return false; 601 break; 602 603 case OP_FILL_ARRAY_DATA: 604 /* verify the associated table */ 605 if (!checkArrayData(meth, i)) 606 return false; 607 break; 608 609 case OP_GOTO: 610 case OP_GOTO_16: 611 case OP_IF_EQ: 612 case OP_IF_NE: 613 case OP_IF_LT: 614 case OP_IF_GE: 615 case OP_IF_GT: 616 case OP_IF_LE: 617 case OP_IF_EQZ: 618 case OP_IF_NEZ: 619 case OP_IF_LTZ: 620 case OP_IF_GEZ: 621 case OP_IF_GTZ: 622 case OP_IF_LEZ: 623 /* check the destination */ 624 if (!dvmCheckBranchTarget(meth, insnFlags, i, false)) 625 return false; 626 break; 627 case OP_GOTO_32: 628 /* check the destination; self-branch is okay */ 629 if (!dvmCheckBranchTarget(meth, insnFlags, i, true)) 630 return false; 631 break; 632 633 case OP_NEW_INSTANCE: 634 if (!checkNewInstance(meth, i)) 635 return false; 636 break; 637 638 case OP_NEW_ARRAY: 639 if (!checkNewArray(meth, i)) 640 return false; 641 break; 642 643 case OP_FILLED_NEW_ARRAY: 644 if (!checkTypeIndex(meth, i, true)) 645 return false; 646 break; 647 case OP_FILLED_NEW_ARRAY_RANGE: 648 if (!checkTypeIndex(meth, i, true)) 649 return false; 650 break; 651 652 case OP_IGET: 653 case OP_IGET_WIDE: 654 case OP_IGET_OBJECT: 655 case OP_IGET_BOOLEAN: 656 case OP_IGET_BYTE: 657 case OP_IGET_CHAR: 658 case OP_IGET_SHORT: 659 case OP_IPUT: 660 case OP_IPUT_WIDE: 661 case OP_IPUT_OBJECT: 662 case OP_IPUT_BOOLEAN: 663 case OP_IPUT_BYTE: 664 case OP_IPUT_CHAR: 665 case OP_IPUT_SHORT: 666 /* check the field index */ 667 if (!checkFieldIndex(meth, i, false)) 668 return false; 669 break; 670 case OP_SGET: 671 case OP_SGET_WIDE: 672 case OP_SGET_OBJECT: 673 case OP_SGET_BOOLEAN: 674 case OP_SGET_BYTE: 675 case OP_SGET_CHAR: 676 case OP_SGET_SHORT: 677 case OP_SPUT: 678 case OP_SPUT_WIDE: 679 case OP_SPUT_OBJECT: 680 case OP_SPUT_BOOLEAN: 681 case OP_SPUT_BYTE: 682 case OP_SPUT_CHAR: 683 case OP_SPUT_SHORT: 684 /* check the field index */ 685 if (!checkFieldIndex(meth, i, true)) 686 return false; 687 break; 688 689 case OP_INVOKE_VIRTUAL: 690 case OP_INVOKE_SUPER: 691 case OP_INVOKE_DIRECT: 692 case OP_INVOKE_STATIC: 693 case OP_INVOKE_INTERFACE: 694 case OP_INVOKE_VIRTUAL_RANGE: 695 case OP_INVOKE_SUPER_RANGE: 696 case OP_INVOKE_DIRECT_RANGE: 697 case OP_INVOKE_STATIC_RANGE: 698 case OP_INVOKE_INTERFACE_RANGE: 699 /* check the method index */ 700 if (!checkMethodIndex(meth, i)) 701 return false; 702 break; 703 704 case OP_EXECUTE_INLINE: 705 case OP_INVOKE_DIRECT_EMPTY: 706 case OP_IGET_QUICK: 707 case OP_IGET_WIDE_QUICK: 708 case OP_IGET_OBJECT_QUICK: 709 case OP_IPUT_QUICK: 710 case OP_IPUT_WIDE_QUICK: 711 case OP_IPUT_OBJECT_QUICK: 712 case OP_INVOKE_VIRTUAL_QUICK: 713 case OP_INVOKE_VIRTUAL_QUICK_RANGE: 714 case OP_INVOKE_SUPER_QUICK: 715 case OP_INVOKE_SUPER_QUICK_RANGE: 716 if ((verifyFlags & VERIFY_ALLOW_OPT_INSTRS) == 0) { 717 LOG_VFY("VFY: not expecting optimized instructions\n"); 718 return false; 719 } 720 break; 721 722 default: 723 /* nothing to do */ 724 break; 725 } 726 727 assert(width > 0); 728 i += width; 729 insns += width; 730 } 731 732 /* make sure the last instruction ends at the end of the insn area */ 733 if (i != insnCount) { 734 LOG_VFY_METH(meth, 735 "VFY: code did not end when expected (end at %d, count %d)\n", 736 i, insnCount); 737 return false; 738 } 739 740 return true; 741 } 742 743