Home | History | Annotate | Download | only in analysis
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * Dalvik verification subroutines.
     19  */
     20 #include "Dalvik.h"
     21 #include "analysis/CodeVerify.h"
     22 #include "libdex/DexCatch.h"
     23 #include "libdex/InstrUtils.h"
     24 
     25 
     26 /*
     27  * Compute the width of the instruction at each address in the instruction
     28  * stream.  Addresses that are in the middle of an instruction, or that
     29  * are part of switch table data, are not set (so the caller should probably
     30  * initialize "insnFlags" to zero).
     31  *
     32  * If "pNewInstanceCount" is not NULL, it will be set to the number of
     33  * new-instance instructions in the method.
     34  *
     35  * Performs some static checks, notably:
     36  * - opcode of first instruction begins at index 0
     37  * - only documented instructions may appear
     38  * - each instruction follows the last
     39  * - last byte of last instruction is at (code_length-1)
     40  *
     41  * Logs an error and returns "false" on failure.
     42  */
     43 bool dvmComputeCodeWidths(const Method* meth, InsnFlags* insnFlags,
     44     int* pNewInstanceCount)
     45 {
     46     size_t insnCount = dvmGetMethodInsnsSize(meth);
     47     const u2* insns = meth->insns;
     48     bool result = false;
     49     int newInstanceCount = 0;
     50     int i;
     51 
     52 
     53     for (i = 0; i < (int) insnCount; /**/) {
     54         size_t width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
     55         if (width == 0) {
     56             LOG_VFY_METH(meth,
     57                 "VFY: invalid post-opt instruction (0x%04x)\n", *insns);
     58             goto bail;
     59         }
     60 
     61         if ((*insns & 0xff) == OP_NEW_INSTANCE)
     62             newInstanceCount++;
     63 
     64         if (width > 65535) {
     65             LOG_VFY_METH(meth, "VFY: insane width %d\n", width);
     66             goto bail;
     67         }
     68 
     69         insnFlags[i] |= width;
     70         i += width;
     71         insns += width;
     72     }
     73     if (i != (int) dvmGetMethodInsnsSize(meth)) {
     74         LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)\n",
     75             i, dvmGetMethodInsnsSize(meth));
     76         goto bail;
     77     }
     78 
     79     result = true;
     80     if (pNewInstanceCount != NULL)
     81         *pNewInstanceCount = newInstanceCount;
     82 
     83 bail:
     84     return result;
     85 }
     86 
     87 /*
     88  * Set the "in try" flags for all instructions protected by "try" statements.
     89  * Also sets the "branch target" flags for exception handlers.
     90  *
     91  * Call this after widths have been set in "insnFlags".
     92  *
     93  * Returns "false" if something in the exception table looks fishy, but
     94  * we're expecting the exception table to be somewhat sane.
     95  */
     96 bool dvmSetTryFlags(const Method* meth, InsnFlags* insnFlags)
     97 {
     98     u4 insnsSize = dvmGetMethodInsnsSize(meth);
     99     const DexCode* pCode = dvmGetMethodCode(meth);
    100     u4 triesSize = pCode->triesSize;
    101     const DexTry* pTries;
    102     u4 handlersSize;
    103     u4 offset;
    104     u4 i;
    105 
    106     if (triesSize == 0) {
    107         return true;
    108     }
    109 
    110     pTries = dexGetTries(pCode);
    111     handlersSize = dexGetHandlersSize(pCode);
    112 
    113     for (i = 0; i < triesSize; i++) {
    114         const DexTry* pTry = &pTries[i];
    115         u4 start = pTry->startAddr;
    116         u4 end = start + pTry->insnCount;
    117         u4 addr;
    118 
    119         if ((start >= end) || (start >= insnsSize) || (end > insnsSize)) {
    120             LOG_VFY_METH(meth,
    121                 "VFY: bad exception entry: startAddr=%d endAddr=%d (size=%d)\n",
    122                 start, end, insnsSize);
    123             return false;
    124         }
    125 
    126         if (dvmInsnGetWidth(insnFlags, start) == 0) {
    127             LOG_VFY_METH(meth,
    128                 "VFY: 'try' block starts inside an instruction (%d)\n",
    129                 start);
    130             return false;
    131         }
    132 
    133         for (addr = start; addr < end;
    134             addr += dvmInsnGetWidth(insnFlags, addr))
    135         {
    136             assert(dvmInsnGetWidth(insnFlags, addr) != 0);
    137             dvmInsnSetInTry(insnFlags, addr, true);
    138         }
    139     }
    140 
    141     /* Iterate over each of the handlers to verify target addresses. */
    142     offset = dexGetFirstHandlerOffset(pCode);
    143     for (i = 0; i < handlersSize; i++) {
    144         DexCatchIterator iterator;
    145         dexCatchIteratorInit(&iterator, pCode, offset);
    146 
    147         for (;;) {
    148             DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
    149             u4 addr;
    150 
    151             if (handler == NULL) {
    152                 break;
    153             }
    154 
    155             addr = handler->address;
    156             if (dvmInsnGetWidth(insnFlags, addr) == 0) {
    157                 LOG_VFY_METH(meth,
    158                     "VFY: exception handler starts at bad address (%d)\n",
    159                     addr);
    160                 return false;
    161             }
    162 
    163             dvmInsnSetBranchTarget(insnFlags, addr, true);
    164         }
    165 
    166         offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
    167     }
    168 
    169     return true;
    170 }
    171 
    172 /*
    173  * Verify a switch table.  "curOffset" is the offset of the switch
    174  * instruction.
    175  */
    176 bool dvmCheckSwitchTargets(const Method* meth, InsnFlags* insnFlags,
    177     int curOffset)
    178 {
    179     const s4 insnCount = dvmGetMethodInsnsSize(meth);
    180     const u2* insns = meth->insns + curOffset;
    181     const u2* switchInsns;
    182     u2 expectedSignature;
    183     u4 switchCount, tableSize;
    184     s4 offsetToSwitch, offsetToKeys, offsetToTargets;
    185     s4 offset, absOffset;
    186     u4 targ;
    187 
    188     assert(curOffset >= 0 && curOffset < insnCount);
    189 
    190     /* make sure the start of the switch is in range */
    191     offsetToSwitch = insns[1] | ((s4) insns[2]) << 16;
    192     if (curOffset + offsetToSwitch < 0 ||
    193         curOffset + offsetToSwitch + 2 >= insnCount)
    194     {
    195         LOG_VFY_METH(meth,
    196             "VFY: invalid switch start: at %d, switch offset %d, count %d\n",
    197             curOffset, offsetToSwitch, insnCount);
    198         return false;
    199     }
    200 
    201     /* offset to switch table is a relative branch-style offset */
    202     switchInsns = insns + offsetToSwitch;
    203 
    204     /* make sure the table is 32-bit aligned */
    205     if ((((u4) switchInsns) & 0x03) != 0) {
    206         LOG_VFY_METH(meth,
    207             "VFY: unaligned switch table: at %d, switch offset %d\n",
    208             curOffset, offsetToSwitch);
    209         return false;
    210     }
    211 
    212     switchCount = switchInsns[1];
    213 
    214     if ((*insns & 0xff) == OP_PACKED_SWITCH) {
    215         /* 0=sig, 1=count, 2/3=firstKey */
    216         offsetToTargets = 4;
    217         offsetToKeys = -1;
    218         expectedSignature = kPackedSwitchSignature;
    219     } else {
    220         /* 0=sig, 1=count, 2..count*2 = keys */
    221         offsetToKeys = 2;
    222         offsetToTargets = 2 + 2*switchCount;
    223         expectedSignature = kSparseSwitchSignature;
    224     }
    225     tableSize = offsetToTargets + switchCount*2;
    226 
    227     if (switchInsns[0] != expectedSignature) {
    228         LOG_VFY_METH(meth,
    229             "VFY: wrong signature for switch table (0x%04x, wanted 0x%04x)\n",
    230             switchInsns[0], expectedSignature);
    231         return false;
    232     }
    233 
    234     /* make sure the end of the switch is in range */
    235     if (curOffset + offsetToSwitch + tableSize > (u4) insnCount) {
    236         LOG_VFY_METH(meth,
    237             "VFY: invalid switch end: at %d, switch offset %d, end %d, count %d\n",
    238             curOffset, offsetToSwitch, curOffset + offsetToSwitch + tableSize,
    239             insnCount);
    240         return false;
    241     }
    242 
    243     /* for a sparse switch, verify the keys are in ascending order */
    244     if (offsetToKeys > 0 && switchCount > 1) {
    245         s4 lastKey;
    246 
    247         lastKey = switchInsns[offsetToKeys] |
    248                   (switchInsns[offsetToKeys+1] << 16);
    249         for (targ = 1; targ < switchCount; targ++) {
    250             s4 key = (s4) switchInsns[offsetToKeys + targ*2] |
    251                     (s4) (switchInsns[offsetToKeys + targ*2 +1] << 16);
    252             if (key <= lastKey) {
    253                 LOG_VFY_METH(meth,
    254                     "VFY: invalid packed switch: last key=%d, this=%d\n",
    255                     lastKey, key);
    256                 return false;
    257             }
    258 
    259             lastKey = key;
    260         }
    261     }
    262 
    263     /* verify each switch target */
    264     for (targ = 0; targ < switchCount; targ++) {
    265         offset = (s4) switchInsns[offsetToTargets + targ*2] |
    266                 (s4) (switchInsns[offsetToTargets + targ*2 +1] << 16);
    267         absOffset = curOffset + offset;
    268 
    269         if (absOffset < 0 || absOffset >= insnCount ||
    270             !dvmInsnIsOpcode(insnFlags, absOffset))
    271         {
    272             LOG_VFY_METH(meth,
    273                 "VFY: invalid switch target %d (-> 0x%x) at 0x%x[%d]\n",
    274                 offset, absOffset, curOffset, targ);
    275             return false;
    276         }
    277         dvmInsnSetBranchTarget(insnFlags, absOffset, true);
    278     }
    279 
    280     return true;
    281 }
    282 
    283 /*
    284  * Verify that the target of a branch instruction is valid.
    285  *
    286  * We don't expect code to jump directly into an exception handler, but
    287  * it's valid to do so as long as the target isn't a "move-exception"
    288  * instruction.  We verify that in a later stage.
    289  *
    290  * The VM spec doesn't forbid an instruction from branching to itself,
    291  * but the Dalvik spec declares that only certain instructions can do so.
    292  */
    293 bool dvmCheckBranchTarget(const Method* meth, InsnFlags* insnFlags,
    294     int curOffset, bool selfOkay)
    295 {
    296     const int insnCount = dvmGetMethodInsnsSize(meth);
    297     int offset, absOffset;
    298     bool isConditional;
    299 
    300     if (!dvmGetBranchTarget(meth, insnFlags, curOffset, &offset,
    301             &isConditional))
    302         return false;
    303 
    304     if (!selfOkay && offset == 0) {
    305         LOG_VFY_METH(meth, "VFY: branch offset of zero not allowed at 0x%x\n",
    306             curOffset);
    307         return false;
    308     }
    309 
    310     /*
    311      * Check for 32-bit overflow.  This isn't strictly necessary if we can
    312      * depend on the VM to have identical "wrap-around" behavior, but
    313      * it's unwise to depend on that.
    314      */
    315     if (((s8) curOffset + (s8) offset) != (s8)(curOffset + offset)) {
    316         LOG_VFY_METH(meth, "VFY: branch target overflow 0x%x +%d\n",
    317             curOffset, offset);
    318         return false;
    319     }
    320     absOffset = curOffset + offset;
    321     if (absOffset < 0 || absOffset >= insnCount ||
    322         !dvmInsnIsOpcode(insnFlags, absOffset))
    323     {
    324         LOG_VFY_METH(meth,
    325             "VFY: invalid branch target %d (-> 0x%x) at 0x%x\n",
    326             offset, absOffset, curOffset);
    327         return false;
    328     }
    329     dvmInsnSetBranchTarget(insnFlags, absOffset, true);
    330 
    331     return true;
    332 }
    333 
    334 
    335 /*
    336  * Output a code verifier warning message.  For the pre-verifier it's not
    337  * a big deal if something fails (and it may even be expected), but if
    338  * we're doing just-in-time verification it's significant.
    339  */
    340 void dvmLogVerifyFailure(const Method* meth, const char* format, ...)
    341 {
    342     va_list ap;
    343     int logLevel;
    344 
    345     if (gDvm.optimizing) {
    346         return;
    347         //logLevel = ANDROID_LOG_DEBUG;
    348     } else {
    349         logLevel = ANDROID_LOG_WARN;
    350     }
    351 
    352     va_start(ap, format);
    353     LOG_PRI_VA(logLevel, LOG_TAG, format, ap);
    354     if (meth != NULL) {
    355         char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
    356         LOG_PRI(logLevel, LOG_TAG, "VFY:  rejected %s.%s %s\n",
    357             meth->clazz->descriptor, meth->name, desc);
    358         free(desc);
    359     }
    360 }
    361 
    362 /*
    363  * Show a relatively human-readable message describing the failure to
    364  * resolve a class.
    365  *
    366  * TODO: this is somewhat misleading when resolution fails because of
    367  * illegal access rather than nonexistent class.
    368  */
    369 void dvmLogUnableToResolveClass(const char* missingClassDescr,
    370     const Method* meth)
    371 {
    372     if (gDvm.optimizing)
    373         return;
    374 
    375     char* dotMissingClass = dvmDescriptorToDot(missingClassDescr);
    376     char* dotFromClass = dvmDescriptorToDot(meth->clazz->descriptor);
    377     //char* methodDescr = dexProtoCopyMethodDescriptor(&meth->prototype);
    378 
    379     LOGE("Could not find class '%s', referenced from method %s.%s\n",
    380         dotMissingClass, dotFromClass, meth->name/*, methodDescr*/);
    381 
    382     free(dotMissingClass);
    383     free(dotFromClass);
    384     //free(methodDescr);
    385 }
    386 
    387 /*
    388  * Extract the relative offset from a branch instruction.
    389  *
    390  * Returns "false" on failure (e.g. this isn't a branch instruction).
    391  */
    392 bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
    393     int curOffset, int* pOffset, bool* pConditional)
    394 {
    395     const u2* insns = meth->insns + curOffset;
    396 
    397     switch (*insns & 0xff) {
    398     case OP_GOTO:
    399         *pOffset = ((s2) *insns) >> 8;
    400         *pConditional = false;
    401         break;
    402     case OP_GOTO_32:
    403         *pOffset = insns[1] | (((u4) insns[2]) << 16);
    404         *pConditional = false;
    405         break;
    406     case OP_GOTO_16:
    407         *pOffset = (s2) insns[1];
    408         *pConditional = false;
    409         break;
    410     case OP_IF_EQ:
    411     case OP_IF_NE:
    412     case OP_IF_LT:
    413     case OP_IF_GE:
    414     case OP_IF_GT:
    415     case OP_IF_LE:
    416     case OP_IF_EQZ:
    417     case OP_IF_NEZ:
    418     case OP_IF_LTZ:
    419     case OP_IF_GEZ:
    420     case OP_IF_GTZ:
    421     case OP_IF_LEZ:
    422         *pOffset = (s2) insns[1];
    423         *pConditional = true;
    424         break;
    425     default:
    426         return false;
    427         break;
    428     }
    429 
    430     return true;
    431 }
    432 
    433 /*
    434  * Given a 32-bit constant, return the most-restricted RegType enum entry
    435  * that can hold the value.
    436  */
    437 char dvmDetermineCat1Const(s4 value)
    438 {
    439     if (value < -32768)
    440         return kRegTypeInteger;
    441     else if (value < -128)
    442         return kRegTypeShort;
    443     else if (value < 0)
    444         return kRegTypeByte;
    445     else if (value == 0)
    446         return kRegTypeZero;
    447     else if (value == 1)
    448         return kRegTypeOne;
    449     else if (value < 128)
    450         return kRegTypePosByte;
    451     else if (value < 32768)
    452         return kRegTypePosShort;
    453     else if (value < 65536)
    454         return kRegTypeChar;
    455     else
    456         return kRegTypeInteger;
    457 }
    458