Esempio n. 1
0
/*
Perform static checks on a "new-array" instruction.  Specifically, make
sure they aren't creating an array of arrays that causes the number of
dimensions to exceed 255.

对“new-instance”指令执行静态检查。具体来说,确保不会创建数组的维数超过255的数组
*/
static bool checkNewArray(const DvmDex* pDvmDex, u4 idx)
{
    const char* classDescriptor;

    if (idx >= pDvmDex->pHeader->typeIdsSize) {
        LOG_VFY("VFY: bad type index %d (max %d)",
            idx, pDvmDex->pHeader->typeIdsSize);
        return false;
    }

    classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);

    int bracketCount = 0;
    const char* cp = classDescriptor;
    while (*cp++ == '[')
        bracketCount++;

    if (bracketCount == 0) {
        /* The given class must be an array type. */
        LOG_VFY("VFY: can't new-array class '%s' (not an array)",
            classDescriptor);
        return false;
    } else if (bracketCount > 255) {
        /* It is illegal to create an array of more than 255 dimensions. */
        LOG_VFY("VFY: can't new-array class '%s' (exceeds limit)",
            classDescriptor);
        return false;
    }

    return true;
}
Esempio n. 2
0
/*
Verify a class.

By the time we get here, the value of gDvm.classVerifyMode should already
have been factored in.  If you want to call into the verifier even
though verification is disabled, that's your business.

Returns "true" on success.

校验类

当调用到这个方法,gDvm.classVerifyMode类校验模式的值应该已经被做过处理。
虽然验证验证已被禁用,如果想要调用到校验器,做自己的处理实现。
*/
bool dvmVerifyClass(ClassObject* clazz)
{
    int i;
		
		/*
		根据类状态判断是否进过校验,不作双重校验
		*/
    if (dvmIsClassVerified(clazz)) {
        ALOGD("Ignoring duplicate verify attempt on %s", clazz->descriptor);
        return true;
    }

		/*
		校验类实例的所有直接方法
		*/
    for (i = 0; i < clazz->directMethodCount; i++) {
        if (!verifyMethod(&clazz->directMethods[i])) {
            LOG_VFY("Verifier rejected class %s", clazz->descriptor);
            return false;
        }
    }
		/*
		校验类实例的所有虚方法
		*/    
    for (i = 0; i < clazz->virtualMethodCount; i++) {
        if (!verifyMethod(&clazz->virtualMethods[i])) {
            LOG_VFY("Verifier rejected class %s", clazz->descriptor);
            return false;
        }
    }

    return true;
}
Esempio n. 3
0
/*
 * Verify a class.
 *
 * By the time we get here, the value of gDvm.classVerifyMode should already
 * have been factored in.  If you want to call into the verifier even
 * though verification is disabled, that's your business.
 *
 * Returns "true" on success.
 */
bool dvmVerifyClass(ClassObject* clazz, int verifyFlags)
{
    int i;

    if (dvmIsClassVerified(clazz)) {
        LOGD("Ignoring duplicate verify attempt on %s\n", clazz->descriptor);
        return true;
    }

    //LOGI("Verify1 '%s'\n", clazz->descriptor);

    // TODO - verify class structure in DEX?

    for (i = 0; i < clazz->directMethodCount; i++) {
        if (!verifyMethod(&clazz->directMethods[i], verifyFlags)) {
            LOG_VFY("Verifier rejected class %s\n", clazz->descriptor);
            return false;
        }
    }
    for (i = 0; i < clazz->virtualMethodCount; i++) {
        if (!verifyMethod(&clazz->virtualMethods[i], verifyFlags)) {
            LOG_VFY("Verifier rejected class %s\n", clazz->descriptor);
            return false;
        }
    }

    return true;
}
Esempio n. 4
0
/*
Ensure that the wide register index is valid for this method.

确保方法的宽寄存器索引有效。
*/
static bool checkWideRegisterIndex(const Method* meth, u4 idx)
{
    if (idx+1 >= meth->registersSize) {
        LOG_VFY("VFY: wide register index out of range (%d+1 >= %d)",
            idx, meth->registersSize);
        return false;
    }
    return true;
}
Esempio n. 5
0
/*
Ensure that the string index is in the valid range.

确保字符串索引在有效范围内。
*/
static bool checkStringIndex(const DvmDex* pDvmDex, u4 idx)
{
    if (idx >= pDvmDex->pHeader->stringIdsSize) {
        LOG_VFY("VFY: bad string index %d (max %d)",
            idx, pDvmDex->pHeader->stringIdsSize);
        return false;
    }
    return true;
}
Esempio n. 6
0
/*
Perform static checks on a method invocation instruction.  All we do
here is ensure that the method index is in the valid range.

对一个方法反射指令做静态检查。
这里做的所有工作是为了确保方法索引在有效范围内。
*/
static bool checkMethodIndex(const DvmDex* pDvmDex, u4 idx)
{
    if (idx >= pDvmDex->pHeader->methodIdsSize) {
        LOG_VFY("VFY: bad method index %d (max %d)",
            idx, pDvmDex->pHeader->methodIdsSize);
        return false;
    }
    return true;
}
Esempio n. 7
0
/*
Perform static checks on an instruction that takes a class constant.
Ensure that the class index is in the valid range.

NOTE TODO:
*/
static bool checkTypeIndex(const DvmDex* pDvmDex, u4 idx)
{
    if (idx >= pDvmDex->pHeader->typeIdsSize) {
        LOG_VFY("VFY: bad type index %d (max %d)",
            idx, pDvmDex->pHeader->typeIdsSize);
        return false;
    }
    return true;
}
Esempio n. 8
0
/*
Perform static checks on a "new-instance" instruction.  Specifically,
make sure the class reference isn't for an array class.

We don't need the actual class, just a pointer to the class name.

对“new-instance”指令执行静态检查。具体来说,确保类引用不是一个数组类。

不需要真实的类,只是类名指针就可以。
*/
static bool checkNewInstance(const DvmDex* pDvmDex, u4 idx)
{
    const char* classDescriptor;

    if (idx >= pDvmDex->pHeader->typeIdsSize) {
        LOG_VFY("VFY: bad type index %d (max %d)",
            idx, pDvmDex->pHeader->typeIdsSize);
        return false;
    }

    classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);
    if (classDescriptor[0] != 'L') {
        LOG_VFY("VFY: can't call new-instance on type '%s'",
            classDescriptor);
        return false;
    }

    return true;
}
Esempio n. 9
0
/*
Check the register indices used in a "vararg" instruction, such as
invoke-virtual or filled-new-array.

检查用于“vararg”指令的寄存器标记,例如:invoke-virtual 或 filled-new-array

vA holds word count (0-5), args[] have values.

NOTE TODO:

There are some tests we don't do here, e.g. we don't try to verify
that invoking a method that takes a double is done with consecutive
registers.  This requires parsing the target method signature, which
we will be doing later on during the code flow analysis.

NOTE TODO:
*/
static bool checkVarargRegs(const Method* meth,
    const DecodedInstruction* pDecInsn)
{
    u2 registersSize = meth->registersSize;
    unsigned int idx;

    if (pDecInsn->vA > 5) {
        LOG_VFY("VFY: invalid arg count (%d) in non-range invoke)",
            pDecInsn->vA);
        return false;
    }

    for (idx = 0; idx < pDecInsn->vA; idx++) {
        if (pDecInsn->arg[idx] > registersSize) {
            LOG_VFY("VFY: invalid reg index (%d) in non-range invoke (> %d)",
                pDecInsn->arg[idx], registersSize);
            return false;
        }
    }

    return true;
}
Esempio n. 10
0
/*
Check the register indices used in a "vararg/range" instruction, such as
invoke-virtual/range or filled-new-array/range.

vA holds word count, vC holds index of first reg.

检查寄存器索引

NOTE TODO:
*/
static bool checkVarargRangeRegs(const Method* meth,
    const DecodedInstruction* pDecInsn)
{
    u2 registersSize = meth->registersSize;

    /*
     * vA/vC are unsigned 8-bit/16-bit quantities for /range instructions,
     * so there's no risk of integer overflow when adding them here.
     */
    if (pDecInsn->vA + pDecInsn->vC > registersSize) {
        LOG_VFY("VFY: invalid reg index %d+%d in range invoke (> %d)",
            pDecInsn->vA, pDecInsn->vC, registersSize);
        return false;
    }

    return true;
}
Esempio n. 11
0
/*
Verify a switch table.  "curOffset" is the offset of the switch
instruction.

Updates "insnFlags", setting the "branch target" flag.

校验一个分支table。“curOffset”是分支指令偏移。

更新“insnFlags”,设置“branch target“”标识。
*/
static bool checkSwitchTargets(const Method* meth, InsnFlags* insnFlags,
    u4 curOffset)
{
    const u4 insnCount = dvmGetMethodInsnsSize(meth);
    const u2* insns = meth->insns + curOffset;
    const u2* switchInsns;
    u2 expectedSignature;
    u4 switchCount, tableSize;
    s4 offsetToSwitch, offsetToKeys, offsetToTargets;
    s4 offset, absOffset;
    u4 targ;

    assert(curOffset < insnCount);

    /* make sure the start of the switch is in range */
    offsetToSwitch = insns[1] | ((s4) insns[2]) << 16;
    if ((s4) curOffset + offsetToSwitch < 0 ||
        curOffset + offsetToSwitch + 2 >= insnCount)
    {
        LOG_VFY("VFY: invalid switch start: at %d, switch offset %d, "
                "count %d",
            curOffset, offsetToSwitch, insnCount);
        return false;
    }

    /* offset to switch table is a relative branch-style offset */
    switchInsns = insns + offsetToSwitch;

    /* make sure the table is 32-bit aligned */
    if ((((u4) switchInsns) & 0x03) != 0) {
        LOG_VFY("VFY: unaligned switch table: at %d, switch offset %d",
            curOffset, offsetToSwitch);
        return false;
    }

    switchCount = switchInsns[1];

    if ((*insns & 0xff) == OP_PACKED_SWITCH) {
        /* 0=sig, 1=count, 2/3=firstKey */
        offsetToTargets = 4;
        offsetToKeys = -1;
        expectedSignature = kPackedSwitchSignature;
    } else {
        /* 0=sig, 1=count, 2..count*2 = keys */
        offsetToKeys = 2;
        offsetToTargets = 2 + 2*switchCount;
        expectedSignature = kSparseSwitchSignature;
    }
    tableSize = offsetToTargets + switchCount*2;

    if (switchInsns[0] != expectedSignature) {
        LOG_VFY("VFY: wrong signature for switch table (0x%04x, wanted 0x%04x)",
            switchInsns[0], expectedSignature);
        return false;
    }

    /* make sure the end of the switch is in range */
    if (curOffset + offsetToSwitch + tableSize > (u4) insnCount) {
        LOG_VFY("VFY: invalid switch end: at %d, switch offset %d, end %d, "
                "count %d",
            curOffset, offsetToSwitch, curOffset + offsetToSwitch + tableSize,
            insnCount);
        return false;
    }

    /* for a sparse switch, verify the keys are in ascending order */
    if (offsetToKeys > 0 && switchCount > 1) {
        s4 lastKey;

        lastKey = switchInsns[offsetToKeys] |
                  (switchInsns[offsetToKeys+1] << 16);
        for (targ = 1; targ < switchCount; targ++) {
            s4 key = (s4) switchInsns[offsetToKeys + targ*2] |
                    (s4) (switchInsns[offsetToKeys + targ*2 +1] << 16);
            if (key <= lastKey) {
                LOG_VFY("VFY: invalid packed switch: last key=%d, this=%d",
                    lastKey, key);
                return false;
            }

            lastKey = key;
        }
    }

    /* verify each switch target */
    for (targ = 0; targ < switchCount; targ++) {
        offset = (s4) switchInsns[offsetToTargets + targ*2] |
                (s4) (switchInsns[offsetToTargets + targ*2 +1] << 16);
        absOffset = curOffset + offset;

        if (absOffset < 0 || absOffset >= (s4)insnCount ||
            !dvmInsnIsOpcode(insnFlags, absOffset))
        {
            LOG_VFY("VFY: invalid switch target %d (-> %#x) at %#x[%d]",
                offset, absOffset, curOffset, targ);
            return false;
        }
        dvmInsnSetBranchTarget(insnFlags, absOffset, true);
    }

    return true;
}
Esempio n. 12
0
/*
Verify an array data table.  "curOffset" is the offset of the
fill-array-data instruction.

校验一个数组数据表。“curOffset”是fill-array-data指令的偏移。
*/
static bool checkArrayData(const Method* meth, u4 curOffset)
{
    const u4 insnCount = dvmGetMethodInsnsSize(meth);
    const u2* insns = meth->insns + curOffset;
    const u2* arrayData;
    u4 valueCount, valueWidth, tableSize;
    s4 offsetToArrayData;

    assert(curOffset < insnCount);

    /* 
    make sure the start of the array data table is in range 
    
    确保数组数据table的起始在范围之内。
    */
    offsetToArrayData = insns[1] | (((s4)insns[2]) << 16);
    if ((s4)curOffset + offsetToArrayData < 0 ||
        curOffset + offsetToArrayData + 2 >= insnCount)
    {
        LOG_VFY("VFY: invalid array data start: at %d, data offset %d, "
                "count %d",
            curOffset, offsetToArrayData, insnCount);
        return false;
    }

    /* 
    offset to array data table is a relative branch-style offset 
    
    数组数据table的偏移是一个相对branch-style偏移
    */
    arrayData = insns + offsetToArrayData;

    /* 
    make sure the table is 32-bit aligned 
    
    确保table是32位对齐。
    */
    if ((((u4) arrayData) & 0x03) != 0) {
        LOG_VFY("VFY: unaligned array data table: at %d, data offset %d",
            curOffset, offsetToArrayData);
        return false;
    }

    valueWidth = arrayData[1];
    valueCount = *(u4*)(&arrayData[2]);

    tableSize = 4 + (valueWidth * valueCount + 1) / 2;

    /* 
    make sure the end of the switch is in range 
    
    确保switch尾部在范围内
    */
    if (curOffset + offsetToArrayData + tableSize > insnCount) {
        LOG_VFY("VFY: invalid array data end: at %d, data offset %d, end %d, "
                "count %d",
            curOffset, offsetToArrayData,
            curOffset + offsetToArrayData + tableSize, insnCount);
        return false;
    }

    return true;
}
Esempio n. 13
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;
}