summaryrefslogtreecommitdiffstats
path: root/vm/analysis/DexVerify.cpp
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2014-08-05 12:46:17 -0700
committerBrian Carlstrom <bdc@google.com>2014-08-05 12:51:13 -0700
commit870b4f2d70d67d6dbb7d0881d101c61bed8caad2 (patch)
tree7487dad3970556a040f88a49852a3dc9ed19bebd /vm/analysis/DexVerify.cpp
parent76e15e367ae1189b6f641ba8d16ca92bd179dac0 (diff)
downloadandroid_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.cpp1226
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;
-}