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 classfile verification.  This file contains the verifier entry
19 * points and the static constraint checks.
20 */
21#include "Dalvik.h"
22#include "analysis/CodeVerify.h"
23
24
25/* fwd */
26static bool verifyMethod(Method* meth, int verifyFlags);
27static bool verifyInstructions(const Method* meth, InsnFlags* insnFlags,
28    int verifyFlags);
29
30
31/*
32 * Initialize some things we need for verification.
33 */
34bool dvmVerificationStartup(void)
35{
36    gDvm.instrWidth = dexCreateInstrWidthTable();
37    gDvm.instrFormat = dexCreateInstrFormatTable();
38    gDvm.instrFlags = dexCreateInstrFlagsTable();
39    if (gDvm.instrWidth == NULL || gDvm.instrFormat == NULL ||
40        gDvm.instrFlags == NULL)
41    {
42        LOGE("Unable to create instruction tables\n");
43        return false;
44    }
45
46    return true;
47}
48
49/*
50 * Free up some things we needed for verification.
51 */
52void dvmVerificationShutdown(void)
53{
54    free(gDvm.instrWidth);
55    free(gDvm.instrFormat);
56    free(gDvm.instrFlags);
57}
58
59/*
60 * Induce verification on all classes loaded from this DEX file as part
61 * of pre-verification and optimization.  This is never called from a
62 * normally running VM.
63 *
64 * Returns "true" when all classes have been processed.
65 */
66bool dvmVerifyAllClasses(DexFile* pDexFile)
67{
68    u4 count = pDexFile->pHeader->classDefsSize;
69    u4 idx;
70
71    assert(gDvm.optimizing);
72
73    if (gDvm.classVerifyMode == VERIFY_MODE_NONE) {
74        LOGV("+++ verification is disabled, skipping all classes\n");
75        return true;
76    }
77    if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE &&
78        gDvm.optimizingBootstrapClass)
79    {
80        LOGV("+++ verification disabled for bootstrap classes\n");
81        return true;
82    }
83
84    for (idx = 0; idx < count; idx++) {
85        const DexClassDef* pClassDef;
86        const char* classDescriptor;
87        ClassObject* clazz;
88
89        pClassDef = dexGetClassDef(pDexFile, idx);
90        classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
91
92        /* all classes are loaded into the bootstrap class loader */
93        clazz = dvmLookupClass(classDescriptor, NULL, false);
94        if (clazz != NULL) {
95            if (clazz->pDvmDex->pDexFile != pDexFile) {
96                LOGD("DexOpt: not verifying '%s': multiple definitions\n",
97                    classDescriptor);
98            } else {
99                if (dvmVerifyClass(clazz, VERIFY_DEFAULT)) {
100                    assert((clazz->accessFlags & JAVA_FLAGS_MASK) ==
101                        pClassDef->accessFlags);
102                    ((DexClassDef*)pClassDef)->accessFlags |=
103                        CLASS_ISPREVERIFIED;
104                }
105                /* keep going even if one fails */
106            }
107        } else {
108            LOGV("DexOpt: +++  not verifying '%s'\n", classDescriptor);
109        }
110    }
111
112    return true;
113}
114
115/*
116 * Verify a class.
117 *
118 * By the time we get here, the value of gDvm.classVerifyMode should already
119 * have been factored in.  If you want to call into the verifier even
120 * though verification is disabled, that's your business.
121 *
122 * Returns "true" on success.
123 */
124bool dvmVerifyClass(ClassObject* clazz, int verifyFlags)
125{
126    int i;
127
128    if (dvmIsClassVerified(clazz)) {
129        LOGD("Ignoring duplicate verify attempt on %s\n", clazz->descriptor);
130        return true;
131    }
132
133    //LOGI("Verify1 '%s'\n", clazz->descriptor);
134
135    // TODO - verify class structure in DEX?
136
137    for (i = 0; i < clazz->directMethodCount; i++) {
138        if (!verifyMethod(&clazz->directMethods[i], verifyFlags)) {
139            LOG_VFY("Verifier rejected class %s\n", clazz->descriptor);
140            return false;
141        }
142    }
143    for (i = 0; i < clazz->virtualMethodCount; i++) {
144        if (!verifyMethod(&clazz->virtualMethods[i], verifyFlags)) {
145            LOG_VFY("Verifier rejected class %s\n", clazz->descriptor);
146            return false;
147        }
148    }
149
150    return true;
151}
152
153
154/*
155 * Perform verification on a single method.
156 *
157 * We do this in three passes:
158 *  (1) Walk through all code units, determining instruction lengths.
159 *  (2) Do static checks, including branch target and operand validation.
160 *  (3) Do structural checks, including data-flow analysis.
161 *
162 * Some checks may be bypassed depending on the verification mode.  We can't
163 * turn this stuff off completely if we want to do "exact" GC.
164 *
165 * - operands of getfield, putfield, getstatic, putstatic must be valid
166 * - operands of method invocation instructions must be valid
167 *
168 * - code array must not be empty
169 * - (N/A) code_length must be less than 65536
170 * - opcode of first instruction begins at index 0
171 * - only documented instructions may appear
172 * - each instruction follows the last
173 * - (below) last byte of last instruction is at (code_length-1)
174 */
175static bool verifyMethod(Method* meth, int verifyFlags)
176{
177    bool result = false;
178    UninitInstanceMap* uninitMap = NULL;
179    InsnFlags* insnFlags = NULL;
180    int i, newInstanceCount;
181
182    /*
183     * If there aren't any instructions, make sure that's expected, then
184     * exit successfully. Note: meth->insns gets set to a native function
185     * pointer on first call.
186     */
187    if (dvmGetMethodInsnsSize(meth) == 0) {
188        if (!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth)) {
189            LOG_VFY_METH(meth,
190                "VFY: zero-length code in concrete non-native method\n");
191            goto bail;
192        }
193
194        goto success;
195    }
196
197    /*
198     * Sanity-check the register counts.  ins + locals = registers, so make
199     * sure that ins <= registers.
200     */
201    if (meth->insSize > meth->registersSize) {
202        LOG_VFY_METH(meth, "VFY: bad register counts (ins=%d regs=%d)\n",
203            meth->insSize, meth->registersSize);
204        goto bail;
205    }
206
207    /*
208     * Allocate and populate an array to hold instruction data.
209     *
210     * TODO: Consider keeping a reusable pre-allocated array sitting
211     * around for smaller methods.
212     */
213    insnFlags = (InsnFlags*)
214        calloc(dvmGetMethodInsnsSize(meth), sizeof(InsnFlags));
215    if (insnFlags == NULL)
216        goto bail;
217
218    /*
219     * Compute the width of each instruction and store the result in insnFlags.
220     * Count up the #of occurrences of new-instance instructions while we're
221     * at it.
222     */
223    if (!dvmComputeCodeWidths(meth, insnFlags, &newInstanceCount))
224        goto bail;
225
226    /*
227     * Allocate a map to hold the classes of uninitialized instances.
228     */
229    uninitMap = dvmCreateUninitInstanceMap(meth, insnFlags, newInstanceCount);
230    if (uninitMap == NULL)
231        goto bail;
232
233    /*
234     * Set the "in try" flags for all instructions guarded by a "try" block.
235     */
236    if (!dvmSetTryFlags(meth, insnFlags))
237        goto bail;
238
239    /*
240     * Perform static instruction verification.
241     */
242    if (!verifyInstructions(meth, insnFlags, verifyFlags))
243        goto bail;
244
245    /*
246     * Do code-flow analysis.  Do this after verifying the branch targets
247     * so we don't need to worry about it here.
248     *
249     * If there are no registers, we don't need to do much in the way of
250     * analysis, but we still need to verify that nothing actually tries
251     * to use a register.
252     */
253    if (!dvmVerifyCodeFlow(meth, insnFlags, uninitMap)) {
254        //LOGD("+++ %s failed code flow\n", meth->name);
255        goto bail;
256    }
257
258success:
259    result = true;
260
261bail:
262    dvmFreeUninitInstanceMap(uninitMap);
263    free(insnFlags);
264    return result;
265}
266
267
268/*
269 * Verify an array data table.  "curOffset" is the offset of the fill-array-data
270 * instruction.
271 */
272static bool checkArrayData(const Method* meth, int curOffset)
273{
274    const int insnCount = dvmGetMethodInsnsSize(meth);
275    const u2* insns = meth->insns + curOffset;
276    const u2* arrayData;
277    int valueCount, valueWidth, tableSize;
278    int offsetToArrayData;
279
280    assert(curOffset >= 0 && curOffset < insnCount);
281
282    /* make sure the start of the array data table is in range */
283    offsetToArrayData = insns[1] | (((s4)insns[2]) << 16);
284    if (curOffset + offsetToArrayData < 0 ||
285        curOffset + offsetToArrayData + 2 >= insnCount)
286    {
287        LOG_VFY_METH(meth,
288            "VFY: invalid array data start: at %d, data offset %d, count %d\n",
289            curOffset, offsetToArrayData, insnCount);
290        return false;
291    }
292
293    /* offset to array data table is a relative branch-style offset */
294    arrayData = insns + offsetToArrayData;
295
296    /* make sure the table is 32-bit aligned */
297    if ((((u4) arrayData) & 0x03) != 0) {
298        LOG_VFY_METH(meth,
299            "VFY: unaligned array data table: at %d, data offset %d\n",
300            curOffset, offsetToArrayData);
301        return false;
302    }
303
304    valueWidth = arrayData[1];
305    valueCount = *(u4*)(&arrayData[2]);
306
307    tableSize = 4 + (valueWidth * valueCount + 1) / 2;
308
309    /* make sure the end of the switch is in range */
310    if (curOffset + offsetToArrayData + tableSize > insnCount) {
311        LOG_VFY_METH(meth,
312            "VFY: invalid array data end: at %d, data offset %d, end %d, "
313            "count %d\n",
314            curOffset, offsetToArrayData,
315            curOffset + offsetToArrayData + tableSize,
316            insnCount);
317        return false;
318    }
319
320    return true;
321}
322
323
324/*
325 * Decode the current instruction.
326 */
327static void decodeInstruction(const Method* meth, int insnIdx,
328    DecodedInstruction* pDecInsn)
329{
330    dexDecodeInstruction(gDvm.instrFormat, meth->insns + insnIdx, pDecInsn);
331}
332
333
334/*
335 * Perform static checks on a "new-instance" instruction.  Specifically,
336 * make sure the class reference isn't for an array class.
337 *
338 * We don't need the actual class, just a pointer to the class name.
339 */
340static bool checkNewInstance(const Method* meth, int insnIdx)
341{
342    DvmDex* pDvmDex = meth->clazz->pDvmDex;
343    DecodedInstruction decInsn;
344    const char* classDescriptor;
345    u4 idx;
346
347    decodeInstruction(meth, insnIdx, &decInsn);
348    idx = decInsn.vB;       // 2nd item
349    if (idx >= pDvmDex->pHeader->typeIdsSize) {
350        LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n",
351            idx, pDvmDex->pHeader->typeIdsSize);
352        return false;
353    }
354
355    classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);
356    if (classDescriptor[0] != 'L') {
357        LOG_VFY_METH(meth, "VFY: can't call new-instance on type '%s'\n",
358            classDescriptor);
359        return false;
360    }
361
362    return true;
363}
364
365/*
366 * Perform static checks on a "new-array" instruction.  Specifically, make
367 * sure they aren't creating an array of arrays that causes the number of
368 * dimensions to exceed 255.
369 */
370static bool checkNewArray(const Method* meth, int insnIdx)
371{
372    DvmDex* pDvmDex = meth->clazz->pDvmDex;
373    DecodedInstruction decInsn;
374    const char* classDescriptor;
375    u4 idx;
376
377    decodeInstruction(meth, insnIdx, &decInsn);
378    idx = decInsn.vC;       // 3rd item
379    if (idx >= pDvmDex->pHeader->typeIdsSize) {
380        LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n",
381            idx, pDvmDex->pHeader->typeIdsSize);
382        return false;
383    }
384
385    classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);
386
387    int bracketCount = 0;
388    const char* cp = classDescriptor;
389    while (*cp++ == '[')
390        bracketCount++;
391
392    if (bracketCount == 0) {
393        /* The given class must be an array type. */
394        LOG_VFY_METH(meth, "VFY: can't new-array class '%s' (not an array)\n",
395            classDescriptor);
396        return false;
397    } else if (bracketCount > 255) {
398        /* It is illegal to create an array of more than 255 dimensions. */
399        LOG_VFY_METH(meth, "VFY: can't new-array class '%s' (exceeds limit)\n",
400            classDescriptor);
401        return false;
402    }
403
404    return true;
405}
406
407/*
408 * Perform static checks on an instruction that takes a class constant.
409 * Ensure that the class index is in the valid range.
410 */
411static bool checkTypeIndex(const Method* meth, int insnIdx, bool useB)
412{
413    DvmDex* pDvmDex = meth->clazz->pDvmDex;
414    DecodedInstruction decInsn;
415    u4 idx;
416
417    decodeInstruction(meth, insnIdx, &decInsn);
418    if (useB)
419        idx = decInsn.vB;
420    else
421        idx = decInsn.vC;
422    if (idx >= pDvmDex->pHeader->typeIdsSize) {
423        LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n",
424            idx, pDvmDex->pHeader->typeIdsSize);
425        return false;
426    }
427
428    return true;
429}
430
431/*
432 * Perform static checks on a field get or set instruction.  All we do
433 * here is ensure that the field index is in the valid range.
434 */
435static bool checkFieldIndex(const Method* meth, int insnIdx, bool useB)
436{
437    DvmDex* pDvmDex = meth->clazz->pDvmDex;
438    DecodedInstruction decInsn;
439    u4 idx;
440
441    decodeInstruction(meth, insnIdx, &decInsn);
442    if (useB)
443        idx = decInsn.vB;
444    else
445        idx = decInsn.vC;
446    if (idx >= pDvmDex->pHeader->fieldIdsSize) {
447        LOG_VFY_METH(meth,
448            "VFY: bad field index %d (max %d) at offset 0x%04x\n",
449            idx, pDvmDex->pHeader->fieldIdsSize, insnIdx);
450        return false;
451    }
452
453    return true;
454}
455
456/*
457 * Perform static checks on a method invocation instruction.  All we do
458 * here is ensure that the method index is in the valid range.
459 */
460static bool checkMethodIndex(const Method* meth, int insnIdx)
461{
462    DvmDex* pDvmDex = meth->clazz->pDvmDex;
463    DecodedInstruction decInsn;
464
465    decodeInstruction(meth, insnIdx, &decInsn);
466    if (decInsn.vB >= pDvmDex->pHeader->methodIdsSize) {
467        LOG_VFY_METH(meth, "VFY: bad method index %d (max %d)\n",
468            decInsn.vB, pDvmDex->pHeader->methodIdsSize);
469        return false;
470    }
471
472    return true;
473}
474
475/*
476 * Perform static checks on a string constant instruction.  All we do
477 * here is ensure that the string index is in the valid range.
478 */
479static bool checkStringIndex(const Method* meth, int insnIdx)
480{
481    DvmDex* pDvmDex = meth->clazz->pDvmDex;
482    DecodedInstruction decInsn;
483
484    decodeInstruction(meth, insnIdx, &decInsn);
485    if (decInsn.vB >= pDvmDex->pHeader->stringIdsSize) {
486        LOG_VFY_METH(meth, "VFY: bad string index %d (max %d)\n",
487            decInsn.vB, pDvmDex->pHeader->stringIdsSize);
488        return false;
489    }
490
491    return true;
492}
493
494/*
495 * Perform static verification on instructions.
496 *
497 * As a side effect, this sets the "branch target" flags in InsnFlags.
498 *
499 * "(CF)" items are handled during code-flow analysis.
500 *
501 * v3 4.10.1
502 * - target of each jump and branch instruction must be valid
503 * - targets of switch statements must be valid
504 * - (CF) operands referencing constant pool entries must be valid
505 * - (CF) operands of getfield, putfield, getstatic, putstatic must be valid
506 * - (new) verify operands of "quick" field ops
507 * - (CF) operands of method invocation instructions must be valid
508 * - (new) verify operands of "quick" method invoke ops
509 * - (CF) only invoke-direct can call a method starting with '<'
510 * - (CF) <clinit> must never be called explicitly
511 * - (CF) operands of instanceof, checkcast, new (and variants) must be valid
512 * - new-array[-type] limited to 255 dimensions
513 * - can't use "new" on an array class
514 * - (?) limit dimensions in multi-array creation
515 * - (CF) local variable load/store register values must be in valid range
516 *
517 * v3 4.11.1.2
518 * - branches must be within the bounds of the code array
519 * - targets of all control-flow instructions are the start of an instruction
520 * - (CF) register accesses fall within range of allocated registers
521 * - (N/A) access to constant pool must be of appropriate type
522 * - (CF) code does not end in the middle of an instruction
523 * - (CF) execution cannot fall off the end of the code
524 * - (earlier) for each exception handler, the "try" area must begin and
525 *   end at the start of an instruction (end can be at the end of the code)
526 * - (earlier) for each exception handler, the handler must start at a valid
527 *   instruction
528 *
529 * TODO: move some of the "CF" items in here for better performance (the
530 * code-flow analysis sometimes has to process the same instruction several
531 * times).
532 */
533static bool verifyInstructions(const Method* meth, InsnFlags* insnFlags,
534    int verifyFlags)
535{
536    const int insnCount = dvmGetMethodInsnsSize(meth);
537    const u2* insns = meth->insns;
538    int i;
539
540    /* the start of the method is a "branch target" */
541    dvmInsnSetBranchTarget(insnFlags, 0, true);
542
543    for (i = 0; i < insnCount; /**/) {
544        /*
545         * These types of instructions can be GC points.  To support precise
546         * GC, all such instructions must export the PC in the interpreter,
547         * or the GC won't be able to identify the current PC for the thread.
548         */
549        static const int gcMask = kInstrCanBranch | kInstrCanSwitch |
550            kInstrCanThrow | kInstrCanReturn;
551
552        int width = dvmInsnGetWidth(insnFlags, i);
553        OpCode opcode = *insns & 0xff;
554        InstructionFlags opFlags = dexGetInstrFlags(gDvm.instrFlags, opcode);
555        int offset, absOffset;
556
557        if ((opFlags & gcMask) != 0) {
558            /*
559             * This instruction is probably a GC point.  Branch instructions
560             * only qualify if they go backward, so we need to check the
561             * offset.
562             */
563            int offset = -1;
564            bool unused;
565            if (dvmGetBranchTarget(meth, insnFlags, i, &offset, &unused)) {
566                if (offset < 0) {
567                    dvmInsnSetGcPoint(insnFlags, i, true);
568                }
569            } else {
570                /* not a branch target */
571                dvmInsnSetGcPoint(insnFlags, i, true);
572            }
573        }
574
575        switch (opcode) {
576        case OP_NOP:
577            /* plain no-op or switch table data; nothing to do here */
578            break;
579
580        case OP_CONST_STRING:
581        case OP_CONST_STRING_JUMBO:
582            if (!checkStringIndex(meth, i))
583                return false;
584            break;
585
586        case OP_CONST_CLASS:
587        case OP_CHECK_CAST:
588            if (!checkTypeIndex(meth, i, true))
589                return false;
590            break;
591        case OP_INSTANCE_OF:
592            if (!checkTypeIndex(meth, i, false))
593                return false;
594            break;
595
596        case OP_PACKED_SWITCH:
597        case OP_SPARSE_SWITCH:
598            /* verify the associated table */
599            if (!dvmCheckSwitchTargets(meth, insnFlags, i))
600                return false;
601            break;
602
603        case OP_FILL_ARRAY_DATA:
604            /* verify the associated table */
605            if (!checkArrayData(meth, i))
606                return false;
607            break;
608
609        case OP_GOTO:
610        case OP_GOTO_16:
611        case OP_IF_EQ:
612        case OP_IF_NE:
613        case OP_IF_LT:
614        case OP_IF_GE:
615        case OP_IF_GT:
616        case OP_IF_LE:
617        case OP_IF_EQZ:
618        case OP_IF_NEZ:
619        case OP_IF_LTZ:
620        case OP_IF_GEZ:
621        case OP_IF_GTZ:
622        case OP_IF_LEZ:
623            /* check the destination */
624            if (!dvmCheckBranchTarget(meth, insnFlags, i, false))
625                return false;
626            break;
627        case OP_GOTO_32:
628            /* check the destination; self-branch is okay */
629            if (!dvmCheckBranchTarget(meth, insnFlags, i, true))
630                return false;
631            break;
632
633        case OP_NEW_INSTANCE:
634            if (!checkNewInstance(meth, i))
635                return false;
636            break;
637
638        case OP_NEW_ARRAY:
639            if (!checkNewArray(meth, i))
640                return false;
641            break;
642
643        case OP_FILLED_NEW_ARRAY:
644            if (!checkTypeIndex(meth, i, true))
645                return false;
646            break;
647        case OP_FILLED_NEW_ARRAY_RANGE:
648            if (!checkTypeIndex(meth, i, true))
649                return false;
650            break;
651
652        case OP_IGET:
653        case OP_IGET_WIDE:
654        case OP_IGET_OBJECT:
655        case OP_IGET_BOOLEAN:
656        case OP_IGET_BYTE:
657        case OP_IGET_CHAR:
658        case OP_IGET_SHORT:
659        case OP_IPUT:
660        case OP_IPUT_WIDE:
661        case OP_IPUT_OBJECT:
662        case OP_IPUT_BOOLEAN:
663        case OP_IPUT_BYTE:
664        case OP_IPUT_CHAR:
665        case OP_IPUT_SHORT:
666            /* check the field index */
667            if (!checkFieldIndex(meth, i, false))
668                return false;
669            break;
670        case OP_SGET:
671        case OP_SGET_WIDE:
672        case OP_SGET_OBJECT:
673        case OP_SGET_BOOLEAN:
674        case OP_SGET_BYTE:
675        case OP_SGET_CHAR:
676        case OP_SGET_SHORT:
677        case OP_SPUT:
678        case OP_SPUT_WIDE:
679        case OP_SPUT_OBJECT:
680        case OP_SPUT_BOOLEAN:
681        case OP_SPUT_BYTE:
682        case OP_SPUT_CHAR:
683        case OP_SPUT_SHORT:
684            /* check the field index */
685            if (!checkFieldIndex(meth, i, true))
686                return false;
687            break;
688
689        case OP_INVOKE_VIRTUAL:
690        case OP_INVOKE_SUPER:
691        case OP_INVOKE_DIRECT:
692        case OP_INVOKE_STATIC:
693        case OP_INVOKE_INTERFACE:
694        case OP_INVOKE_VIRTUAL_RANGE:
695        case OP_INVOKE_SUPER_RANGE:
696        case OP_INVOKE_DIRECT_RANGE:
697        case OP_INVOKE_STATIC_RANGE:
698        case OP_INVOKE_INTERFACE_RANGE:
699            /* check the method index */
700            if (!checkMethodIndex(meth, i))
701                return false;
702            break;
703
704        case OP_EXECUTE_INLINE:
705        case OP_INVOKE_DIRECT_EMPTY:
706        case OP_IGET_QUICK:
707        case OP_IGET_WIDE_QUICK:
708        case OP_IGET_OBJECT_QUICK:
709        case OP_IPUT_QUICK:
710        case OP_IPUT_WIDE_QUICK:
711        case OP_IPUT_OBJECT_QUICK:
712        case OP_INVOKE_VIRTUAL_QUICK:
713        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
714        case OP_INVOKE_SUPER_QUICK:
715        case OP_INVOKE_SUPER_QUICK_RANGE:
716            if ((verifyFlags & VERIFY_ALLOW_OPT_INSTRS) == 0) {
717                LOG_VFY("VFY: not expecting optimized instructions\n");
718                return false;
719            }
720            break;
721
722        default:
723            /* nothing to do */
724            break;
725        }
726
727        assert(width > 0);
728        i += width;
729        insns += width;
730    }
731
732    /* make sure the last instruction ends at the end of the insn area */
733    if (i != insnCount) {
734        LOG_VFY_METH(meth,
735            "VFY: code did not end when expected (end at %d, count %d)\n",
736            i, insnCount);
737        return false;
738    }
739
740    return true;
741}
742
743