Пример #1
0
/*
Perform static verification on instructions.

As a side effect, this sets the "branch target" flags in InsnFlags.

"(CF)" items are handled during code-flow analysis.

v3 4.10.1
- target of each jump and branch instruction must be valid
- targets of switch statements must be valid
- operands referencing constant pool entries must be valid
- (CF) operands of getfield, putfield, getstatic, putstatic must be valid
- (new) verify operands of "quick" field ops
- (CF) operands of method invocation instructions must be valid
- (new) verify operands of "quick" method invoke ops
- (CF) only invoke-direct can call a method starting with '<'
- (CF) <clinit> must never be called explicitly
- operands of instanceof, checkcast, new (and variants) must be valid
- new-array[-type] limited to 255 dimensions
- can't use "new" on an array class
- (?) limit dimensions in multi-array creation
- local variable load/store register values must be in valid range

v3 4.11.1.2
- branches must be within the bounds of the code array
- targets of all control-flow instructions are the start of an instruction
- register accesses fall within range of allocated registers
- (N/A) access to constant pool must be of appropriate type
- code does not end in the middle of an instruction
- execution cannot fall off the end of the code
- (earlier) for each exception handler, the "try" area must begin and
  end at the start of an instruction (end can be at the end of the code)
- (earlier) for each exception handler, the handler must start at a valid
  instruction
  
执行指令的静态检查。
 */
static bool verifyInstructions(VerifierData* vdata)
{
    const Method* meth = vdata->method;
    const DvmDex* pDvmDex = meth->clazz->pDvmDex;
    InsnFlags* insnFlags = vdata->insnFlags;
    const u2* insns = meth->insns;
    unsigned int codeOffset;

    /* the start of the method is a "branch target" */
    /* 方法起始是一个分支目标 */    
    dvmInsnSetBranchTarget(insnFlags, 0, true);

    for (codeOffset = 0; codeOffset < vdata->insnsSize; /**/) {
        /*
        Pull the instruction apart.
        
        将指令分开。
        */
        int width = dvmInsnGetWidth(insnFlags, codeOffset);
        DecodedInstruction decInsn;
        bool okay = true;

        dexDecodeInstruction(meth->insns + codeOffset, &decInsn);

        /*
        Check register, type, class, field, method, and string indices
        for out-of-range values.  Do additional checks on branch targets
        and some special cases like new-instance and new-array.
        
        检查寄存器,类型,类,域,方法,字符串索引下标越界。
        在分支目标上做额外检查和一些特殊的情况,如:new-instance 和 new-array。
        */
        switch (decInsn.opcode) {
        case OP_NOP:
        case OP_RETURN_VOID:
            /* nothing to check */
            /* 不做任何检查 */            
            break;
        case OP_MOVE_RESULT:
        case OP_MOVE_RESULT_OBJECT:
        case OP_MOVE_EXCEPTION:
        case OP_RETURN:
        case OP_RETURN_OBJECT:
        case OP_CONST_4:
        case OP_CONST_16:
        case OP_CONST:
        case OP_CONST_HIGH16:
        case OP_MONITOR_ENTER:
        case OP_MONITOR_EXIT:
        case OP_THROW:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            break;
        case OP_MOVE_RESULT_WIDE:
        case OP_RETURN_WIDE:
        case OP_CONST_WIDE_16:
        case OP_CONST_WIDE_32:
        case OP_CONST_WIDE:
        case OP_CONST_WIDE_HIGH16:
            okay &= checkWideRegisterIndex(meth, decInsn.vA);
            break;
        case OP_GOTO:
        case OP_GOTO_16:
            okay &= checkBranchTarget(meth, insnFlags, codeOffset, false);
            break;
        case OP_GOTO_32:
            okay &= checkBranchTarget(meth, insnFlags, codeOffset, true);
            break;
        case OP_MOVE:
        case OP_MOVE_FROM16:
        case OP_MOVE_16:
        case OP_MOVE_OBJECT:
        case OP_MOVE_OBJECT_FROM16:
        case OP_MOVE_OBJECT_16:
        case OP_ARRAY_LENGTH:
        case OP_NEG_INT:
        case OP_NOT_INT:
        case OP_NEG_FLOAT:
        case OP_INT_TO_FLOAT:
        case OP_FLOAT_TO_INT:
        case OP_INT_TO_BYTE:
        case OP_INT_TO_CHAR:
        case OP_INT_TO_SHORT:
        case OP_ADD_INT_2ADDR:
        case OP_SUB_INT_2ADDR:
        case OP_MUL_INT_2ADDR:
        case OP_DIV_INT_2ADDR:
        case OP_REM_INT_2ADDR:
        case OP_AND_INT_2ADDR:
        case OP_OR_INT_2ADDR:
        case OP_XOR_INT_2ADDR:
        case OP_SHL_INT_2ADDR:
        case OP_SHR_INT_2ADDR:
        case OP_USHR_INT_2ADDR:
        case OP_ADD_FLOAT_2ADDR:
        case OP_SUB_FLOAT_2ADDR:
        case OP_MUL_FLOAT_2ADDR:
        case OP_DIV_FLOAT_2ADDR:
        case OP_REM_FLOAT_2ADDR:
        case OP_ADD_INT_LIT16:
        case OP_RSUB_INT:
        case OP_MUL_INT_LIT16:
        case OP_DIV_INT_LIT16:
        case OP_REM_INT_LIT16:
        case OP_AND_INT_LIT16:
        case OP_OR_INT_LIT16:
        case OP_XOR_INT_LIT16:
        case OP_ADD_INT_LIT8:
        case OP_RSUB_INT_LIT8:
        case OP_MUL_INT_LIT8:
        case OP_DIV_INT_LIT8:
        case OP_REM_INT_LIT8:
        case OP_AND_INT_LIT8:
        case OP_OR_INT_LIT8:
        case OP_XOR_INT_LIT8:
        case OP_SHL_INT_LIT8:
        case OP_SHR_INT_LIT8:
        case OP_USHR_INT_LIT8:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkRegisterIndex(meth, decInsn.vB);
            break;
        case OP_INT_TO_LONG:
        case OP_INT_TO_DOUBLE:
        case OP_FLOAT_TO_LONG:
        case OP_FLOAT_TO_DOUBLE:
        case OP_SHL_LONG_2ADDR:
        case OP_SHR_LONG_2ADDR:
        case OP_USHR_LONG_2ADDR:
            okay &= checkWideRegisterIndex(meth, decInsn.vA);
            okay &= checkRegisterIndex(meth, decInsn.vB);
            break;
        case OP_LONG_TO_INT:
        case OP_LONG_TO_FLOAT:
        case OP_DOUBLE_TO_INT:
        case OP_DOUBLE_TO_FLOAT:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkWideRegisterIndex(meth, decInsn.vB);
            break;
        case OP_MOVE_WIDE:
        case OP_MOVE_WIDE_FROM16:
        case OP_MOVE_WIDE_16:
        case OP_DOUBLE_TO_LONG:
        case OP_LONG_TO_DOUBLE:
        case OP_NEG_DOUBLE:
        case OP_NEG_LONG:
        case OP_NOT_LONG:
        case OP_ADD_LONG_2ADDR:
        case OP_SUB_LONG_2ADDR:
        case OP_MUL_LONG_2ADDR:
        case OP_DIV_LONG_2ADDR:
        case OP_REM_LONG_2ADDR:
        case OP_AND_LONG_2ADDR:
        case OP_OR_LONG_2ADDR:
        case OP_XOR_LONG_2ADDR:
        case OP_ADD_DOUBLE_2ADDR:
        case OP_SUB_DOUBLE_2ADDR:
        case OP_MUL_DOUBLE_2ADDR:
        case OP_DIV_DOUBLE_2ADDR:
        case OP_REM_DOUBLE_2ADDR:
            okay &= checkWideRegisterIndex(meth, decInsn.vA);
            okay &= checkWideRegisterIndex(meth, decInsn.vB);
            break;
        case OP_CONST_STRING:
        case OP_CONST_STRING_JUMBO:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkStringIndex(pDvmDex, decInsn.vB);
            break;
        case OP_CONST_CLASS:
        case OP_CHECK_CAST:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkTypeIndex(pDvmDex, decInsn.vB);
            break;
        case OP_INSTANCE_OF:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkRegisterIndex(meth, decInsn.vB);
            okay &= checkTypeIndex(pDvmDex, decInsn.vC);
            break;
        case OP_NEW_INSTANCE:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkNewInstance(pDvmDex, decInsn.vB);
            break;
        case OP_NEW_ARRAY:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkRegisterIndex(meth, decInsn.vB);
            okay &= checkNewArray(pDvmDex, decInsn.vC);
            break;
        case OP_FILL_ARRAY_DATA:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkArrayData(meth, codeOffset);
            break;
        case OP_PACKED_SWITCH:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkSwitchTargets(meth, insnFlags, codeOffset);
            break;
        case OP_SPARSE_SWITCH:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkSwitchTargets(meth, insnFlags, codeOffset);
            break;
        case OP_CMPL_FLOAT:
        case OP_CMPG_FLOAT:
        case OP_AGET:
        case OP_AGET_OBJECT:
        case OP_AGET_BOOLEAN:
        case OP_AGET_BYTE:
        case OP_AGET_CHAR:
        case OP_AGET_SHORT:
        case OP_APUT:
        case OP_APUT_OBJECT:
        case OP_APUT_BOOLEAN:
        case OP_APUT_BYTE:
        case OP_APUT_CHAR:
        case OP_APUT_SHORT:
        case OP_ADD_INT:
        case OP_SUB_INT:
        case OP_MUL_INT:
        case OP_DIV_INT:
        case OP_REM_INT:
        case OP_AND_INT:
        case OP_OR_INT:
        case OP_XOR_INT:
        case OP_SHL_INT:
        case OP_SHR_INT:
        case OP_USHR_INT:
        case OP_ADD_FLOAT:
        case OP_SUB_FLOAT:
        case OP_MUL_FLOAT:
        case OP_DIV_FLOAT:
        case OP_REM_FLOAT:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkRegisterIndex(meth, decInsn.vB);
            okay &= checkRegisterIndex(meth, decInsn.vC);
            break;
        case OP_AGET_WIDE:
        case OP_APUT_WIDE:
            okay &= checkWideRegisterIndex(meth, decInsn.vA);
            okay &= checkRegisterIndex(meth, decInsn.vB);
            okay &= checkRegisterIndex(meth, decInsn.vC);
            break;
        case OP_CMPL_DOUBLE:
        case OP_CMPG_DOUBLE:
        case OP_CMP_LONG:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkWideRegisterIndex(meth, decInsn.vB);
            okay &= checkWideRegisterIndex(meth, decInsn.vC);
            break;
        case OP_ADD_DOUBLE:
        case OP_SUB_DOUBLE:
        case OP_MUL_DOUBLE:
        case OP_DIV_DOUBLE:
        case OP_REM_DOUBLE:
        case OP_ADD_LONG:
        case OP_SUB_LONG:
        case OP_MUL_LONG:
        case OP_DIV_LONG:
        case OP_REM_LONG:
        case OP_AND_LONG:
        case OP_OR_LONG:
        case OP_XOR_LONG:
            okay &= checkWideRegisterIndex(meth, decInsn.vA);
            okay &= checkWideRegisterIndex(meth, decInsn.vB);
            okay &= checkWideRegisterIndex(meth, decInsn.vC);
            break;
        case OP_SHL_LONG:
        case OP_SHR_LONG:
        case OP_USHR_LONG:
            okay &= checkWideRegisterIndex(meth, decInsn.vA);
            okay &= checkWideRegisterIndex(meth, decInsn.vB);
            okay &= checkRegisterIndex(meth, decInsn.vC);
            break;
        case OP_IF_EQ:
        case OP_IF_NE:
        case OP_IF_LT:
        case OP_IF_GE:
        case OP_IF_GT:
        case OP_IF_LE:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkRegisterIndex(meth, decInsn.vB);
            okay &= checkBranchTarget(meth, insnFlags, codeOffset, false);
            break;
        case OP_IF_EQZ:
        case OP_IF_NEZ:
        case OP_IF_LTZ:
        case OP_IF_GEZ:
        case OP_IF_GTZ:
        case OP_IF_LEZ:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkBranchTarget(meth, insnFlags, codeOffset, false);
            break;
        case OP_IGET:
        case OP_IGET_OBJECT:
        case OP_IGET_BOOLEAN:
        case OP_IGET_BYTE:
        case OP_IGET_CHAR:
        case OP_IGET_SHORT:
        case OP_IPUT:
        case OP_IPUT_OBJECT:
        case OP_IPUT_BOOLEAN:
        case OP_IPUT_BYTE:
        case OP_IPUT_CHAR:
        case OP_IPUT_SHORT:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkRegisterIndex(meth, decInsn.vB);
            okay &= checkFieldIndex(pDvmDex, decInsn.vC);
            break;
        case OP_IGET_WIDE:
        case OP_IPUT_WIDE:
            okay &= checkWideRegisterIndex(meth, decInsn.vA);
            okay &= checkRegisterIndex(meth, decInsn.vB);
            okay &= checkFieldIndex(pDvmDex, decInsn.vC);
            break;
        case OP_SGET:
        case OP_SGET_OBJECT:
        case OP_SGET_BOOLEAN:
        case OP_SGET_BYTE:
        case OP_SGET_CHAR:
        case OP_SGET_SHORT:
        case OP_SPUT:
        case OP_SPUT_OBJECT:
        case OP_SPUT_BOOLEAN:
        case OP_SPUT_BYTE:
        case OP_SPUT_CHAR:
        case OP_SPUT_SHORT:
            okay &= checkRegisterIndex(meth, decInsn.vA);
            okay &= checkFieldIndex(pDvmDex, decInsn.vB);
            break;
        case OP_SGET_WIDE:
        case OP_SPUT_WIDE:
            okay &= checkWideRegisterIndex(meth, decInsn.vA);
            okay &= checkFieldIndex(pDvmDex, decInsn.vB);
            break;
        case OP_FILLED_NEW_ARRAY:
            /* decoder uses B, not C, for type ref */
            okay &= checkTypeIndex(pDvmDex, decInsn.vB);
            okay &= checkVarargRegs(meth, &decInsn);
            break;
        case OP_FILLED_NEW_ARRAY_RANGE:
            okay &= checkTypeIndex(pDvmDex, decInsn.vB);
            okay &= checkVarargRangeRegs(meth, &decInsn);
            break;
        case OP_INVOKE_VIRTUAL:
        case OP_INVOKE_SUPER:
        case OP_INVOKE_DIRECT:
        case OP_INVOKE_STATIC:
        case OP_INVOKE_INTERFACE:
            /* decoder uses B, not C, for type ref */
            okay &= checkMethodIndex(pDvmDex, decInsn.vB);
            okay &= checkVarargRegs(meth, &decInsn);
            break;
        case OP_INVOKE_VIRTUAL_RANGE:
        case OP_INVOKE_SUPER_RANGE:
        case OP_INVOKE_DIRECT_RANGE:
        case OP_INVOKE_STATIC_RANGE:
        case OP_INVOKE_INTERFACE_RANGE:
            okay &= checkMethodIndex(pDvmDex, decInsn.vB);
            okay &= checkVarargRangeRegs(meth, &decInsn);
            break;

        /* verifier/optimizer output; we should never see these */
        case OP_IGET_VOLATILE:
        case OP_IPUT_VOLATILE:
        case OP_SGET_VOLATILE:
        case OP_SPUT_VOLATILE:
        case OP_IGET_OBJECT_VOLATILE:
        case OP_IPUT_OBJECT_VOLATILE:
        case OP_SGET_OBJECT_VOLATILE:
        case OP_SPUT_OBJECT_VOLATILE:
        case OP_IGET_WIDE_VOLATILE:
        case OP_IPUT_WIDE_VOLATILE:
        case OP_SGET_WIDE_VOLATILE:
        case OP_SPUT_WIDE_VOLATILE:
        case OP_BREAKPOINT:
        case OP_THROW_VERIFICATION_ERROR:
        case OP_EXECUTE_INLINE:
        case OP_EXECUTE_INLINE_RANGE:
        case OP_INVOKE_OBJECT_INIT_RANGE:
        case OP_RETURN_VOID_BARRIER:
        case OP_IGET_QUICK:
        case OP_IGET_WIDE_QUICK:
        case OP_IGET_OBJECT_QUICK:
        case OP_IPUT_QUICK:
        case OP_IPUT_WIDE_QUICK:
        case OP_IPUT_OBJECT_QUICK:
        case OP_INVOKE_VIRTUAL_QUICK:
        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
        case OP_INVOKE_SUPER_QUICK:
        case OP_INVOKE_SUPER_QUICK_RANGE:
        case OP_UNUSED_3E:
        case OP_UNUSED_3F:
        case OP_UNUSED_40:
        case OP_UNUSED_41:
        case OP_UNUSED_42:
        case OP_UNUSED_43:
        case OP_UNUSED_73:
        case OP_UNUSED_79:
        case OP_UNUSED_7A:
        case OP_UNUSED_FF:
            ALOGE("VFY: unexpected opcode %04x", decInsn.opcode);
            okay = false;
            break;

        /*
         * DO NOT add a "default" clause here.  Without it the compiler will
         * complain if an instruction is missing (which is desirable).
         */
        }

        if (!okay) {
            LOG_VFY_METH(meth, "VFY:  rejecting opcode 0x%02x at 0x%04x",
                decInsn.opcode, codeOffset);
            return false;
        }

        OpcodeFlags opFlags = dexGetFlagsFromOpcode(decInsn.opcode);
        if ((opFlags & VERIFY_GC_INST_MASK) != 0) {
            /*
             * This instruction is a GC point.  If space is a concern,
             * the set of GC points could be reduced by eliminating
             * foward branches.
             *
             * TODO: we could also scan the targets of a "switch" statement,
             * and if none of them branch backward we could ignore that
             * instruction as well.
             */
            dvmInsnSetGcPoint(insnFlags, codeOffset, true);
        }

        assert(width > 0);
        codeOffset += width;
        insns += width;
    }

    /* make sure the last instruction ends at the end of the insn area */
    if (codeOffset != vdata->insnsSize) {
        LOG_VFY_METH(meth,
            "VFY: code did not end when expected (end at %d, count %d)",
            codeOffset, vdata->insnsSize);
        return false;
    }

    return true;
}
Пример #2
0
/*
 * Perform static verification on instructions.
 *
 * As a side effect, this sets the "branch target" flags in InsnFlags.
 *
 * "(CF)" items are handled during code-flow analysis.
 *
 * v3 4.10.1
 * - target of each jump and branch instruction must be valid
 * - targets of switch statements must be valid
 * - (CF) operands referencing constant pool entries must be valid
 * - (CF) operands of getfield, putfield, getstatic, putstatic must be valid
 * - (new) verify operands of "quick" field ops
 * - (CF) operands of method invocation instructions must be valid
 * - (new) verify operands of "quick" method invoke ops
 * - (CF) only invoke-direct can call a method starting with '<'
 * - (CF) <clinit> must never be called explicitly
 * - (CF) operands of instanceof, checkcast, new (and variants) must be valid
 * - new-array[-type] limited to 255 dimensions
 * - can't use "new" on an array class
 * - (?) limit dimensions in multi-array creation
 * - (CF) local variable load/store register values must be in valid range
 *
 * v3 4.11.1.2
 * - branches must be within the bounds of the code array
 * - targets of all control-flow instructions are the start of an instruction
 * - (CF) register accesses fall within range of allocated registers
 * - (N/A) access to constant pool must be of appropriate type
 * - (CF) code does not end in the middle of an instruction
 * - (CF) execution cannot fall off the end of the code
 * - (earlier) for each exception handler, the "try" area must begin and
 *   end at the start of an instruction (end can be at the end of the code)
 * - (earlier) for each exception handler, the handler must start at a valid
 *   instruction
 *
 * TODO: move some of the "CF" items in here for better performance (the
 * code-flow analysis sometimes has to process the same instruction several
 * times).
 */
static bool verifyInstructions(const Method* meth, InsnFlags* insnFlags,
    int verifyFlags)
{
    const int insnCount = dvmGetMethodInsnsSize(meth);
    const u2* insns = meth->insns;
    int i;

    /* the start of the method is a "branch target" */
    dvmInsnSetBranchTarget(insnFlags, 0, true);

    for (i = 0; i < insnCount; /**/) {
        /*
         * These types of instructions can be GC points.  To support precise
         * GC, all such instructions must export the PC in the interpreter,
         * or the GC won't be able to identify the current PC for the thread.
         */
        static const int gcMask = kInstrCanBranch | kInstrCanSwitch |
            kInstrCanThrow | kInstrCanReturn;

        int width = dvmInsnGetWidth(insnFlags, i);
        OpCode opcode = *insns & 0xff;
        InstructionFlags opFlags = dexGetInstrFlags(gDvm.instrFlags, opcode);
        int offset, absOffset;

        if ((opFlags & gcMask) != 0) {
            /*
             * This instruction is probably a GC point.  Branch instructions
             * only qualify if they go backward, so we need to check the
             * offset.
             */
            int offset = -1;
            bool unused;
            if (dvmGetBranchTarget(meth, insnFlags, i, &offset, &unused)) {
                if (offset < 0) {
                    dvmInsnSetGcPoint(insnFlags, i, true);
                }
            } else {
                /* not a branch target */
                dvmInsnSetGcPoint(insnFlags, i, true);
            }
        }

        switch (opcode) {
        case OP_NOP:
            /* plain no-op or switch table data; nothing to do here */
            break;

        case OP_CONST_STRING:
        case OP_CONST_STRING_JUMBO:
            if (!checkStringIndex(meth, i))
                return false;
            break;

        case OP_CONST_CLASS:
        case OP_CHECK_CAST:
            if (!checkTypeIndex(meth, i, true))
                return false;
            break;
        case OP_INSTANCE_OF:
            if (!checkTypeIndex(meth, i, false))
                return false;
            break;

        case OP_PACKED_SWITCH:
        case OP_SPARSE_SWITCH:
            /* verify the associated table */
            if (!dvmCheckSwitchTargets(meth, insnFlags, i))
                return false;
            break;

        case OP_FILL_ARRAY_DATA:
            /* verify the associated table */
            if (!checkArrayData(meth, i))
                return false;
            break;

        case OP_GOTO:
        case OP_GOTO_16:
        case OP_IF_EQ:
        case OP_IF_NE:
        case OP_IF_LT:
        case OP_IF_GE:
        case OP_IF_GT:
        case OP_IF_LE:
        case OP_IF_EQZ:
        case OP_IF_NEZ:
        case OP_IF_LTZ:
        case OP_IF_GEZ:
        case OP_IF_GTZ:
        case OP_IF_LEZ:
            /* check the destination */
            if (!dvmCheckBranchTarget(meth, insnFlags, i, false))
                return false;
            break;
        case OP_GOTO_32:
            /* check the destination; self-branch is okay */
            if (!dvmCheckBranchTarget(meth, insnFlags, i, true))
                return false;
            break;

        case OP_NEW_INSTANCE:
            if (!checkNewInstance(meth, i))
                return false;
            break;

        case OP_NEW_ARRAY:
            if (!checkNewArray(meth, i))
                return false;
            break;

        case OP_FILLED_NEW_ARRAY:
            if (!checkTypeIndex(meth, i, true))
                return false;
            break;
        case OP_FILLED_NEW_ARRAY_RANGE:
            if (!checkTypeIndex(meth, i, true))
                return false;
            break;

        case OP_IGET:
        case OP_IGET_WIDE:
        case OP_IGET_OBJECT:
        case OP_IGET_BOOLEAN:
        case OP_IGET_BYTE:
        case OP_IGET_CHAR:
        case OP_IGET_SHORT:
        case OP_IPUT:
        case OP_IPUT_WIDE:
        case OP_IPUT_OBJECT:
        case OP_IPUT_BOOLEAN:
        case OP_IPUT_BYTE:
        case OP_IPUT_CHAR:
        case OP_IPUT_SHORT:
            /* check the field index */
            if (!checkFieldIndex(meth, i, false))
                return false;
            break;
        case OP_SGET:
        case OP_SGET_WIDE:
        case OP_SGET_OBJECT:
        case OP_SGET_BOOLEAN:
        case OP_SGET_BYTE:
        case OP_SGET_CHAR:
        case OP_SGET_SHORT:
        case OP_SPUT:
        case OP_SPUT_WIDE:
        case OP_SPUT_OBJECT:
        case OP_SPUT_BOOLEAN:
        case OP_SPUT_BYTE:
        case OP_SPUT_CHAR:
        case OP_SPUT_SHORT:
            /* check the field index */
            if (!checkFieldIndex(meth, i, true))
                return false;
            break;

        case OP_INVOKE_VIRTUAL:
        case OP_INVOKE_SUPER:
        case OP_INVOKE_DIRECT:
        case OP_INVOKE_STATIC:
        case OP_INVOKE_INTERFACE:
        case OP_INVOKE_VIRTUAL_RANGE:
        case OP_INVOKE_SUPER_RANGE:
        case OP_INVOKE_DIRECT_RANGE:
        case OP_INVOKE_STATIC_RANGE:
        case OP_INVOKE_INTERFACE_RANGE:
            /* check the method index */
            if (!checkMethodIndex(meth, i))
                return false;
            break;

        case OP_EXECUTE_INLINE:
        case OP_INVOKE_DIRECT_EMPTY:
        case OP_IGET_QUICK:
        case OP_IGET_WIDE_QUICK:
        case OP_IGET_OBJECT_QUICK:
        case OP_IPUT_QUICK:
        case OP_IPUT_WIDE_QUICK:
        case OP_IPUT_OBJECT_QUICK:
        case OP_INVOKE_VIRTUAL_QUICK:
        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
        case OP_INVOKE_SUPER_QUICK:
        case OP_INVOKE_SUPER_QUICK_RANGE:
            if ((verifyFlags & VERIFY_ALLOW_OPT_INSTRS) == 0) {
                LOG_VFY("VFY: not expecting optimized instructions\n");
                return false;
            }
            break;

        default:
            /* nothing to do */
            break;
        }

        assert(width > 0);
        i += width;
        insns += width;
    }

    /* make sure the last instruction ends at the end of the insn area */
    if (i != insnCount) {
        LOG_VFY_METH(meth,
            "VFY: code did not end when expected (end at %d, count %d)\n",
            i, insnCount);
        return false;
    }

    return true;
}