📄 verify.c
字号:
/* * verifyMethod3a() * check static constraints. section 4.8.1 of JVML Spec 2. * * NOTE: we don't check whether execution can fall off the end of method code here as * that would require us to know whether the last statements are reachable. * Sun's verifier, for instance, rejects code with an unreachable NOP at the end! * Thus we check whether execution can fall off the end during the data flow analysis * of pass 3b, structural constraint checking. */staticBlockInfo**verifyMethod3a(errorInfo* einfo, Method* method, uint32* status, /* array of status info for all opcodes */ uint32* numBlocks) /* number of basic blocks */{#define VERIFY_ERROR(_MSG) \ if (einfo->type == 0) { \ postExceptionMessage(einfo, JAVA_LANG(VerifyError), \ "in method \"%s.%s\": %s", \ CLASS_CNAME(method->class), METHOD_NAMED(method), _MSG); \ } \ return NULL#define ENSURE_NON_WIDE \ if (wide) { \ VERIFY_ERROR("illegal instruction following wide instruction"); \ }#define CHECK_POOL_IDX(_IDX) \ if (_IDX > pool->size) { \ VERIFY_ERROR("attempt to access a constant pool index beyond constant pool range"); \ } #define GET_IDX(_IDX, _PC) \ (_IDX) = (_PC) + 1; \ (_IDX) = code[_IDX]; \ CHECK_POOL_IDX(_IDX) #define GET_WIDX(_IDX, _PC) \ _IDX = (_PC) + 1; \ _IDX = WORD(code, _IDX); \ CHECK_POOL_IDX(_IDX)#define BRANCH_IN_BOUNDS(_N, _INST) \ if (_N < 0 || _N >= codelen) { \ DBG(VERIFY3, dprintf("ERROR: branch to (%d) out of bound (%d) \n", _N, codelen); ); \ VERIFY_ERROR("branch out of method code"); \ } /* makes sure the index given for a local variable is within the correct index */#define CHECK_LOCAL_INDEX(_N) \ if ((_N) >= method->localsz) { \ DBG(VERIFY3, \ dprintf("ERROR: pc = %d, instruction = ", pc); \ printInstruction(code[pc]); \ dprintf(", localsz = %d, localindex = %d\n", method->localsz, _N); \ ); \ VERIFY_ERROR("attempting to access a local variable beyond local array"); \ } constants* pool = CLASS_CONSTANTS(method->class); /* used for looking at method signatures... */ const char* sig; int codelen = METHOD_BYTECODE_LEN(method); unsigned char* code = METHOD_BYTECODE_CODE(method); uint32 pc = 0, newpc = 0, n = 0, idx = 0;#define NEXTPC (pc + insnLen[code[pc]]) int32 branchoffset = 0; int32 low, high; bool wide; bool inABlock; /* used when calculating the start/return address of each block */ uint32 blockCount = 0; BlockInfo** blocks = NULL; DBG(VERIFY3, dprintf(" Verifier Pass 3a: checking static constraints and finding basic blocks...\n"); ); /* find the start of every instruction and basic block to determine legal branches * * also, this makes sure that only legal instructions follow the WIDE instruction */ status[0] |= START_BLOCK; wide = false; pc = 0; while(pc < codelen) { status[pc] |= IS_INSTRUCTION; DBG(VERIFY3, dprintf(" instruction: (%d) ", pc); printInstruction(code[pc]); dprintf("\n"); ); if (codelen - pc < insnLen[code[pc]]) { VERIFY_ERROR("last operand in code array is cut off"); } switch(code[pc]) { case ALOAD_0: case ASTORE_0: case ILOAD_0: case ISTORE_0: case FLOAD_0: case FSTORE_0: ENSURE_NON_WIDE; CHECK_LOCAL_INDEX(0); break; case ALOAD_1: case ASTORE_1: case ILOAD_1: case ISTORE_1: case FLOAD_1: case FSTORE_1: case LLOAD_0: case LSTORE_0: case DLOAD_0: case DSTORE_0: ENSURE_NON_WIDE; CHECK_LOCAL_INDEX(1); break; case ALOAD_2: case ASTORE_2: case ILOAD_2: case ISTORE_2: case FLOAD_2: case FSTORE_2: case LLOAD_1: case LSTORE_1: case DLOAD_1: case DSTORE_1: ENSURE_NON_WIDE; CHECK_LOCAL_INDEX(2); break; case ALOAD_3: case ASTORE_3: case ILOAD_3: case ISTORE_3: case FLOAD_3: case FSTORE_3: case LLOAD_2: case LSTORE_2: case DLOAD_2: case DSTORE_2: ENSURE_NON_WIDE; CHECK_LOCAL_INDEX(3); break; case LLOAD_3: case LSTORE_3: case DLOAD_3: case DSTORE_3: ENSURE_NON_WIDE; CHECK_LOCAL_INDEX(4); break; case LDC1: GET_IDX(idx, pc); goto LDC_common; case LDC2: GET_WIDX(idx, pc); LDC_common: n = CONST_TAG(idx, pool); if (n != CONSTANT_Integer && n != CONSTANT_Float && n != CONSTANT_String && n != CONSTANT_ResolvedString) { VERIFY_ERROR("ldc* on constant pool entry other than int/float/string"); } break; case LDC2W: GET_WIDX(idx, pc); n = CONST_TAG(idx, pool); if (n != CONSTANT_Double && n != CONSTANT_Long) { VERIFY_ERROR("ldc2_w on constant pool entry other than long or double"); } break; case GETFIELD: case PUTFIELD: case GETSTATIC: case PUTSTATIC: ENSURE_NON_WIDE; GET_WIDX(idx, pc); idx = CONST_TAG(idx, pool); if (idx != CONSTANT_Fieldref) { VERIFY_ERROR("[get/put][field/static] accesses something in the constant pool that is not a CONSTANT_Fieldref"); } break; case INVOKEVIRTUAL: case INVOKESTATIC: case INVOKESPECIAL: ENSURE_NON_WIDE; GET_WIDX(idx, pc); n = CONST_TAG(idx, pool); if (n != CONSTANT_Methodref) { VERIFY_ERROR("invoke* accesses something in the constant pool that is not a CONSTANT_Methodref"); } sig = METHODREF_SIGD(idx, pool); if (*sig == '<') { if (!strcmp(constructor_name->data, sig)) { if (code[pc] != INVOKESPECIAL) { VERIFY_ERROR("only invokespecial can be used to execute <init> methods"); } } else { VERIFY_ERROR("no method with a name whose first character is '<' may be called by an invoke instruction"); } } break; /* invokeinterface is a 5 byte instruction. the first byte is the instruction. * the next two are the index into the constant pool for the methodreference. * the fourth is the number of parameters expected by the method, and the verifier * must check that the actual method signature of the method to be invoked matches * this number. the 5th must be zero. these are apparently present for historical * reasons (yeah Sun :::smirk:::). */ case INVOKEINTERFACE: ENSURE_NON_WIDE; GET_WIDX(idx, pc); n = CONST_TAG(idx, pool); if (n != CONSTANT_InterfaceMethodref) { VERIFY_ERROR("invokeinterface accesses something in the constant pool that is not a CONSTANT_InterfaceMethodref"); } sig = INTERFACEMETHODREF_SIGD(idx, pool); if (*sig == '<') { VERIFY_ERROR("invokeinterface cannot be used to invoke any instruction with a name starting with '<'"); } if (code[pc + 3] == 0) { VERIFY_ERROR("fourth byte of invokeinterface is zero"); } else if (code[pc + 4] != 0) { VERIFY_ERROR("fifth byte of invokeinterface is not zero"); } break; case INSTANCEOF: case CHECKCAST: ENSURE_NON_WIDE; GET_WIDX(n, pc); n = CONST_TAG(n, pool); if (n != CONSTANT_Class && n != CONSTANT_ResolvedClass) { VERIFY_ERROR("instanceof/checkcast indexes a constant pool entry that is not type CONSTANT_Class or CONSTANT_ResolvedClass"); } break; case MULTIANEWARRAY: ENSURE_NON_WIDE; GET_WIDX(idx, pc); n = CONST_TAG(idx, pool); if (n != CONSTANT_Class && n != CONSTANT_ResolvedClass) { VERIFY_ERROR("multinewarray indexes a constant pool entry that is not type CONSTANT_Class or CONSTANT_ResolvedClass"); } /* number of dimensions must be <= num dimensions of array type being created */ sig = CLASS_NAMED(idx, pool); newpc = code[pc + 3]; if (newpc == 0) { VERIFY_ERROR("dimensions operand of multianewarray must be non-zero"); } for(n = 0; *sig == '['; sig++, n++); if (n < newpc) { VERIFY_ERROR("dimensions operand of multianewarray is > the number of dimensions in array being created"); } break; case NEW: ENSURE_NON_WIDE; GET_WIDX(idx, pc); n = CONST_TAG(idx, pool); if (n != CONSTANT_Class && n != CONSTANT_ResolvedClass) { VERIFY_ERROR("new indexes a constant pool entry that is not type CONSTANT_Class or CONSTANT_ResolvedClass"); } /* cannot create arrays with NEW */ sig = CLASS_NAMED(idx, pool); if (*sig == '[') { VERIFY_ERROR("new instruction used to create a new array"); } break; case ANEWARRAY: ENSURE_NON_WIDE; GET_WIDX(idx, pc); n = CONST_TAG(idx, pool); if (n != CONSTANT_Class && n != CONSTANT_ResolvedClass) { VERIFY_ERROR("anewarray indexes a constant pool entry that is not type CONSTANT_Class or CONSTANT_ResolvedClass"); } /* count the number of dimensions of the array being created...it must be <= 255 */ sig = CLASS_NAMED(idx, pool); for (n = 0; *sig == '['; sig++, n++); if (n > 255) { VERIFY_ERROR("anewarray used to create an array of > 255 dimensions"); } break; case NEWARRAY: ENSURE_NON_WIDE; n = code[pc + 1]; if (n < 4 || n > 11) { VERIFY_ERROR("newarray operand must be in the range [4,11]"); } break; /*********************************************************** * Instructions that can be modified by WIDE ***********************************************************/ case WIDE: ENSURE_NON_WIDE; wide = true; break; case ALOAD: case ASTORE: case ILOAD: case ISTORE: case FLOAD: case FSTORE: if (wide == true) { /* the WIDE is considered the beginning of the instruction */ status[pc] ^= IS_INSTRUCTION; status[pc] |= WIDE_MODDED; pc++; wide = false; n = WORD(code, pc); } else { n = code[pc + 1]; } CHECK_LOCAL_INDEX(n); break; case LLOAD: case LSTORE: case DLOAD: case DSTORE: if (wide == true) { /* the WIDE is considered the beginning of the instruction */ status[pc] ^= IS_INSTRUCTION; status[pc] |= WIDE_MODDED; pc++; wide = false; n = WORD(code, pc); } else { GET_IDX(n, pc); } /* makes sure the index given for a local variable is within the correct index * * REM: longs and doubles take two consecutive local spots */ CHECK_LOCAL_INDEX(n + 1); break; case IINC: if (wide == true) { /* the WIDE is considered the beginning of the instruction */ status[pc] ^= IS_INSTRUCTION; status[pc] |= WIDE_MODDED; pc += 2; wide = false; } break; /******************************************************************** * BRANCHING INSTRUCTIONS ********************************************************************/ case GOTO: ENSURE_NON_WIDE; status[pc] |= END_BLOCK; n = pc + 1; branchoffset = WORD(code, n); newpc = pc + branchoffset; BRANCH_IN_BOUNDS(newpc, "goto"); status[newpc] |= START_BLOCK; break; case GOTO_W: ENSURE_NON_WIDE; status[pc] |= END_BLOCK; n = pc + 1; branchoffset = DWORD(code, n); newpc = pc + branchoffset; BRANCH_IN_BOUNDS(newpc, "goto_w"); status[newpc] |= START_BLOCK; break; case IF_ACMPEQ: case IFNONNULL: case IF_ACMPNE: case IFNULL: case IF_ICMPEQ: case IFEQ: case IF_ICMPNE: case IFNE: case IF_ICMPGT: case IFGT: case IF_ICMPGE: case IFGE: case IF_ICMPLT: case IFLT: case IF_ICMPLE: case IFLE: ENSURE_NON_WIDE; status[pc] |= END_BLOCK; newpc = NEXTPC; BRANCH_IN_BOUNDS(newpc, "if<condition> = false"); status[newpc] |= START_BLOCK; n = pc + 1; branchoffset = WORD(code, n); newpc = pc + branchoffset; BRANCH_IN_BOUNDS(newpc, "if<condition> = true"); status[newpc] |= START_BLOCK; break; case JSR: newpc = pc + 1; newpc = pc + WORD(code, newpc); goto JSR_common; case JSR_W: newpc = pc + 1; newpc = pc + DWORD(code, newpc); JSR_common: ENSURE_NON_WIDE; status[pc] |= END_BLOCK;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -