blob: 2285aef6cc8233b565c3f9ea724727de18468403 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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 *
Andy McFadden228a6b02010-05-04 15:02:32 -070035 * 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 *
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080041 * Logs an error and returns "false" on failure.
42 */
43bool dvmComputeCodeWidths(const Method* meth, InsnFlags* insnFlags,
44 int* pNewInstanceCount)
45{
Andy McFadden228a6b02010-05-04 15:02:32 -070046 size_t insnCount = dvmGetMethodInsnsSize(meth);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080047 const u2* insns = meth->insns;
48 bool result = false;
49 int newInstanceCount = 0;
50 int i;
51
52
Andy McFadden228a6b02010-05-04 15:02:32 -070053 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;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080059 }
60
Andy McFadden228a6b02010-05-04 15:02:32 -070061 if ((*insns & 0xff) == OP_NEW_INSTANCE)
62 newInstanceCount++;
63
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080064 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
83bail:
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 */
96bool dvmSetTryFlags(const Method* meth, InsnFlags* insnFlags)
97{
98 u4 insnsSize = dvmGetMethodInsnsSize(meth);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080099 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 */
176bool dvmCheckSwitchTargets(const Method* meth, InsnFlags* insnFlags,
177 int curOffset)
178{
179 const int insnCount = dvmGetMethodInsnsSize(meth);
180 const u2* insns = meth->insns + curOffset;
181 const u2* switchInsns;
182 u2 expectedSignature;
183 int switchCount, tableSize;
184 int offsetToSwitch, offsetToKeys, offsetToTargets, targ;
185 int offset, absOffset;
186
187 assert(curOffset >= 0 && curOffset < insnCount);
188
189 /* make sure the start of the switch is in range */
190 offsetToSwitch = (s2) insns[1];
191 if (curOffset + offsetToSwitch < 0 ||
192 curOffset + offsetToSwitch + 2 >= insnCount)
193 {
194 LOG_VFY_METH(meth,
195 "VFY: invalid switch start: at %d, switch offset %d, count %d\n",
196 curOffset, offsetToSwitch, insnCount);
197 return false;
198 }
199
200 /* offset to switch table is a relative branch-style offset */
201 switchInsns = insns + offsetToSwitch;
202
203 /* make sure the table is 32-bit aligned */
204 if ((((u4) switchInsns) & 0x03) != 0) {
205 LOG_VFY_METH(meth,
206 "VFY: unaligned switch table: at %d, switch offset %d\n",
207 curOffset, offsetToSwitch);
208 return false;
209 }
210
211 switchCount = switchInsns[1];
212
213 if ((*insns & 0xff) == OP_PACKED_SWITCH) {
214 /* 0=sig, 1=count, 2/3=firstKey */
215 offsetToTargets = 4;
216 offsetToKeys = -1;
217 expectedSignature = kPackedSwitchSignature;
218 } else {
219 /* 0=sig, 1=count, 2..count*2 = keys */
220 offsetToKeys = 2;
221 offsetToTargets = 2 + 2*switchCount;
222 expectedSignature = kSparseSwitchSignature;
223 }
224 tableSize = offsetToTargets + switchCount*2;
225
226 if (switchInsns[0] != expectedSignature) {
227 LOG_VFY_METH(meth,
228 "VFY: wrong signature for switch table (0x%04x, wanted 0x%04x)\n",
229 switchInsns[0], expectedSignature);
230 return false;
231 }
232
233 /* make sure the end of the switch is in range */
234 if (curOffset + offsetToSwitch + tableSize > insnCount) {
235 LOG_VFY_METH(meth,
236 "VFY: invalid switch end: at %d, switch offset %d, end %d, count %d\n",
237 curOffset, offsetToSwitch, curOffset + offsetToSwitch + tableSize,
238 insnCount);
239 return false;
240 }
241
242 /* for a sparse switch, verify the keys are in ascending order */
243 if (offsetToKeys > 0 && switchCount > 1) {
244 s4 lastKey;
245
246 lastKey = switchInsns[offsetToKeys] |
247 (switchInsns[offsetToKeys+1] << 16);
248 for (targ = 1; targ < switchCount; targ++) {
249 s4 key = (s4) switchInsns[offsetToKeys + targ*2] |
250 (s4) (switchInsns[offsetToKeys + targ*2 +1] << 16);
251 if (key <= lastKey) {
252 LOG_VFY_METH(meth,
253 "VFY: invalid packed switch: last key=%d, this=%d\n",
254 lastKey, key);
255 return false;
256 }
257
258 lastKey = key;
259 }
260 }
261
262 /* verify each switch target */
263 for (targ = 0; targ < switchCount; targ++) {
264 offset = (s4) switchInsns[offsetToTargets + targ*2] |
265 (s4) (switchInsns[offsetToTargets + targ*2 +1] << 16);
266 absOffset = curOffset + offset;
267
268 if (absOffset < 0 || absOffset >= insnCount ||
269 !dvmInsnIsOpcode(insnFlags, absOffset))
270 {
271 LOG_VFY_METH(meth,
272 "VFY: invalid switch target %d (-> 0x%x) at 0x%x[%d]\n",
273 offset, absOffset, curOffset, targ);
274 return false;
275 }
276 dvmInsnSetBranchTarget(insnFlags, absOffset, true);
277 }
278
279 return true;
280}
281
282/*
283 * Verify that the target of a branch instruction is valid.
284 *
285 * We don't expect code to jump directly into an exception handler, but
286 * it's valid to do so as long as the target isn't a "move-exception"
287 * instruction. We verify that in a later stage.
288 *
289 * The VM spec doesn't forbid an instruction from branching to itself,
290 * but the Dalvik spec declares that only certain instructions can do so.
291 */
292bool dvmCheckBranchTarget(const Method* meth, InsnFlags* insnFlags,
293 int curOffset, bool selfOkay)
294{
295 const int insnCount = dvmGetMethodInsnsSize(meth);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800296 int offset, absOffset;
297 bool isConditional;
298
299 if (!dvmGetBranchTarget(meth, insnFlags, curOffset, &offset,
300 &isConditional))
301 return false;
302
303 if (!selfOkay && offset == 0) {
304 LOG_VFY_METH(meth, "VFY: branch offset of zero not allowed at 0x%x\n",
305 curOffset);
306 return false;
307 }
308
309 /*
310 * Check for 32-bit overflow. This isn't strictly necessary if we can
311 * depend on the VM to have identical "wrap-around" behavior, but
312 * it's unwise to depend on that.
313 */
314 if (((s8) curOffset + (s8) offset) != (s8)(curOffset + offset)) {
315 LOG_VFY_METH(meth, "VFY: branch target overflow 0x%x +%d\n",
316 curOffset, offset);
317 return false;
318 }
319 absOffset = curOffset + offset;
320 if (absOffset < 0 || absOffset >= insnCount ||
321 !dvmInsnIsOpcode(insnFlags, absOffset))
322 {
323 LOG_VFY_METH(meth,
324 "VFY: invalid branch target %d (-> 0x%x) at 0x%x\n",
325 offset, absOffset, curOffset);
326 return false;
327 }
328 dvmInsnSetBranchTarget(insnFlags, absOffset, true);
329
330 return true;
331}
332
333
334/*
335 * Output a code verifier warning message. For the pre-verifier it's not
336 * a big deal if something fails (and it may even be expected), but if
337 * we're doing just-in-time verification it's significant.
338 */
339void dvmLogVerifyFailure(const Method* meth, const char* format, ...)
340{
341 va_list ap;
342 int logLevel;
343
344 if (gDvm.optimizing) {
345 return;
346 //logLevel = ANDROID_LOG_DEBUG;
347 } else {
348 logLevel = ANDROID_LOG_WARN;
349 }
350
351 va_start(ap, format);
352 LOG_PRI_VA(logLevel, LOG_TAG, format, ap);
353 if (meth != NULL) {
354 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
355 LOG_PRI(logLevel, LOG_TAG, "VFY: rejected %s.%s %s\n",
356 meth->clazz->descriptor, meth->name, desc);
357 free(desc);
358 }
359}
360
361/*
362 * Show a relatively human-readable message describing the failure to
363 * resolve a class.
364 *
365 * TODO: this is somewhat misleading when resolution fails because of
366 * illegal access rather than nonexistent class.
367 */
368void dvmLogUnableToResolveClass(const char* missingClassDescr,
369 const Method* meth)
370{
371 if (gDvm.optimizing)
372 return;
373
374 char* dotMissingClass = dvmDescriptorToDot(missingClassDescr);
375 char* dotFromClass = dvmDescriptorToDot(meth->clazz->descriptor);
376 //char* methodDescr = dexProtoCopyMethodDescriptor(&meth->prototype);
377
378 LOGE("Could not find class '%s', referenced from method %s.%s\n",
379 dotMissingClass, dotFromClass, meth->name/*, methodDescr*/);
380
381 free(dotMissingClass);
382 free(dotFromClass);
383 //free(methodDescr);
384}
385
386/*
387 * Extract the relative offset from a branch instruction.
388 *
389 * Returns "false" on failure (e.g. this isn't a branch instruction).
390 */
391bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
392 int curOffset, int* pOffset, bool* pConditional)
393{
394 const u2* insns = meth->insns + curOffset;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800395
396 switch (*insns & 0xff) {
397 case OP_GOTO:
398 *pOffset = ((s2) *insns) >> 8;
399 *pConditional = false;
400 break;
401 case OP_GOTO_32:
402 *pOffset = insns[1] | (((u4) insns[2]) << 16);
403 *pConditional = false;
404 break;
405 case OP_GOTO_16:
406 *pOffset = (s2) insns[1];
407 *pConditional = false;
408 break;
409 case OP_IF_EQ:
410 case OP_IF_NE:
411 case OP_IF_LT:
412 case OP_IF_GE:
413 case OP_IF_GT:
414 case OP_IF_LE:
415 case OP_IF_EQZ:
416 case OP_IF_NEZ:
417 case OP_IF_LTZ:
418 case OP_IF_GEZ:
419 case OP_IF_GTZ:
420 case OP_IF_LEZ:
421 *pOffset = (s2) insns[1];
422 *pConditional = true;
423 break;
424 default:
425 return false;
426 break;
427 }
428
429 return true;
430}
431
432/*
433 * Given a 32-bit constant, return the most-restricted RegType enum entry
434 * that can hold the value.
435 */
436char dvmDetermineCat1Const(s4 value)
437{
438 if (value < -32768)
439 return kRegTypeInteger;
440 else if (value < -128)
441 return kRegTypeShort;
442 else if (value < 0)
443 return kRegTypeByte;
444 else if (value == 0)
445 return kRegTypeZero;
446 else if (value == 1)
447 return kRegTypeOne;
448 else if (value < 128)
449 return kRegTypePosByte;
450 else if (value < 32768)
451 return kRegTypePosShort;
452 else if (value < 65536)
453 return kRegTypeChar;
454 else
455 return kRegTypeInteger;
456}