/* * Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/
/*- * Verify that the code within a method block doesn't exploit any * security holes.
*/ /* Exported function:
/* On AIX malloc(0) and calloc(0, ...) return a NULL pointer, which is legal, * but the code here does not handles it. So we wrap the methods and return non-NULL * pointers even if we allocate 0 bytes.
*/ #ifdef _AIX staticint aix_dummy; staticvoid* aix_malloc(size_t len) { if (len == 0) { return &aix_dummy;
} return malloc(len);
}
staticvoid* aix_calloc(size_t n, size_t size) { if (n == 0) { return &aix_dummy;
} return calloc(n, size);
}
int verify_verbose = 0; staticstruct context_type *GlobalContext; #endif
enum {
ITEM_Bogus,
ITEM_Void, /* only as a function return value */
ITEM_Integer,
ITEM_Float,
ITEM_Double,
ITEM_Double_2, /* 2nd word of double in register */
ITEM_Long,
ITEM_Long_2, /* 2nd word of long in register */
ITEM_Array,
ITEM_Object, /* Extra info field gives name. */
ITEM_NewObject, /* Like object, but uninitialized. */
ITEM_InitObject, /* "this" is init method, before call
to super() */
ITEM_ReturnAddress, /* Extra info gives instr # of start pc */ /* The following four are only used within array types.
* Normally, we use ITEM_Integer, instead. */
ITEM_Byte,
ITEM_Short,
ITEM_Char,
ITEM_Boolean
};
/* JVM_OPC_invokespecial calls to <init> need to be treated special */ #define JVM_OPC_invokeinit 0x100
/* A hash mechanism used by the verifier. * Maps class names to unique 16 bit integers.
*/
#define HASH_TABLE_SIZE 503
/* The buckets are managed as a 256 by 256 matrix. We allocate an entire * row (256 buckets) at a time to minimize fragmentation. Rows are * allocated on demand so that we don't waste too much space.
*/
typedefstruct {
hash_bucket_type **buckets; unsignedshort *table; int entries_used;
} hash_table_type;
#define GET_BUCKET(class_hash, ID)\
(class_hash->buckets[ID / HASH_ROW_SIZE] + ID % HASH_ROW_SIZE)
/* * There are currently two types of resources that we need to keep * track of (in addition to the CCalloc pool).
*/ enum {
VM_STRING_UTF, /* VM-allocated UTF strings */
VM_MALLOC_BLK /* malloc'ed blocks */
};
/* The context type encapsulates the current invocation of the byte * code verifier.
*/ struct context_type {
JNIEnv *env; /* current JNIEnv */
/* buffers etc. */ char *message;
jint message_buf_len;
jboolean err_code;
alloc_stack_type *allocated_memory; /* all memory blocks that we have not
had a chance to free */ /* Store up to ALLOC_STACK_SIZE number of handles to allocated memory
blocks here, to save mallocs. */
alloc_stack_type alloc_stack[ALLOC_STACK_SIZE]; int alloc_stack_top;
/* these fields are per class */
jclass class; /* current class */
jint major_version;
jint nconstants; unsignedchar *constant_types;
hash_table_type class_hash;
fullinfo_type object_info; /* fullinfo for java/lang/Object */
fullinfo_type string_info; /* fullinfo for java/lang/String */
fullinfo_type throwable_info; /* fullinfo for java/lang/Throwable */
fullinfo_type cloneable_info; /* fullinfo for java/lang/Cloneable */
fullinfo_type serializable_info; /* fullinfo for java/io/Serializable */
fullinfo_type currentclass_info; /* fullinfo for context->class */
fullinfo_type superclass_info; /* fullinfo for superclass */
/* these fields are per method */ int method_index; /* current method */ unsignedshort *exceptions; /* exceptions */ unsignedchar *code; /* current code object */
jint code_length; int *code_data; /* offset to instruction number */ struct instruction_data_type *instruction_data; /* info about each */ struct handler_info_type *handler_info;
fullinfo_type *superclasses; /* null terminated superclasses */ int instruction_count; /* number of instructions */
fullinfo_type return_type; /* function return type */
fullinfo_type swap_table[4]; /* used for passing information */ int bitmask_size; /* words needed to hold bitmap of arguments */
/* these fields are per field */ int field_index;
/* Used by the space allocator */ struct CCpool *CCroot, *CCcurrent; char *CCfree_ptr; int CCfree_size;
/* Jump here on any error. */
jmp_buf jump_buffer;
#ifdef DEBUG /* keep track of how many global refs are allocated. */ int n_globalrefs; #endif
};
struct stack_info_type { struct stack_item_type *stack; int stack_size;
};
struct register_info_type { int register_count; /* number of registers used */
fullinfo_type *registers; int mask_count; /* number of masks in the following */ struct mask_type *masks;
};
struct mask_type { int entry; int *modifies;
};
typedefunsignedshort flag_type;
struct instruction_data_type { int opcode; /* may turn into "canonical" opcode */ unsigned changed:1; /* has it changed */ unsignedprotected:1; /* must accessor be a subclass of "this" */ union { int i; /* operand to the opcode */ int *ip;
fullinfo_type fi;
} operand, operand2;
fullinfo_type p; struct stack_info_type stack_info; struct register_info_type register_info; #define FLAG_REACHED 0x01 /* instruction reached */ #define FLAG_NEED_CONSTRUCTOR 0x02 /* must call this.<init> or super.<init> */ #define FLAG_NO_RETURN 0x04 /* must throw out of method */
flag_type or_flags; /* true for at least one path to this inst */ #define FLAG_CONSTRUCTED 0x01 /* this.<init> or super.<init> called */
flag_type and_flags; /* true for all paths to this instruction */
};
staticvoid finalize_class_hash(context_type *context)
{
hash_table_type *class_hash = &(context->class_hash);
JNIEnv *env = context->env; int i; /* 4296677: bucket index starts from 1. */ for (i=1;i<=class_hash->entries_used;i++) {
hash_bucket_type *bucket = GET_BUCKET(class_hash, i);
assert(bucket != NULL);
free(bucket->name); if (bucket->class) {
(*env)->DeleteGlobalRef(env, bucket->class); #ifdef DEBUG
context->n_globalrefs--; #endif
}
} if (class_hash->buckets) { for (i=0;i<MAX_HASH_ENTRIES / HASH_ROW_SIZE; i++) { if (class_hash->buckets[i] == 0) break;
free(class_hash->buckets[i]);
}
}
free(class_hash->buckets);
free(class_hash->table);
}
static hash_bucket_type *
new_bucket(context_type *context, unsignedshort *pID)
{
hash_table_type *class_hash = &(context->class_hash); int i = *pID = class_hash->entries_used + 1; int row = i / HASH_ROW_SIZE; if (i >= MAX_HASH_ENTRIES)
CCerror(context, "Exceeded verifier's limit of 65535 referred classes"); if (class_hash->buckets[row] == 0) {
class_hash->buckets[row] = (hash_bucket_type*)
calloc(HASH_ROW_SIZE, sizeof(hash_bucket_type)); if (class_hash->buckets[row] == 0)
CCout_of_memory(context);
}
class_hash->entries_used++; /* only increment when we are sure there
is no overflow. */ return GET_BUCKET(class_hash, i);
}
staticunsignedint
class_hash_fun(constchar *s)
{ int i; unsigned raw_hash; for (raw_hash = 0; (i = *s) != '\0'; ++s)
raw_hash = raw_hash * 37 + i; return raw_hash;
}
/* * Find a class using the defining loader of the current class * and return a local reference to it.
*/ static jclass load_class_local(context_type *context,constchar *classname)
{
jclass cb = JVM_FindClassFromClass(context->env, classname,
JNI_FALSE, context->class); if (cb == 0)
CCerror(context, "Cannot find class %s", classname); return cb;
}
/* * Find a class using the defining loader of the current class * and return a global reference to it.
*/ static jclass load_class_global(context_type *context, constchar *classname)
{
JNIEnv *env = context->env;
jclass local, global;
local = load_class_local(context, classname);
global = (*env)->NewGlobalRef(env, local); if (global == 0)
CCout_of_memory(context); #ifdef DEBUG
context->n_globalrefs++; #endif
(*env)->DeleteLocalRef(env, local); return global;
}
/* * Return a unique ID given a local class reference. The loadable * flag is true if the defining class loader of context->class * is known to be capable of loading the class.
*/ staticunsignedshort
class_to_ID(context_type *context, jclass cb, jboolean loadable)
{
JNIEnv *env = context->env;
hash_table_type *class_hash = &(context->class_hash); unsignedint hash;
hash_bucket_type *bucket; unsignedshort *pID; constchar *name = JVM_GetClassNameUTF(env, cb);
check_and_push_string_utf(context, name);
hash = class_hash_fun(name);
pID = &(class_hash->table[hash % HASH_TABLE_SIZE]); while (*pID) {
bucket = GET_BUCKET(class_hash, *pID); if (bucket->hash == hash && strcmp(name, bucket->name) == 0) { /* * There is an unresolved entry with our name * so we're forced to load it in case it matches us.
*/ if (bucket->class == 0) {
assert(bucket->loadable == JNI_TRUE);
bucket->class = load_class_global(context, name);
}
/* * It's already in the table. Update the loadable * state if it's known and then we're done.
*/ if ((*env)->IsSameObject(env, cb, bucket->class)) { if (loadable && !bucket->loadable)
bucket->loadable = JNI_TRUE; goto done;
}
}
pID = &bucket->next;
}
bucket = new_bucket(context, pID);
bucket->next = 0;
bucket->hash = hash;
bucket->name = malloc(strlen(name) + 1); if (bucket->name == 0)
CCout_of_memory(context);
strcpy(bucket->name, name);
bucket->loadable = loadable;
bucket->class = (*env)->NewGlobalRef(env, cb); if (bucket->class == 0)
CCout_of_memory(context); #ifdef DEBUG
context->n_globalrefs++; #endif
done:
pop_and_free(context); return *pID;
}
/* * Return a unique ID given a class name from the constant pool. * All classes are lazily loaded from the defining loader of * context->class.
*/ staticunsignedshort
class_name_to_ID(context_type *context, constchar *name)
{
hash_table_type *class_hash = &(context->class_hash); unsignedint hash = class_hash_fun(name);
hash_bucket_type *bucket; unsignedshort *pID;
jboolean force_load = JNI_FALSE;
if (force_load) { /* * We found at least one matching named entry for a class that * was not known to be loadable through the defining class loader * of context->class. We must load our named class and update * the hash table in case one these entries matches our class.
*/
JNIEnv *env = context->env;
jclass cb = load_class_local(context, name); unsignedshort id = class_to_ID(context, cb, JNI_TRUE);
(*env)->DeleteLocalRef(env, cb); return id;
}
/* RETURNS * 1: on success chosen to be consistent with previous VerifyClass * 0: verify error * 2: out of memory * 3: class format error * * Called by verify_class. Verify the code of each of the methods * in a class. Note that this function apparently can't be JNICALL, * because if it is the dynamic linker doesn't appear to be able to * find it on Win32.
*/
/* Can't go on context heap since it survives more than
one method */
context->superclasses = gptr =
malloc(sizeof(fullinfo_type)*(i + 1)); if (gptr == 0) {
CCout_of_memory(context);
}
/** * We read all of the class's methods' code because it is possible that * the verification of one method could resulting in linking further * down the stack (due to class loading), which could end up rewriting * some of the bytecode of methods we haven't verified yet. Since we * don't want to see the rewritten bytecode, cache all the code and * operate only on that.
*/ staticvoid
read_all_code(context_type* context, jclass cb, int num_methods, int** lengths_addr, unsignedchar*** code_addr)
{ int* lengths; unsignedchar** code; int i;
for (i = 0; i < num_methods; ++i) {
lengths[i] = JVM_GetMethodIxByteCodeLength(context->env, cb, i); if (lengths[i] > 0) {
code[i] = malloc(sizeof(unsignedchar) * (lengths[i] + 1));
check_and_push_malloc_block(context, code[i]);
JVM_GetMethodIxByteCode(context->env, cb, i, code[i]);
} else {
code[i] = NULL;
}
}
}
staticvoid
free_all_code(context_type* context, int num_methods, unsignedchar** code)
{ int i; for (i = 0; i < num_methods; ++i) { if (code[i] != NULL) {
pop_and_free(context);
}
}
pop_and_free(context); /* code */
pop_and_free(context); /* lengths */
}
/* Verify the code of one method */ staticvoid
verify_method(context_type *context, jclass cb, int method_index, int code_length, unsignedchar* code)
{
JNIEnv *env = context->env; int access_bits = JVM_GetMethodIxModifiers(env, cb, method_index); int *code_data;
instruction_data_type *idata = 0; int instruction_count; int i, offset; unsignedint inumber;
jint nexceptions;
if ((access_bits & (JVM_ACC_NATIVE | JVM_ACC_ABSTRACT)) != 0) { /* not much to do for abstract and native methods */ return;
}
// If this method is an overpass method, which is generated by the VM, // we trust the code and no check needs to be done. if (JVM_IsVMGeneratedMethodIx(env, cb, method_index)) { return;
}
/* Run through the code. Mark the start of each instruction, and give
* the instruction a number */ for (i = 0, offset = 0; offset < code_length; i++) { int length = instruction_length(&code[offset], code + code_length); int next_offset = offset + length; if (length <= 0)
CCerror(context, "Illegal instruction found at offset %d", offset); if (next_offset > code_length)
CCerror(context, "Code stops in the middle of instruction " " starting at offset %d", offset);
code_data[offset] = i; while (++offset < next_offset)
code_data[offset] = -1; /* illegal location */
}
instruction_count = i; /* number of instructions in code */
/* Allocate a structure to hold info about each instruction. */
idata = NEW(instruction_data_type, instruction_count);
/* Initialize the heap, and other info in the context structure. */
context->code = code;
context->instruction_data = idata;
context->code_data = code_data;
context->instruction_count = instruction_count;
context->handler_info = NEW(struct handler_info_type,
JVM_GetMethodIxExceptionTableLength(env, cb, method_index));
context->bitmask_size =
(JVM_GetMethodIxLocalsCount(env, cb, method_index)
+ (BITS_PER_INT - 1))/BITS_PER_INT;
if (instruction_count == 0)
CCerror(context, "Empty code");
for (inumber = 0, offset = 0; offset < code_length; inumber++) { int length = instruction_length(&code[offset], code + code_length);
instruction_data_type *this_idata = &idata[inumber];
this_idata->opcode = code[offset];
this_idata->stack_info.stack = NULL;
this_idata->stack_info.stack_size = UNKNOWN_STACK_SIZE;
this_idata->register_info.register_count = UNKNOWN_REGISTER_COUNT;
this_idata->changed = JNI_FALSE; /* no need to look at it yet. */
this_idata->protected = JNI_FALSE; /* no need to look at it yet. */
this_idata->and_flags = (flag_type) -1; /* "bottom" and value */
this_idata->or_flags = 0; /* "bottom" or value*/ /* This also sets up this_data->operand. It also makes the
* xload_x and xstore_x instructions look like the generic form. */
verify_opcode_operands(context, inumber, offset);
offset += length;
}
/* make sure exception table is reasonable. */
initialize_exception_table(context); /* Set up first instruction, and start of exception handlers. */
initialize_dataflow(context); /* Run data flow analysis on the instructions. */
run_dataflow(context);
/* verify checked exceptions, if any */
nexceptions = JVM_GetMethodIxExceptionsCount(env, cb, method_index);
context->exceptions = (unsignedshort *)
malloc(sizeof(unsignedshort) * nexceptions + 1); if (context->exceptions == 0)
CCout_of_memory(context);
JVM_GetMethodIxExceptionIndexes(env, cb, method_index,
context->exceptions); for (i = 0; i < nexceptions; i++) { /* Make sure the constant pool item is JVM_CONSTANT_Class */
verify_constant_pool_type(context, (int)context->exceptions[i],
1 << JVM_CONSTANT_Class);
}
free(context->exceptions);
context->exceptions = 0;
context->code = 0;
context->method_index = -1;
}
/* Look at a single instruction, and verify its operands. Also, for * simplicity, move the operand into the ->operand field. * Make sure that branches don't go into the middle of nowhere.
*/
staticvoid
verify_opcode_operands(context_type *context, unsignedint inumber, int offset)
{
JNIEnv *env = context->env;
instruction_data_type *idata = context->instruction_data;
instruction_data_type *this_idata = &idata[inumber]; int *code_data = context->code_data; int mi = context->method_index; unsignedchar *code = context->code; int opcode = this_idata->opcode; int var;
/* * Set the ip fields to 0 not the i fields because the ip fields * are 64 bits on 64 bit architectures, the i field is only 32
*/
this_idata->operand.ip = 0;
this_idata->operand2.ip = 0;
switch (opcode) {
case JVM_OPC_jsr: /* instruction of ret statement */
this_idata->operand2.i = UNKNOWN_RET_INSTRUCTION; /* FALLTHROUGH */ case JVM_OPC_ifeq: case JVM_OPC_ifne: case JVM_OPC_iflt: case JVM_OPC_ifge: case JVM_OPC_ifgt: case JVM_OPC_ifle: case JVM_OPC_ifnull: case JVM_OPC_ifnonnull: case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmplt: case JVM_OPC_if_icmpge: case JVM_OPC_if_icmpgt: case JVM_OPC_if_icmple: case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne: case JVM_OPC_goto: { /* Set the ->operand to be the instruction number of the target. */ int jump = (((signedchar)(code[offset+1])) << 8) + code[offset+2]; int target = offset + jump; if (!isLegalTarget(context, target))
CCerror(context, "Illegal target of jump or branch");
this_idata->operand.i = code_data[target]; break;
}
case JVM_OPC_jsr_w: /* instruction of ret statement */
this_idata->operand2.i = UNKNOWN_RET_INSTRUCTION; /* FALLTHROUGH */ case JVM_OPC_goto_w: { /* Set the ->operand to be the instruction number of the target. */ int jump = (((signedchar)(code[offset+1])) << 24) +
(code[offset+2] << 16) + (code[offset+3] << 8) +
(code[offset + 4]); int target = offset + jump; if (!isLegalTarget(context, target))
CCerror(context, "Illegal target of jump or branch");
this_idata->operand.i = code_data[target]; break;
}
case JVM_OPC_tableswitch: case JVM_OPC_lookupswitch: { /* Set the ->operand to be a table of possible instruction targets. */ int *lpc = (int *) UCALIGN(code + offset + 1); int *lptr; int *saved_operand; int keys; int k, delta;
if (context->major_version < NONZERO_PADDING_BYTES_IN_SWITCH_MAJOR_VERSION) { /* 4639449, 4647081: Padding bytes must be zero. */ unsignedchar* bptr = (unsignedchar*) (code + offset + 1); for (; bptr < (unsignedchar*)lpc; bptr++) { if (*bptr != 0) {
CCerror(context, "Non zero padding bytes in switch");
}
}
} if (opcode == JVM_OPC_tableswitch) {
keys = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]) + 1;
delta = 1;
} else {
keys = _ck_ntohl(lpc[1]); /* number of pairs */
delta = 2; /* Make sure that the tableswitch items are sorted */ for (k = keys - 1, lptr = &lpc[2]; --k >= 0; lptr += 2) { int this_key = _ck_ntohl(lptr[0]); /* NB: ntohl may be unsigned */ int next_key = _ck_ntohl(lptr[2]); if (this_key >= next_key) {
CCerror(context, "Unsorted lookup switch");
}
}
}
saved_operand = NEW(int, keys + 2); if (!isLegalTarget(context, offset + _ck_ntohl(lpc[0])))
CCerror(context, "Illegal default target in switch");
saved_operand[keys + 1] = code_data[offset + _ck_ntohl(lpc[0])]; for (k = keys, lptr = &lpc[3]; --k >= 0; lptr += delta) { int target = offset + _ck_ntohl(lptr[0]); if (!isLegalTarget(context, target))
CCerror(context, "Illegal branch in tableswitch");
saved_operand[k + 1] = code_data[target];
}
saved_operand[0] = keys + 1; /* number of successors */
this_idata->operand.ip = saved_operand; break;
}
case JVM_OPC_ldc: { /* Make sure the constant pool item is the right type. */ int key = code[offset + 1]; int types = (1 << JVM_CONSTANT_Integer) | (1 << JVM_CONSTANT_Float) |
(1 << JVM_CONSTANT_String); if (context->major_version >= LDC_CLASS_MAJOR_VERSION) {
types |= 1 << JVM_CONSTANT_Class;
} if (context->major_version >= LDC_METHOD_HANDLE_MAJOR_VERSION) {
types |= (1 << JVM_CONSTANT_MethodHandle) |
(1 << JVM_CONSTANT_MethodType);
}
this_idata->operand.i = key;
verify_constant_pool_type(context, key, types); break;
}
case JVM_OPC_ldc_w: { /* Make sure the constant pool item is the right type. */ int key = (code[offset + 1] << 8) + code[offset + 2]; int types = (1 << JVM_CONSTANT_Integer) | (1 << JVM_CONSTANT_Float) |
(1 << JVM_CONSTANT_String); if (context->major_version >= LDC_CLASS_MAJOR_VERSION) {
types |= 1 << JVM_CONSTANT_Class;
} if (context->major_version >= LDC_METHOD_HANDLE_MAJOR_VERSION) {
types |= (1 << JVM_CONSTANT_MethodHandle) |
(1 << JVM_CONSTANT_MethodType);
}
this_idata->operand.i = key;
verify_constant_pool_type(context, key, types); break;
}
case JVM_OPC_ldc2_w: { /* Make sure the constant pool item is the right type. */ int key = (code[offset + 1] << 8) + code[offset + 2]; int types = (1 << JVM_CONSTANT_Double) | (1 << JVM_CONSTANT_Long);
this_idata->operand.i = key;
verify_constant_pool_type(context, key, types); break;
}
case JVM_OPC_getfield: case JVM_OPC_putfield: case JVM_OPC_getstatic: case JVM_OPC_putstatic: { /* Make sure the constant pool item is the right type. */ int key = (code[offset + 1] << 8) + code[offset + 2];
this_idata->operand.i = key;
verify_constant_pool_type(context, key, 1 << JVM_CONSTANT_Fieldref); if (opcode == JVM_OPC_getfield || opcode == JVM_OPC_putfield)
set_protected(context, inumber, key, opcode); break;
}
case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial: case JVM_OPC_invokestatic: case JVM_OPC_invokeinterface: { /* Make sure the constant pool item is the right type. */ int key = (code[offset + 1] << 8) + code[offset + 2]; constchar *methodname;
jclass cb = context->class;
fullinfo_type clazz_info; int is_constructor, is_internal; int kind;
/* The optimizer may cause this to happen on local code */ if (not_found) {
CCerror(context, "Illegal use of nonvirtual function call");
}
}
} if (opcode == JVM_OPC_invokeinterface) { unsignedint args1; unsignedint args2; constchar *signature =
JVM_GetCPMethodSignatureUTF(env, context->class, key);
check_and_push_string_utf(context, signature);
args1 = signature_to_args_size(signature) + 1;
args2 = code[offset + 3]; if (args1 != args2) {
CCerror(context, "Inconsistent args_size for invokeinterface");
} if (code[offset + 4] != 0) {
CCerror(context, "Fourth operand byte of invokeinterface must be zero");
}
pop_and_free(context);
} elseif (opcode == JVM_OPC_invokevirtual
|| opcode == JVM_OPC_invokespecial)
set_protected(context, inumber, key, opcode); break;
}
case JVM_OPC_invokedynamic:
CCerror(context, "invokedynamic bytecode is not supported in this class file version"); break; case JVM_OPC_instanceof: case JVM_OPC_checkcast: case JVM_OPC_new: case JVM_OPC_anewarray: case JVM_OPC_multianewarray: { /* Make sure the constant pool item is a class */ int key = (code[offset + 1] << 8) + code[offset + 2];
fullinfo_type target;
verify_constant_pool_type(context, key, 1 << JVM_CONSTANT_Class);
target = cp_index_to_class_fullinfo(context, key, JVM_CONSTANT_Class); if (GET_ITEM_TYPE(target) == ITEM_Bogus)
CCerror(context, "Illegal type"); switch(opcode) { case JVM_OPC_anewarray: if ((GET_INDIRECTION(target)) >= MAX_ARRAY_DIMENSIONS)
CCerror(context, "Array with too many dimensions");
this_idata->operand.fi = MAKE_FULLINFO(GET_ITEM_TYPE(target),
GET_INDIRECTION(target) + 1,
GET_EXTRA_INFO(target)); break; case JVM_OPC_new: if (WITH_ZERO_EXTRA_INFO(target) !=
MAKE_FULLINFO(ITEM_Object, 0, 0))
CCerror(context, "Illegal creation of multi-dimensional array"); /* operand gets set to the "uninitialized object". operand2 gets
* set to what the value will be after it's initialized. */
this_idata->operand.fi = MAKE_FULLINFO(ITEM_NewObject, 0, inumber);
this_idata->operand2.fi = target; break; case JVM_OPC_multianewarray:
this_idata->operand.fi = target;
this_idata->operand2.i = code[offset + 3]; if ( (this_idata->operand2.i > (int)GET_INDIRECTION(target))
|| (this_idata->operand2.i == 0))
CCerror(context, "Illegal dimension argument"); break; default:
this_idata->operand.fi = target;
} break;
}
case JVM_OPC_newarray: { /* Cache the result of the JVM_OPC_newarray into the operand slot */
fullinfo_type full_info; switch (code[offset + 1]) { case JVM_T_INT:
full_info = MAKE_FULLINFO(ITEM_Integer, 1, 0); break; case JVM_T_LONG:
full_info = MAKE_FULLINFO(ITEM_Long, 1, 0); break; case JVM_T_FLOAT:
full_info = MAKE_FULLINFO(ITEM_Float, 1, 0); break; case JVM_T_DOUBLE:
full_info = MAKE_FULLINFO(ITEM_Double, 1, 0); break; case JVM_T_BOOLEAN:
full_info = MAKE_FULLINFO(ITEM_Boolean, 1, 0); break; case JVM_T_BYTE:
full_info = MAKE_FULLINFO(ITEM_Byte, 1, 0); break; case JVM_T_CHAR:
full_info = MAKE_FULLINFO(ITEM_Char, 1, 0); break; case JVM_T_SHORT:
full_info = MAKE_FULLINFO(ITEM_Short, 1, 0); break; default:
full_info = 0; /* Keep lint happy */
CCerror(context, "Bad type passed to newarray");
}
this_idata->operand.fi = full_info; break;
}
/* Fudge iload_x, aload_x, etc to look like their generic cousin. */ case JVM_OPC_iload_0: case JVM_OPC_iload_1: case JVM_OPC_iload_2: case JVM_OPC_iload_3:
this_idata->opcode = JVM_OPC_iload;
var = opcode - JVM_OPC_iload_0; goto check_local_variable;
case JVM_OPC_fload_0: case JVM_OPC_fload_1: case JVM_OPC_fload_2: case JVM_OPC_fload_3:
this_idata->opcode = JVM_OPC_fload;
var = opcode - JVM_OPC_fload_0; goto check_local_variable;
case JVM_OPC_aload_0: case JVM_OPC_aload_1: case JVM_OPC_aload_2: case JVM_OPC_aload_3:
this_idata->opcode = JVM_OPC_aload;
var = opcode - JVM_OPC_aload_0; goto check_local_variable;
case JVM_OPC_lload_0: case JVM_OPC_lload_1: case JVM_OPC_lload_2: case JVM_OPC_lload_3:
this_idata->opcode = JVM_OPC_lload;
var = opcode - JVM_OPC_lload_0; goto check_local_variable2;
case JVM_OPC_dload_0: case JVM_OPC_dload_1: case JVM_OPC_dload_2: case JVM_OPC_dload_3:
this_idata->opcode = JVM_OPC_dload;
var = opcode - JVM_OPC_dload_0; goto check_local_variable2;
case JVM_OPC_istore_0: case JVM_OPC_istore_1: case JVM_OPC_istore_2: case JVM_OPC_istore_3:
this_idata->opcode = JVM_OPC_istore;
var = opcode - JVM_OPC_istore_0; goto check_local_variable;
case JVM_OPC_fstore_0: case JVM_OPC_fstore_1: case JVM_OPC_fstore_2: case JVM_OPC_fstore_3:
this_idata->opcode = JVM_OPC_fstore;
var = opcode - JVM_OPC_fstore_0; goto check_local_variable;
case JVM_OPC_astore_0: case JVM_OPC_astore_1: case JVM_OPC_astore_2: case JVM_OPC_astore_3:
this_idata->opcode = JVM_OPC_astore;
var = opcode - JVM_OPC_astore_0; goto check_local_variable;
case JVM_OPC_lstore_0: case JVM_OPC_lstore_1: case JVM_OPC_lstore_2: case JVM_OPC_lstore_3:
this_idata->opcode = JVM_OPC_lstore;
var = opcode - JVM_OPC_lstore_0; goto check_local_variable2;
case JVM_OPC_dstore_0: case JVM_OPC_dstore_1: case JVM_OPC_dstore_2: case JVM_OPC_dstore_3:
this_idata->opcode = JVM_OPC_dstore;
var = opcode - JVM_OPC_dstore_0; goto check_local_variable2;
case JVM_OPC_wide:
this_idata->opcode = code[offset + 1];
var = (code[offset + 2] << 8) + code[offset + 3]; switch(this_idata->opcode) { case JVM_OPC_lload: case JVM_OPC_dload: case JVM_OPC_lstore: case JVM_OPC_dstore: goto check_local_variable2; default: goto check_local_variable;
}
case JVM_OPC_iinc: /* the increment amount doesn't matter */ case JVM_OPC_ret: case JVM_OPC_aload: case JVM_OPC_iload: case JVM_OPC_fload: case JVM_OPC_astore: case JVM_OPC_istore: case JVM_OPC_fstore:
var = code[offset + 1];
check_local_variable: /* Make sure that the variable number isn't illegal. */
this_idata->operand.i = var; if (var >= JVM_GetMethodIxLocalsCount(env, context->class, mi))
CCerror(context, "Illegal local variable number"); break;
case JVM_OPC_lload: case JVM_OPC_dload: case JVM_OPC_lstore: case JVM_OPC_dstore:
var = code[offset + 1];
check_local_variable2: /* Make sure that the variable number isn't illegal. */
this_idata->operand.i = var; if ((var + 1) >= JVM_GetMethodIxLocalsCount(env, context->class, mi))
CCerror(context, "Illegal local variable number"); break;
default: if (opcode > JVM_OPC_MAX)
CCerror(context, "Quick instructions shouldn't appear yet."); break;
} /* of switch */
}
staticvoid
set_protected(context_type *context, unsignedint inumber, int key, int opcode)
{
JNIEnv *env = context->env;
fullinfo_type clazz_info; if (opcode != JVM_OPC_invokevirtual && opcode != JVM_OPC_invokespecial) {
clazz_info = cp_index_to_class_fullinfo(context, key,
JVM_CONSTANT_Fieldref);
} else {
clazz_info = cp_index_to_class_fullinfo(context, key,
JVM_CONSTANT_Methodref);
} if (is_superclass(context, clazz_info)) {
jclass calledClass =
object_fullinfo_to_classclass(context, clazz_info); int access; /* 4734966: JVM_GetCPFieldModifiers() or JVM_GetCPMethodModifiers() only searches the referenced field or method in calledClass. The following while loop is added to search up the superclass chain to make this symbolic resolution consistent with the field/method resolution
specified in VM spec 5.4.3. */
calledClass = (*env)->NewLocalRef(env, calledClass); do {
jclass tmp_cb; if (opcode != JVM_OPC_invokevirtual && opcode != JVM_OPC_invokespecial) {
access = JVM_GetCPFieldModifiers
(env, context->class, key, calledClass);
} else {
access = JVM_GetCPMethodModifiers
(env, context->class, key, calledClass);
} if (access != -1) { break;
}
tmp_cb = (*env)->GetSuperclass(env, calledClass);
(*env)->DeleteLocalRef(env, calledClass);
calledClass = tmp_cb;
} while (calledClass != 0);
if (access == -1) { /* field/method not found, detected at runtime. */
} elseif (access & JVM_ACC_PROTECTED) { if (!JVM_IsSameClassPackage(env, calledClass, context->class))
context->instruction_data[inumber].protected = JNI_TRUE;
}
(*env)->DeleteLocalRef(env, calledClass);
}
}
if (fptr == 0) return JNI_FALSE; for (; *fptr != 0; fptr++) { if (*fptr == clazz_info) return JNI_TRUE;
} return JNI_FALSE;
}
/* Look through each item on the exception table. Each of the fields must * refer to a legal instruction.
*/ staticvoid
initialize_exception_table(context_type *context)
{
JNIEnv *env = context->env; int mi = context->method_index; struct handler_info_type *handler_info = context->handler_info; int *code_data = context->code_data; int code_length = context->code_length; int max_stack_size = JVM_GetMethodIxMaxStack(env, context->class, mi); int i = JVM_GetMethodIxExceptionTableLength(env, context->class, mi); if (max_stack_size < 1 && i > 0) { // If the method contains exception handlers, it must have room // on the expression stack for the exception that the VM could push
CCerror(context, "Stack size too large");
} for (; --i >= 0; handler_info++) {
JVM_ExceptionTableEntryType einfo;
stack_item_type *stack_item = NEW(stack_item_type, 1);
JVM_GetMethodIxExceptionTableEntry(env, context->class, mi,
i, &einfo);
handler_info->start = code_data[einfo.start_pc]; /* einfo.end_pc may point to one byte beyond the end of bytecodes. */
handler_info->end = (einfo.end_pc == context->code_length) ?
context->instruction_count : code_data[einfo.end_pc];
handler_info->handler = code_data[einfo.handler_pc];
handler_info->stack_info.stack = stack_item;
handler_info->stack_info.stack_size = 1;
stack_item->next = NULL; if (einfo.catchType != 0) { constchar *classname; /* Constant pool entry type has been checked in format checker */
classname = JVM_GetCPClassNameUTF(env,
context->class,
einfo.catchType);
check_and_push_string_utf(context, classname);
stack_item->item = make_class_info_from_name(context, classname); if (!isAssignableTo(context,
stack_item->item,
context->throwable_info))
CCerror(context, "catch_type not a subclass of Throwable");
pop_and_free(context);
} else {
stack_item->item = context->throwable_info;
}
}
}
/* Given a pointer to an instruction, return its length. Use the table * opcode_length[] which is automatically built.
*/ staticint instruction_length(unsignedchar *iptr, unsignedchar *end)
{ staticunsignedchar opcode_length[] = JVM_OPCODE_LENGTH_INITIALIZER; int instruction = *iptr; switch (instruction) { case JVM_OPC_tableswitch: { int *lpc = (int *)UCALIGN(iptr + 1); int index; if (lpc + 2 >= (int *)end) { return -1; /* do not read pass the end */
}
index = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]); if ((index < 0) || (index > 65535)) { return -1; /* illegal */
} else { unsignedchar *finish = (unsignedchar *)(&lpc[index + 4]);
assert(finish >= iptr); return (int)(finish - iptr);
}
}
case JVM_OPC_lookupswitch: { int *lpc = (int *) UCALIGN(iptr + 1); int npairs; if (lpc + 1 >= (int *)end) return -1; /* do not read pass the end */
npairs = _ck_ntohl(lpc[1]); /* There can't be more than 64K labels because of the limit * on per-method byte code length.
*/ if (npairs < 0 || npairs >= 65536) return -1; else { unsignedchar *finish = (unsignedchar *)(&lpc[2 * (npairs + 1)]);
assert(finish >= iptr); return (int)(finish - iptr);
}
}
case JVM_OPC_wide: if (iptr + 1 >= end) return -1; /* do not read pass the end */ switch(iptr[1]) { case JVM_OPC_ret: case JVM_OPC_iload: case JVM_OPC_istore: case JVM_OPC_fload: case JVM_OPC_fstore: case JVM_OPC_aload: case JVM_OPC_astore: case JVM_OPC_lload: case JVM_OPC_lstore: case JVM_OPC_dload: case JVM_OPC_dstore: return 4; case JVM_OPC_iinc: return 6; default: return -1;
}
/* A length of 0 indicates an error. */ if (opcode_length[instruction] <= 0) return -1;
return opcode_length[instruction];
}
}
}
/* Given the target of a branch, make sure that it's a legal target. */ static jboolean
isLegalTarget(context_type *context, int offset)
{ int code_length = context->code_length; int *code_data = context->code_data; return (offset >= 0 && offset < code_length && code_data[offset] >= 0);
}
/* Make sure that an element of the constant pool really is of the indicated * type.
*/ staticvoid
verify_constant_pool_type(context_type *context, int index, unsigned mask)
{ int nconstants = context->nconstants; unsignedchar *type_table = context->constant_types; unsigned type;
if ((index <= 0) || (index >= nconstants))
CCerror(context, "Illegal constant pool index");
type = type_table[index]; if ((mask & (1 << type)) == 0)
CCerror(context, "Illegal type in constant pool");
}
/* Initialize the function entry, since we know everything about it. */
idata[0].stack_info.stack_size = 0;
idata[0].stack_info.stack = NULL;
idata[0].register_info.register_count = args_size;
idata[0].register_info.registers = NEW(fullinfo_type, args_size);
idata[0].register_info.mask_count = 0;
idata[0].register_info.masks = NULL;
idata[0].and_flags = 0; /* nothing needed */
idata[0].or_flags = FLAG_REACHED; /* instruction reached */
reg_ptr = idata[0].register_info.registers;
if ((JVM_GetMethodIxModifiers(env, cb, mi) & JVM_ACC_STATIC) == 0) { /* A non static method. If this is an <init> method, the first * argument is an uninitialized object. Otherwise it is an object of * the given class type. java.lang.Object.<init> is special since * we don't call its superclass <init> method.
*/ if (JVM_IsConstructorIx(env, cb, mi)
&& context->currentclass_info != context->object_info) {
*reg_ptr++ = MAKE_FULLINFO(ITEM_InitObject, 0, 0);
idata[0].or_flags |= FLAG_NEED_CONSTRUCTOR;
} else {
*reg_ptr++ = context->currentclass_info;
}
}
signature = JVM_GetMethodIxSignatureUTF(env, cb, mi);
check_and_push_string_utf(context, signature); /* Fill in each of the arguments into the registers. */ for (p = signature + 1; *p != JVM_SIGNATURE_ENDFUNC; ) { char fieldchar = signature_to_fieldtype(context, &p, &full_info); switch (fieldchar) { case'D': case'L':
*reg_ptr++ = full_info;
*reg_ptr++ = full_info + 1; break; default:
*reg_ptr++ = full_info; break;
}
}
p++; /* skip over right parenthesis */ if (*p == 'V') {
context->return_type = MAKE_FULLINFO(ITEM_Void, 0, 0);
} else {
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.