/* 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; }
/* * 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; }