OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / dalvik / vm / analysis / VerifySubs.c
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 }