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