diff options
author | Brian Carlstrom <bdc@google.com> | 2014-08-05 12:46:17 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2014-08-05 12:51:13 -0700 |
commit | 870b4f2d70d67d6dbb7d0881d101c61bed8caad2 (patch) | |
tree | 7487dad3970556a040f88a49852a3dc9ed19bebd /vm/analysis/DexVerify.cpp | |
parent | 76e15e367ae1189b6f641ba8d16ca92bd179dac0 (diff) | |
download | android_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.tar.gz android_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.tar.bz2 android_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.zip |
Dalvik is dead, long live Dalvik!
croot
cd dalvik
repo start dalvik-is-dead-long-live-dalvik .
repo sync -c .
git rm -r README.txt
git rm -r dexopt
git rm -r tools/deadcode.py
git rm -r tools/dex-preopt
git rm -r tools/dexcheck
git rm -r tools/gdbjithelper
git rm -r unit-tests
git rm -r vm
git checkout HEAD vm/Common.h (needed by libdex)
git checkout HEAD vm/DalvikVersion.h (needed by libdex)
git checkout HEAD vm/Profile.h (needed by dmtracedump)
git add Android.mk (after removing vm, dexopt, and unit-tests references)
git commit -a -m 'Dalvik is dead, long live Dalvik!'
Bug: 14298175
Change-Id: I9dd13053677629d13496d4238af4374452cda415
Diffstat (limited to 'vm/analysis/DexVerify.cpp')
-rw-r--r-- | vm/analysis/DexVerify.cpp | 1226 |
1 files changed, 0 insertions, 1226 deletions
diff --git a/vm/analysis/DexVerify.cpp b/vm/analysis/DexVerify.cpp deleted file mode 100644 index c47c30120..000000000 --- a/vm/analysis/DexVerify.cpp +++ /dev/null @@ -1,1226 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Dalvik classfile verification. This file contains the verifier entry - * points and the static constraint checks. - */ -#include "Dalvik.h" -#include "analysis/CodeVerify.h" -#include "libdex/DexCatch.h" - - -/* fwd */ -static bool verifyMethod(Method* meth); -static bool verifyInstructions(VerifierData* vdata); - - -/* - * 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 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; -} - - -/* - * Compute the width of the instruction at each address in the instruction - * stream, and store it in vdata->insnFlags. Addresses that are in the - * middle of an instruction, or that are part of switch table data, are not - * touched (so the caller should probably initialize "insnFlags" to zero). - * - * The "newInstanceCount" and "monitorEnterCount" fields in vdata are - * also set. - * - * Performs some static checks, notably: - * - opcode of first instruction begins at index 0 - * - only documented instructions may appear - * - each instruction follows the last - * - last byte of last instruction is at (code_length-1) - * - * Logs an error and returns "false" on failure. - */ -static bool computeWidthsAndCountOps(VerifierData* vdata) -{ - const Method* meth = vdata->method; - InsnFlags* insnFlags = vdata->insnFlags; - size_t insnCount = vdata->insnsSize; - const u2* insns = meth->insns; - bool result = false; - int newInstanceCount = 0; - int monitorEnterCount = 0; - int i; - - for (i = 0; i < (int) insnCount; /**/) { - size_t width = dexGetWidthFromInstruction(insns); - if (width == 0) { - LOG_VFY_METH(meth, "VFY: invalid instruction (0x%04x)", *insns); - goto bail; - } else if (width > 65535) { - LOG_VFY_METH(meth, - "VFY: warning: unusually large instr width (%d)", width); - } - - Opcode opcode = dexOpcodeFromCodeUnit(*insns); - if (opcode == OP_NEW_INSTANCE) - newInstanceCount++; - if (opcode == OP_MONITOR_ENTER) - monitorEnterCount++; - - insnFlags[i] |= width; - i += width; - insns += width; - } - if (i != (int) vdata->insnsSize) { - LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)", - i, dvmGetMethodInsnsSize(meth)); - goto bail; - } - - result = true; - vdata->newInstanceCount = newInstanceCount; - vdata->monitorEnterCount = monitorEnterCount; - -bail: - return result; -} - -/* - * Set the "in try" flags for all instructions protected by "try" statements. - * Also sets the "branch target" flags for exception handlers. - * - * Call this after widths have been set in "insnFlags". - * - * Returns "false" if something in the exception table looks fishy, but - * we're expecting the exception table to be somewhat sane. - */ -static bool scanTryCatchBlocks(const Method* meth, InsnFlags* insnFlags) -{ - u4 insnsSize = dvmGetMethodInsnsSize(meth); - const DexCode* pCode = dvmGetMethodCode(meth); - u4 triesSize = pCode->triesSize; - const DexTry* pTries; - u4 idx; - - if (triesSize == 0) { - return true; - } - - pTries = dexGetTries(pCode); - - for (idx = 0; idx < triesSize; idx++) { - const DexTry* pTry = &pTries[idx]; - u4 start = pTry->startAddr; - u4 end = start + pTry->insnCount; - u4 addr; - - if ((start >= end) || (start >= insnsSize) || (end > insnsSize)) { - LOG_VFY_METH(meth, - "VFY: bad exception entry: startAddr=%d endAddr=%d (size=%d)", - start, end, insnsSize); - return false; - } - - if (dvmInsnGetWidth(insnFlags, start) == 0) { - LOG_VFY_METH(meth, - "VFY: 'try' block starts inside an instruction (%d)", - start); - return false; - } - - for (addr = start; addr < end; - addr += dvmInsnGetWidth(insnFlags, addr)) - { - assert(dvmInsnGetWidth(insnFlags, addr) != 0); - dvmInsnSetInTry(insnFlags, addr, true); - } - } - - /* Iterate over each of the handlers to verify target addresses. */ - u4 handlersSize = dexGetHandlersSize(pCode); - u4 offset = dexGetFirstHandlerOffset(pCode); - for (idx = 0; idx < handlersSize; idx++) { - DexCatchIterator iterator; - dexCatchIteratorInit(&iterator, pCode, offset); - - for (;;) { - DexCatchHandler* handler = dexCatchIteratorNext(&iterator); - u4 addr; - - if (handler == NULL) { - break; - } - - addr = handler->address; - if (dvmInsnGetWidth(insnFlags, addr) == 0) { - LOG_VFY_METH(meth, - "VFY: exception handler starts at bad address (%d)", - addr); - return false; - } - - dvmInsnSetBranchTarget(insnFlags, addr, true); - } - - offset = dexCatchIteratorGetEndOffset(&iterator, pCode); - } - - return true; -} - -/* - * Perform verification on a single method. - * - * We do this in three passes: - * (1) Walk through all code units, determining instruction locations, - * widths, and other characteristics. - * (2) Walk through all code units, performing static checks on - * operands. - * (3) Iterate through the method, checking type safety and looking - * for code flow problems. - * - * Some checks may be bypassed depending on the verification mode. We can't - * turn this stuff off completely if we want to do "exact" GC. - * - * TODO: cite source? - * Confirmed here: - * - code array must not be empty - * - (N/A) code_length must be less than 65536 - * Confirmed by computeWidthsAndCountOps(): - * - opcode of first instruction begins at index 0 - * - only documented instructions may appear - * - each instruction follows the last - * - last byte of last instruction is at (code_length-1) - */ -static bool verifyMethod(Method* meth) -{ - bool result = false; - - /* - * Verifier state blob. Various values will be cached here so we - * can avoid expensive lookups and pass fewer arguments around. - */ - VerifierData vdata; -#if 1 // ndef NDEBUG - memset(&vdata, 0x99, sizeof(vdata)); -#endif - - vdata.method = meth; - vdata.insnsSize = dvmGetMethodInsnsSize(meth); - vdata.insnRegCount = meth->registersSize; - vdata.insnFlags = NULL; - vdata.uninitMap = NULL; - vdata.basicBlocks = NULL; - - /* - * If there aren't any instructions, make sure that's expected, then - * exit successfully. Note: for native methods, meth->insns gets set - * to a native function pointer on first call, so don't use that as - * an indicator. - */ - if (vdata.insnsSize == 0) { - if (!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth)) { - LOG_VFY_METH(meth, - "VFY: zero-length code in concrete non-native method"); - goto bail; - } - - goto success; - } - - /* - * Sanity-check the register counts. ins + locals = registers, so make - * sure that ins <= registers. - */ - if (meth->insSize > meth->registersSize) { - LOG_VFY_METH(meth, "VFY: bad register counts (ins=%d regs=%d)", - meth->insSize, meth->registersSize); - goto bail; - } - - /* - * Allocate and populate an array to hold instruction data. - * - * TODO: Consider keeping a reusable pre-allocated array sitting - * around for smaller methods. - */ - vdata.insnFlags = (InsnFlags*) calloc(vdata.insnsSize, sizeof(InsnFlags)); - if (vdata.insnFlags == NULL) - goto bail; - - /* - * Compute the width of each instruction and store the result in insnFlags. - * Count up the #of occurrences of certain opcodes while we're at it. - */ - if (!computeWidthsAndCountOps(&vdata)) - goto bail; - - /* - * Allocate a map to hold the classes of uninitialized instances. - */ - vdata.uninitMap = dvmCreateUninitInstanceMap(meth, vdata.insnFlags, - vdata.newInstanceCount); - if (vdata.uninitMap == NULL) - goto bail; - - /* - * Set the "in try" flags for all instructions guarded by a "try" block. - * Also sets the "branch target" flag on exception handlers. - */ - if (!scanTryCatchBlocks(meth, vdata.insnFlags)) - goto bail; - - /* - * Perform static instruction verification. Also sets the "branch - * target" flags. - */ - if (!verifyInstructions(&vdata)) - goto bail; - - /* - * Do code-flow analysis. - * - * We could probably skip this for a method with no registers, but - * that's so rare that there's little point in checking. - */ - if (!dvmVerifyCodeFlow(&vdata)) { - //ALOGD("+++ %s failed code flow", meth->name); - goto bail; - } - -success: - result = true; - -bail: - dvmFreeVfyBasicBlocks(&vdata); - dvmFreeUninitInstanceMap(vdata.uninitMap); - free(vdata.insnFlags); - return result; -} - - -/* - * Verify an array data table. "curOffset" is the offset of the - * fill-array-data instruction. - */ -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 */ - 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 */ - arrayData = insns + offsetToArrayData; - - /* make sure the table is 32-bit aligned */ - 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 */ - 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; -} - -/* - * 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. - */ -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; -} - -/* - * 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. - */ -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; -} - -/* - * Perform static checks on an instruction that takes a class constant. - * Ensure that the class index is in the valid range. - */ -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; -} - -/* - * Perform static checks on a field get or set instruction. All we do - * here is ensure that the field index is in the valid range. - */ -static bool checkFieldIndex(const DvmDex* pDvmDex, u4 idx) -{ - if (idx >= pDvmDex->pHeader->fieldIdsSize) { - LOG_VFY("VFY: bad field index %d (max %d)", - idx, pDvmDex->pHeader->fieldIdsSize); - return false; - } - return true; -} - -/* - * 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; -} - -/* - * 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; -} - -/* - * Ensure that the register index is valid for this method. - */ -static bool checkRegisterIndex(const Method* meth, u4 idx) -{ - if (idx >= meth->registersSize) { - LOG_VFY("VFY: register index out of range (%d >= %d)", - idx, meth->registersSize); - return false; - } - return true; -} - -/* - * 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; -} - -/* - * Check the register indices used in a "vararg" instruction, such as - * invoke-virtual or filled-new-array. - * - * vA holds word count (0-5), args[] have values. - * - * 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. - */ -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; -} - -/* - * 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. - */ -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; -} - -/* - * Verify a switch table. "curOffset" is the offset of the switch - * instruction. - * - * Updates "insnFlags", setting the "branch target" flag. - */ -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; -} - -/* - * Verify that the target of a branch instruction is valid. - * - * We don't expect code to jump directly into an exception handler, but - * it's valid to do so as long as the target isn't a "move-exception" - * instruction. We verify that in a later stage. - * - * The VM spec doesn't forbid an instruction from branching to itself, - * but the Dalvik spec declares that only certain instructions can do so. - * - * Updates "insnFlags", setting the "branch target" flag. - */ -static bool checkBranchTarget(const Method* meth, InsnFlags* insnFlags, - int curOffset, bool selfOkay) -{ - const int insnCount = dvmGetMethodInsnsSize(meth); - s4 offset, absOffset; - bool isConditional; - - if (!dvmGetBranchOffset(meth, insnFlags, curOffset, &offset, - &isConditional)) - return false; - - if (!selfOkay && offset == 0) { - LOG_VFY_METH(meth, "VFY: branch offset of zero not allowed at %#x", - curOffset); - return false; - } - - /* - * Check for 32-bit overflow. This isn't strictly necessary if we can - * depend on the VM to have identical "wrap-around" behavior, but - * it's unwise to depend on that. - */ - if (((s8) curOffset + (s8) offset) != (s8)(curOffset + offset)) { - LOG_VFY_METH(meth, "VFY: branch target overflow %#x +%d", - curOffset, offset); - return false; - } - absOffset = curOffset + offset; - if (absOffset < 0 || absOffset >= insnCount || - !dvmInsnIsOpcode(insnFlags, absOffset)) - { - LOG_VFY_METH(meth, - "VFY: invalid branch target %d (-> %#x) at %#x", - offset, absOffset, curOffset); - return false; - } - dvmInsnSetBranchTarget(insnFlags, absOffset, true); - - 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 - * - 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. - */ - 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; -} |