• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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  * Verifier basic block functions.
19  */
20 #include "Dalvik.h"
21 #include "analysis/VfyBasicBlock.h"
22 #include "analysis/CodeVerify.h"
23 #include "analysis/VerifySubs.h"
24 #include "libdex/DexCatch.h"
25 #include "libdex/InstrUtils.h"
26 
27 
28 /*
29  * Extract the list of catch handlers from "pTry" into "addrBuf".
30  *
31  * Returns the size of the catch handler list.  If the return value
32  * exceeds "addrBufSize", the items at the end of the list will not be
33  * represented in the output array, and this function should be called
34  * again with a larger buffer.
35  */
extractCatchHandlers(const DexCode * pCode,const DexTry * pTry,u4 * addrBuf,size_t addrBufSize)36 static u4 extractCatchHandlers(const DexCode* pCode, const DexTry* pTry,
37     u4* addrBuf, size_t addrBufSize)
38 {
39     DexCatchIterator iterator;
40     unsigned int idx = 0;
41 
42     dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
43     while (true) {
44         DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
45         if (handler == NULL)
46             break;
47 
48         if (idx < addrBufSize) {
49             addrBuf[idx] = handler->address;
50         }
51         idx++;
52     }
53 
54     return idx;
55 }
56 
57 /*
58  * Returns "true" if the instruction represents a data chunk, such as a
59  * switch statement block.
60  */
isDataChunk(u2 insn)61 static bool isDataChunk(u2 insn)
62 {
63     return (insn == kPackedSwitchSignature ||
64             insn == kSparseSwitchSignature ||
65             insn == kArrayDataSignature);
66 }
67 
68 /*
69  * Alloc a basic block in the specified slot.  The storage will be
70  * initialized.
71  */
allocVfyBasicBlock(VerifierData * vdata,u4 idx)72 static VfyBasicBlock* allocVfyBasicBlock(VerifierData* vdata, u4 idx)
73 {
74     VfyBasicBlock* newBlock = (VfyBasicBlock*) calloc(1, sizeof(VfyBasicBlock));
75     if (newBlock == NULL)
76         return NULL;
77 
78     /*
79      * TODO: there is no good default size here -- the problem is that most
80      * addresses will only have one predecessor, but a fair number will
81      * have 10+, and a few will have 100+ (e.g. the synthetic "finally"
82      * in a large synchronized method).  We probably want to use a small
83      * base allocation (perhaps two) and then have the first overflow
84      * allocation jump dramatically (to 32 or thereabouts).
85      */
86     newBlock->predecessors = dvmPointerSetAlloc(32);
87     if (newBlock->predecessors == NULL) {
88         free(newBlock);
89         return NULL;
90     }
91 
92     newBlock->firstAddr = (u4) -1;      // DEBUG
93 
94     newBlock->liveRegs = dvmAllocBitVector(vdata->insnRegCount, false);
95     if (newBlock->liveRegs == NULL) {
96         dvmPointerSetFree(newBlock->predecessors);
97         free(newBlock);
98         return NULL;
99     }
100 
101     return newBlock;
102 }
103 
104 /*
105  * Add "curBlock" to the predecessor list in "targetIdx".
106  */
addToPredecessor(VerifierData * vdata,VfyBasicBlock * curBlock,u4 targetIdx)107 static bool addToPredecessor(VerifierData* vdata, VfyBasicBlock* curBlock,
108     u4 targetIdx)
109 {
110     assert(targetIdx < vdata->insnsSize);
111 
112     /*
113      * Allocate the target basic block if necessary.  This will happen
114      * on e.g. forward branches.
115      *
116      * We can't fill in all the fields, but that will happen automatically
117      * when we get to that part of the code.
118      */
119     VfyBasicBlock* targetBlock = vdata->basicBlocks[targetIdx];
120     if (targetBlock == NULL) {
121         targetBlock = allocVfyBasicBlock(vdata, targetIdx);
122         if (targetBlock == NULL)
123             return false;
124         vdata->basicBlocks[targetIdx] = targetBlock;
125     }
126 
127     PointerSet* preds = targetBlock->predecessors;
128     bool added = dvmPointerSetAddEntry(preds, curBlock);
129     if (!added) {
130         /*
131          * This happens sometimes for packed-switch instructions, where
132          * the same target address appears more than once.  Also, a
133          * (pointless) conditional branch to the next instruction will
134          * trip over this.
135          */
136         LOGV("ODD: point set for targ=0x%04x (%p) already had block "
137              "fir=0x%04x (%p)",
138             targetIdx, targetBlock, curBlock->firstAddr, curBlock);
139     }
140 
141     return true;
142 }
143 
144 /*
145  * Add ourselves to the predecessor list in all blocks we might transfer
146  * control to.
147  *
148  * There are four ways to proceed to a new instruction:
149  *  (1) continue to the following instruction
150  *  (2) [un]conditionally branch to a specific location
151  *  (3) conditionally branch through a "switch" statement
152  *  (4) throw an exception
153  *
154  * Returning from the method (via a return statement or an uncaught
155  * exception) are not interesting for liveness analysis.
156  */
setPredecessors(VerifierData * vdata,VfyBasicBlock * curBlock,u4 curIdx,OpcodeFlags opFlags,u4 nextIdx,u4 * handlerList,size_t numHandlers)157 static bool setPredecessors(VerifierData* vdata, VfyBasicBlock* curBlock,
158     u4 curIdx, OpcodeFlags opFlags, u4 nextIdx, u4* handlerList,
159     size_t numHandlers)
160 {
161     const InsnFlags* insnFlags = vdata->insnFlags;
162     const Method* meth = vdata->method;
163 
164     unsigned int handlerIdx;
165     for (handlerIdx = 0; handlerIdx < numHandlers; handlerIdx++) {
166         if (!addToPredecessor(vdata, curBlock, handlerList[handlerIdx]))
167             return false;
168     }
169 
170     if ((opFlags & kInstrCanContinue) != 0) {
171         if (!addToPredecessor(vdata, curBlock, nextIdx))
172             return false;
173     }
174     if ((opFlags & kInstrCanBranch) != 0) {
175         bool unused, gotBranch;
176         s4 branchOffset, absOffset;
177 
178         gotBranch = dvmGetBranchOffset(meth, insnFlags, curIdx,
179                 &branchOffset, &unused);
180         assert(gotBranch);
181         absOffset = curIdx + branchOffset;
182         assert(absOffset >= 0 && (u4) absOffset < vdata->insnsSize);
183 
184         if (!addToPredecessor(vdata, curBlock, absOffset))
185             return false;
186     }
187 
188     if ((opFlags & kInstrCanSwitch) != 0) {
189         const u2* curInsn = &meth->insns[curIdx];
190         const u2* dataPtr;
191 
192         /* these values have already been verified, so we can trust them */
193         s4 offsetToData = curInsn[1] | ((s4) curInsn[2]) << 16;
194         dataPtr = curInsn + offsetToData;
195 
196         /*
197          * dataPtr points to the start of the switch data.  The first
198          * item is the NOP+magic, the second is the number of entries in
199          * the switch table.
200          */
201         u2 switchCount = dataPtr[1];
202 
203         /*
204          * Skip past the ident field, size field, and the first_key field
205          * (for packed) or the key list (for sparse).
206          */
207         if (dexOpcodeFromCodeUnit(meth->insns[curIdx]) == OP_PACKED_SWITCH) {
208             dataPtr += 4;
209         } else {
210             assert(dexOpcodeFromCodeUnit(meth->insns[curIdx]) ==
211                     OP_SPARSE_SWITCH);
212             dataPtr += 2 + 2 * switchCount;
213         }
214 
215         u4 switchIdx;
216         for (switchIdx = 0; switchIdx < switchCount; switchIdx++) {
217             s4 offset, absOffset;
218 
219             offset = (s4) dataPtr[switchIdx*2] |
220                      (s4) (dataPtr[switchIdx*2 +1] << 16);
221             absOffset = curIdx + offset;
222             assert(absOffset >= 0 && (u4) absOffset < vdata->insnsSize);
223 
224             if (!addToPredecessor(vdata, curBlock, absOffset))
225                 return false;
226         }
227     }
228 
229     if (false) {
230         if (dvmPointerSetGetCount(curBlock->predecessors) > 256) {
231             LOGI("Lots of preds at 0x%04x in %s.%s:%s", curIdx,
232                 meth->clazz->descriptor, meth->name, meth->shorty);
233         }
234     }
235 
236     return true;
237 }
238 
239 /*
240  * Dump the contents of the basic blocks.
241  */
dumpBasicBlocks(const VerifierData * vdata)242 static void dumpBasicBlocks(const VerifierData* vdata)
243 {
244     char printBuf[256];
245     unsigned int idx;
246     int count;
247 
248     LOGI("Basic blocks for %s.%s:%s", vdata->method->clazz->descriptor,
249         vdata->method->name, vdata->method->shorty);
250     for (idx = 0; idx < vdata->insnsSize; idx++) {
251         VfyBasicBlock* block = vdata->basicBlocks[idx];
252         if (block == NULL)
253             continue;
254 
255         assert(block->firstAddr == idx);
256         count = snprintf(printBuf, sizeof(printBuf), " %04x-%04x ",
257             block->firstAddr, block->lastAddr);
258 
259         PointerSet* preds = block->predecessors;
260         size_t numPreds = dvmPointerSetGetCount(preds);
261 
262         if (numPreds > 0) {
263             count += snprintf(printBuf + count, sizeof(printBuf) - count,
264                     "preds:");
265 
266             unsigned int predIdx;
267             for (predIdx = 0; predIdx < numPreds; predIdx++) {
268                 if (count >= (int) sizeof(printBuf))
269                     break;
270                 const VfyBasicBlock* pred =
271                     (const VfyBasicBlock*) dvmPointerSetGetEntry(preds, predIdx);
272                 count += snprintf(printBuf + count, sizeof(printBuf) - count,
273                         "%04x(%p),", pred->firstAddr, pred);
274             }
275         } else {
276             count += snprintf(printBuf + count, sizeof(printBuf) - count,
277                     "(no preds)");
278         }
279 
280         printBuf[sizeof(printBuf)-2] = '!';
281         printBuf[sizeof(printBuf)-1] = '\0';
282 
283         LOGI("%s", printBuf);
284     }
285 
286     usleep(100 * 1000);      /* ugh...let logcat catch up */
287 }
288 
289 
290 /*
291  * Generate a list of basic blocks and related information.
292  *
293  * On success, returns "true" with vdata->basicBlocks initialized.
294  */
dvmComputeVfyBasicBlocks(VerifierData * vdata)295 bool dvmComputeVfyBasicBlocks(VerifierData* vdata)
296 {
297     const InsnFlags* insnFlags = vdata->insnFlags;
298     const Method* meth = vdata->method;
299     const u4 insnsSize = vdata->insnsSize;
300     const DexCode* pCode = dvmGetMethodCode(meth);
301     const DexTry* pTries = NULL;
302     const size_t kHandlerStackAllocSize = 16;   /* max seen so far is 7 */
303     u4 handlerAddrs[kHandlerStackAllocSize];
304     u4* handlerListAlloc = NULL;
305     u4* handlerList = NULL;
306     size_t numHandlers = 0;
307     u4 idx, blockStartAddr;
308     bool result = false;
309 
310     bool verbose = false; //dvmWantVerboseVerification(meth);
311     if (verbose) {
312         LOGI("Basic blocks for %s.%s:%s",
313             meth->clazz->descriptor, meth->name, meth->shorty);
314     }
315 
316     /*
317      * Allocate a data structure that allows us to map from an address to
318      * the corresponding basic block.  Initially all pointers are NULL.
319      * They are populated on demand as we proceed (either when we reach a
320      * new BB, or when we need to add an item to the predecessor list in
321      * a not-yet-reached BB).
322      *
323      * Only the first instruction in the block points to the BB structure;
324      * the rest remain NULL.
325      */
326     vdata->basicBlocks =
327         (VfyBasicBlock**) calloc(insnsSize, sizeof(VfyBasicBlock*));
328     if (vdata->basicBlocks == NULL)
329       return false;
330 
331     /*
332      * The "tries" list is a series of non-overlapping regions with a list
333      * of "catch" handlers.  Rather than do the "find a matching try block"
334      * computation at each step, we just walk the "try" list in parallel.
335      *
336      * Not all methods have "try" blocks.  If this one does, we init tryEnd
337      * to zero, so that the (exclusive bound) range check trips immediately.
338      */
339     u4 tryIndex = 0, tryStart = 0, tryEnd = 0;
340     if (pCode->triesSize != 0) {
341         pTries = dexGetTries(pCode);
342     }
343 
344     u4 debugBBIndex = 0;
345 
346     /*
347      * The address associated with a basic block is the start address.
348      */
349     blockStartAddr = 0;
350 
351     for (idx = 0; idx < insnsSize; ) {
352         /*
353          * Make sure we're pointing at the right "try" block.  It should
354          * not be possible to "jump over" a block, so if we're no longer
355          * in the correct one we can just advance to the next.
356          */
357         if (pTries != NULL && idx >= tryEnd) {
358             if (tryIndex == pCode->triesSize) {
359                 /* no more try blocks in this method */
360                 pTries = NULL;
361                 numHandlers = 0;
362             } else {
363                 /*
364                  * Extract the set of handlers.  We want to avoid doing
365                  * this for each block, so we copy them to local storage.
366                  * If it doesn't fit in the small stack area, we'll use
367                  * the heap instead.
368                  *
369                  * It's rare to encounter a method with more than half a
370                  * dozen possible handlers.
371                  */
372                 tryStart = pTries[tryIndex].startAddr;
373                 tryEnd = tryStart + pTries[tryIndex].insnCount;
374 
375                 if (handlerListAlloc != NULL) {
376                     free(handlerListAlloc);
377                     handlerListAlloc = NULL;
378                 }
379                 numHandlers = extractCatchHandlers(pCode, &pTries[tryIndex],
380                     handlerAddrs, kHandlerStackAllocSize);
381                 assert(numHandlers > 0);    // TODO make sure this is verified
382                 if (numHandlers <= kHandlerStackAllocSize) {
383                     handlerList = handlerAddrs;
384                 } else {
385                     LOGD("overflow, numHandlers=%d", numHandlers);
386                     handlerListAlloc = (u4*) malloc(sizeof(u4) * numHandlers);
387                     if (handlerListAlloc == NULL)
388                         return false;
389                     extractCatchHandlers(pCode, &pTries[tryIndex],
390                         handlerListAlloc, numHandlers);
391                     handlerList = handlerListAlloc;
392                 }
393 
394                 LOGV("+++ start=%x end=%x numHan=%d",
395                     tryStart, tryEnd, numHandlers);
396 
397                 tryIndex++;
398             }
399         }
400 
401         /*
402          * Check the current instruction, and possibly aspects of the
403          * next instruction, to see if this instruction ends the current
404          * basic block.
405          *
406          * Instructions that can throw only end the block if there is the
407          * possibility of a local handler catching the exception.
408          */
409         Opcode opcode = dexOpcodeFromCodeUnit(meth->insns[idx]);
410         OpcodeFlags opFlags = dexGetFlagsFromOpcode(opcode);
411         size_t nextIdx = idx + dexGetWidthFromInstruction(&meth->insns[idx]);
412         bool endBB = false;
413         bool ignoreInstr = false;
414 
415         if ((opFlags & kInstrCanContinue) == 0) {
416             /* does not continue */
417             endBB = true;
418         } else if ((opFlags & (kInstrCanBranch | kInstrCanSwitch)) != 0) {
419             /* conditionally branches elsewhere */
420             endBB = true;
421         } else if ((opFlags & kInstrCanThrow) != 0 &&
422                 dvmInsnIsInTry(insnFlags, idx))
423         {
424             /* throws an exception that might be caught locally */
425             endBB = true;
426         } else if (isDataChunk(meth->insns[idx])) {
427             /*
428              * If this is a data chunk (e.g. switch data) we want to skip
429              * over it entirely.  Set endBB so we don't carry this along as
430              * the start of a block, and ignoreInstr so we don't try to
431              * open a basic block for this instruction.
432              */
433             endBB = ignoreInstr = true;
434         } else if (dvmInsnIsBranchTarget(insnFlags, nextIdx)) {
435             /*
436              * We also need to end it if the next instruction is a branch
437              * target.  Note we've tagged exception catch blocks as such.
438              *
439              * If we're this far along in the "else" chain, we know that
440              * this isn't a data-chunk NOP, and control can continue to
441              * the next instruction, so we're okay examining "nextIdx".
442              */
443             assert(nextIdx < insnsSize);
444             endBB = true;
445         } else if (opcode == OP_NOP && isDataChunk(meth->insns[nextIdx])) {
446             /*
447              * Handle an odd special case: if this is NOP padding before a
448              * data chunk, also treat it as "ignore".  Otherwise it'll look
449              * like a block that starts and doesn't end.
450              */
451             endBB = ignoreInstr = true;
452         } else {
453             /* check: return ops should be caught by absence of can-continue */
454             assert((opFlags & kInstrCanReturn) == 0);
455         }
456 
457         if (verbose) {
458             char btc = dvmInsnIsBranchTarget(insnFlags, idx) ? '>' : ' ';
459             char tryc =
460                 (pTries != NULL && idx >= tryStart && idx < tryEnd) ? 't' : ' ';
461             bool startBB = (idx == blockStartAddr);
462             const char* startEnd;
463 
464 
465             if (ignoreInstr)
466                 startEnd = "IGNORE";
467             else if (startBB && endBB)
468                 startEnd = "START/END";
469             else if (startBB)
470                 startEnd = "START";
471             else if (endBB)
472                 startEnd = "END";
473             else
474                 startEnd = "-";
475 
476             LOGI("%04x: %c%c%s #%d", idx, tryc, btc, startEnd, debugBBIndex);
477 
478             if (pTries != NULL && idx == tryStart) {
479                 assert(numHandlers > 0);
480                 LOGI("  EXC block: [%04x, %04x) %d:(%04x...)",
481                     tryStart, tryEnd, numHandlers, handlerList[0]);
482             }
483         }
484 
485         if (idx != blockStartAddr) {
486             /* should not be a basic block struct associated with this addr */
487             assert(vdata->basicBlocks[idx] == NULL);
488         }
489         if (endBB) {
490             if (!ignoreInstr) {
491                 /*
492                  * Create a new BB if one doesn't already exist.
493                  */
494                 VfyBasicBlock* curBlock = vdata->basicBlocks[blockStartAddr];
495                 if (curBlock == NULL) {
496                     curBlock = allocVfyBasicBlock(vdata, blockStartAddr);
497                     if (curBlock == NULL)
498                         return false;
499                     vdata->basicBlocks[blockStartAddr] = curBlock;
500                 }
501 
502                 curBlock->firstAddr = blockStartAddr;
503                 curBlock->lastAddr = idx;
504 
505                 if (!setPredecessors(vdata, curBlock, idx, opFlags, nextIdx,
506                         handlerList, numHandlers))
507                 {
508                     goto bail;
509                 }
510             }
511 
512             blockStartAddr = nextIdx;
513             debugBBIndex++;
514         }
515 
516         idx = nextIdx;
517     }
518 
519     assert(idx == insnsSize);
520 
521     result = true;
522 
523     if (verbose)
524         dumpBasicBlocks(vdata);
525 
526 bail:
527     free(handlerListAlloc);
528     return result;
529 }
530 
531 /*
532  * Free the storage used by basic blocks.
533  */
dvmFreeVfyBasicBlocks(VerifierData * vdata)534 void dvmFreeVfyBasicBlocks(VerifierData* vdata)
535 {
536     unsigned int idx;
537 
538     if (vdata->basicBlocks == NULL)
539         return;
540 
541     for (idx = 0; idx < vdata->insnsSize; idx++) {
542         VfyBasicBlock* block = vdata->basicBlocks[idx];
543         if (block == NULL)
544             continue;
545 
546         dvmPointerSetFree(block->predecessors);
547         free(block);
548     }
549 }
550