#include "config.h"
#include "verify.h"
#undef current_class
#ifdef VERIFY_DEBUG
#include <stdio.h>
#endif
#define INVALID_STATE ((state *) -1)
#ifdef VERIFY_DEBUG
static void
debug_print (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
}
#else
static void
debug_print (const char *fmt ATTRIBUTE_UNUSED, ...)
{
}
#endif
#define FLAG_INSN_START 1
#define FLAG_BRANCH_TARGET 2
#define FLAG_INSN_SEEN 4
struct state;
struct type;
struct ref_intersection;
typedef struct state state;
typedef struct type type;
typedef struct ref_intersection ref_intersection;
typedef struct state_list
{
state *val;
struct state_list *next;
} state_list;
typedef struct vfy_string_list
{
vfy_string val;
struct vfy_string_list *next;
} vfy_string_list;
typedef struct verifier_context
{
int PC;
int start_PC;
state *current_state;
state_list **states;
state *next_verify_state;
char *flags;
const unsigned char *bytecode;
vfy_exception *exception;
vfy_jclass current_class;
vfy_method *current_method;
vfy_string_list *utf8_list;
ref_intersection *isect_list;
} verifier_context;
static GTY(()) verifier_context *vfr;
bool type_initialized (type *t);
int ref_count_dimensions (ref_intersection *ref);
static void
verify_fail_pc (const char *s, int pc)
{
vfy_fail (s, pc, vfr->current_class, vfr->current_method);
}
static void
verify_fail (const char *s)
{
verify_fail_pc (s, vfr->PC);
}
typedef enum type_val
{
void_type,
boolean_type = 4,
char_type = 5,
float_type = 6,
double_type = 7,
byte_type = 8,
short_type = 9,
int_type = 10,
long_type = 11,
unsuitable_type,
return_address_type,
continuation_type,
reference_type,
null_type,
uninitialized_reference_type
} type_val;
struct ref_intersection
{
bool is_resolved;
union
{
vfy_jclass klass;
vfy_string name;
} data;
ref_intersection *ref_next;
ref_intersection *alloc_next;
};
static ref_intersection *
make_ref (void)
{
ref_intersection *new_ref =
(ref_intersection *) vfy_alloc (sizeof (ref_intersection));
new_ref->alloc_next = vfr->isect_list;
vfr->isect_list = new_ref;
return new_ref;
}
static ref_intersection *
clone_ref (ref_intersection *dup)
{
ref_intersection *new_ref = make_ref ();
new_ref->is_resolved = dup->is_resolved;
new_ref->data = dup->data;
return new_ref;
}
static void
resolve_ref (ref_intersection *ref)
{
if (ref->is_resolved)
return;
ref->data.klass = vfy_find_class (vfr->current_class, ref->data.name);
ref->is_resolved = true;
}
static bool
refs_equal (ref_intersection *ref1, ref_intersection *ref2)
{
if (! ref1->is_resolved && ! ref2->is_resolved
&& vfy_strings_equal (ref1->data.name, ref2->data.name))
return true;
if (! ref1->is_resolved)
resolve_ref (ref1);
if (! ref2->is_resolved)
resolve_ref (ref2);
return ref1->data.klass == ref2->data.klass;
}
static ref_intersection *
merge_refs (ref_intersection *ref1, ref_intersection *ref2)
{
ref_intersection *tail = ref2;
for (; ref1 != NULL; ref1 = ref1->ref_next)
{
bool add = true;
ref_intersection *iter;
for (iter = ref2; iter != NULL; iter = iter->ref_next)
{
if (refs_equal (ref1, iter))
{
add = false;
break;
}
}
if (add)
{
ref_intersection *new_tail = clone_ref (ref1);
new_tail->ref_next = tail;
tail = new_tail;
}
}
return tail;
}
static bool
ref_compatible (ref_intersection *target, ref_intersection *source)
{
for (; target != NULL; target = target->ref_next)
{
ref_intersection *source_iter = source;
for (; source_iter != NULL; source_iter = source_iter->ref_next)
{
if (! target->is_resolved
&& ! source_iter->is_resolved
&& vfy_strings_equal (target->data.name,
source_iter->data.name))
continue;
if (! target->is_resolved)
resolve_ref (target);
if (! source_iter->is_resolved)
resolve_ref (source_iter);
if (! vfy_is_assignable_from (target->data.klass,
source_iter->data.klass))
return false;
}
}
return true;
}
static bool
ref_isarray (ref_intersection *ref)
{
if (ref->is_resolved)
return vfy_is_array (ref->data.klass);
else
return vfy_string_bytes (ref->data.name)[0] == '[';
}
static bool
ref_isinterface (ref_intersection *ref)
{
if (! ref->is_resolved)
resolve_ref (ref);
return vfy_is_interface (ref->data.klass);
}
static bool
ref_isabstract (ref_intersection *ref)
{
if (! ref->is_resolved)
resolve_ref (ref);
return vfy_is_abstract (ref->data.klass);
}
static vfy_jclass
ref_getclass (ref_intersection *ref)
{
if (! ref->is_resolved)
resolve_ref (ref);
return ref->data.klass;
}
int
ref_count_dimensions (ref_intersection *ref)
{
int ndims = 0;
if (ref->is_resolved)
{
vfy_jclass k = ref->data.klass;
while (vfy_is_array (k))
{
k = vfy_get_component_type (k);
++ndims;
}
}
else
{
const char *p = vfy_string_bytes (ref->data.name);
while (*p++ == '[')
++ndims;
}
return ndims;
}
static type_val
get_type_val_for_signature (char sig)
{
type_val rt;
switch (sig)
{
case 'Z':
rt = boolean_type;
break;
case 'B':
rt = byte_type;
break;
case 'C':
rt = char_type;
break;
case 'S':
rt = short_type;
break;
case 'I':
rt = int_type;
break;
case 'J':
rt = long_type;
break;
case 'F':
rt = float_type;
break;
case 'D':
rt = double_type;
break;
case 'V':
rt = void_type;
break;
default:
verify_fail ("invalid signature");
return null_type;
}
return rt;
}
static type_val
get_type_val_for_primtype (vfy_jclass k)
{
return get_type_val_for_signature (vfy_get_primitive_char (k));
}
struct type
{
type_val key;
ref_intersection *klass;
int pc;
#define UNINIT -2
#define SELF -1
#define EITHER -3
};
static void
init_type_from_tag (type *t, type_val k)
{
t->key = k;
t->klass = NULL;
t->pc = UNINIT;
}
static type
make_type (type_val k)
{
type t;
init_type_from_tag (&t, k);
return t;
}
static void
init_type_from_class (type *t, vfy_jclass k)
{
t->key = reference_type;
t->klass = make_ref ();
t->klass->is_resolved = true;
t->klass->data.klass = k;
t->klass->ref_next = NULL;
t->pc = UNINIT;
}
static type
make_type_from_class (vfy_jclass k)
{
type t;
init_type_from_class (&t, k);
return t;
}
static void
init_type_from_string (type *t, vfy_string n)
{
t->key = reference_type;
t->klass = make_ref ();
t->klass->is_resolved = false;
t->klass->data.name = n;
t->klass->ref_next = NULL;
t->pc = UNINIT;
}
static type
make_type_from_string (vfy_string n)
{
type t;
init_type_from_string (&t, n);
return t;
}
static void
vfy_promote_type (type *t)
{
if (t->key == boolean_type || t->key == char_type
|| t->key == byte_type || t->key == short_type)
t->key = int_type;
}
#define promote_type vfy_promote_type
static void
type_set_uninitialized (type *t, int npc)
{
if (t->key == reference_type)
t->key = uninitialized_reference_type;
else
verify_fail ("internal error in type::uninitialized");
t->pc = npc;
}
static void
type_set_initialized (type *t, int npc)
{
if (npc != UNINIT && t->pc == npc && t->key == uninitialized_reference_type)
{
t->key = reference_type;
t->pc = UNINIT;
}
}
static void type_set_return_address (type *t, int npc)
{
t->pc = npc;
}
static bool
type_state_mergeable_p (type *t1, type *t2)
{
return (t1->key != return_address_type
|| t2->key != return_address_type
|| t1->pc == t2->pc);
}
static bool
types_compatible (type *t, type *k)
{
if (k->key == unsuitable_type)
return true;
if (t->key < reference_type || k->key < reference_type)
return t->key == k->key;
if (t->key == null_type)
return k->key != uninitialized_reference_type;
if (k->key == null_type)
return t->key != uninitialized_reference_type;
if (t->klass == NULL)
return true;
if (k->klass == NULL)
verify_fail ("programmer error in type::compatible");
if (! type_initialized (t) && t->pc == EITHER)
{
if (! type_initialized (k) && k->pc != SELF)
return false;
}
else if (type_initialized (t) != type_initialized (k))
{
return false;
}
else
{
if (type_initialized (t))
{
if (t->pc != k->pc && t->pc != UNINIT && k->pc != UNINIT)
return false;
}
}
return ref_compatible (t->klass, k->klass);
}
static bool
types_equal (type *t1, type *t2)
{
if ((t1->key != reference_type && t1->key != uninitialized_reference_type)
|| (t2->key != reference_type
&& t2->key != uninitialized_reference_type))
return false;
if (t1->klass->ref_next || t2->klass->ref_next)
return false;
return refs_equal (t1->klass, t2->klass);
}
static bool
type_isvoid (type *t)
{
return t->key == void_type;
}
static bool
type_iswide (type *t)
{
return t->key == long_type || t->key == double_type;
}
static int
type_depth (type *t)
{
return type_iswide (t) ? 2 : 1;
}
static bool
type_isarray (type *t)
{
if (t->key == reference_type)
return ref_isarray (t->klass);
return false;
}
static bool
type_isnull (type *t)
{
return t->key == null_type;
}
static bool
type_isinterface (type *t)
{
if (t->key != reference_type)
return false;
return ref_isinterface (t->klass);
}
static bool
type_isabstract (type *t)
{
if (t->key != reference_type)
return false;
return ref_isabstract (t->klass);
}
static type
type_array_element (type *t)
{
type et;
vfy_jclass k;
if (t->key != reference_type)
verify_fail ("programmer error in type::element_type()");
k = vfy_get_component_type (ref_getclass (t->klass));
if (vfy_is_primitive (k))
init_type_from_tag (&et, get_type_val_for_primtype (k));
else
init_type_from_class (&et, k);
return et;
}
static type
type_to_array (type *t)
{
type at;
vfy_jclass k;
if (t->key != reference_type)
verify_fail ("internal error in type::to_array()");
k = ref_getclass (t->klass);
init_type_from_class (&at, vfy_get_array_class (k));
return at;
}
static bool
type_isreference (type *t)
{
return t->key >= reference_type;
}
static int
type_get_pc (type *t)
{
return t->pc;
}
bool
type_initialized (type *t)
{
return t->key == reference_type || t->key == null_type;
}
static void
type_verify_dimensions (type *t, int ndims)
{
if (t->key != reference_type)
verify_fail ("internal error in verify_dimensions:"
" not a reference type");
if (ref_count_dimensions (t->klass) < ndims)
verify_fail ("array type has fewer dimensions"
" than required");
}
static bool
merge_types (type *t, type *old_type, bool local_semantics)
{
bool changed = false;
bool refo = type_isreference (old_type);
bool refn = type_isreference (t);
if (refo && refn)
{
if (old_type->key == null_type)
;
else if (t->key == null_type)
{
*t = *old_type;
changed = true;
}
else if (type_initialized (t) != type_initialized (old_type))
verify_fail ("merging initialized and uninitialized types");
else
{
ref_intersection *merged;
if (! type_initialized (t))
{
if (t->pc == UNINIT)
t->pc = old_type->pc;
else if (old_type->pc == UNINIT)
;
else if (t->pc != old_type->pc)
verify_fail ("merging different uninitialized types");
}
merged = merge_refs (old_type->klass, t->klass);
if (merged != t->klass)
{
t->klass = merged;
changed = true;
}
}
}
else if (refo || refn || t->key != old_type->key)
{
if (local_semantics)
{
if (t->key != unsuitable_type)
{
t->key = unsuitable_type;
changed = true;
}
}
else
verify_fail ("unmergeable type");
}
return changed;
}
#ifdef VERIFY_DEBUG
static void
type_print (type *t)
{
char c = '?';
switch (t->key)
{
case boolean_type: c = 'Z'; break;
case byte_type: c = 'B'; break;
case char_type: c = 'C'; break;
case short_type: c = 'S'; break;
case int_type: c = 'I'; break;
case long_type: c = 'J'; break;
case float_type: c = 'F'; break;
case double_type: c = 'D'; break;
case void_type: c = 'V'; break;
case unsuitable_type: c = '-'; break;
case return_address_type: c = 'r'; break;
case continuation_type: c = '+'; break;
case reference_type: c = 'L'; break;
case null_type: c = '@'; break;
case uninitialized_reference_type: c = 'U'; break;
}
debug_print ("%c", c);
}
#endif
struct state
{
int stacktop;
int stackdepth;
type *stack;
type *locals;
type this_type;
int pc;
state *next;
};
#define NO_NEXT -1
static void
init_state_with_stack (state *s, int max_stack, int max_locals)
{
int i;
s->stacktop = 0;
s->stackdepth = 0;
s->stack = (type *) vfy_alloc (max_stack * sizeof (type));
for (i = 0; i < max_stack; ++i)
init_type_from_tag (&s->stack[i], unsuitable_type);
s->locals = (type *) vfy_alloc (max_locals * sizeof (type));
for (i = 0; i < max_locals; ++i)
init_type_from_tag (&s->locals[i], unsuitable_type);
init_type_from_tag (&s->this_type, unsuitable_type);
s->pc = NO_NEXT;
s->next = INVALID_STATE;
}
static void
copy_state (state *s, state *copy, int max_stack, int max_locals)
{
int i;
s->stacktop = copy->stacktop;
s->stackdepth = copy->stackdepth;
for (i = 0; i < max_stack; ++i)
s->stack[i] = copy->stack[i];
for (i = 0; i < max_locals; ++i)
s->locals[i] = copy->locals[i];
s->this_type = copy->this_type;
}
static void
copy_state_with_stack (state *s, state *orig, int max_stack, int max_locals)
{
init_state_with_stack (s, max_stack, max_locals);
copy_state (s, orig, max_stack, max_locals);
}
static state *
make_state_copy (state *orig, int max_stack, int max_locals)
{
state *s = vfy_alloc (sizeof (state));
copy_state_with_stack (s, orig, max_stack, max_locals);
return s;
}
static state *
make_state (int max_stack, int max_locals)
{
state *s = vfy_alloc (sizeof (state));
init_state_with_stack (s, max_stack, max_locals);
return s;
}
static void
free_state (state *s)
{
if (s->stack != NULL)
vfy_free (s->stack);
if (s->locals != NULL)
vfy_free (s->locals);
}
static void
state_set_exception (state *s, type *t, int max_stack)
{
int i;
s->stackdepth = 1;
s->stacktop = 1;
s->stack[0] = *t;
for (i = s->stacktop; i < max_stack; ++i)
init_type_from_tag (&s->stack[i], unsuitable_type);
}
static bool
merge_states (state *s, state *state_old, int max_locals)
{
int i;
bool changed = false;
if (type_initialized (&s->this_type))
s->this_type = state_old->this_type;
if (state_old->stacktop != s->stacktop)
verify_fail ("stack sizes differ");
for (i = 0; i < state_old->stacktop; ++i)
{
if (merge_types (&s->stack[i], &state_old->stack[i], false))
changed = true;
}
for (i = 0; i < max_locals; ++i)
{
if (merge_types (&s->locals[i], &state_old->locals[i], true))
changed = true;
}
return changed;
}
static void
state_check_this_initialized (state *s)
{
if (type_isreference (&s->this_type) && ! type_initialized (&s->this_type))
verify_fail ("`this' is uninitialized");
}
static void
state_set_this_type (state *s, type *k)
{
s->this_type = *k;
}
static void
state_set_initialized (state *s, int pc, int max_locals)
{
int i;
for (i = 0; i < s->stacktop; ++i)
type_set_initialized (&s->stack[i], pc);
for (i = 0; i < max_locals; ++i)
type_set_initialized (&s->locals[i], pc);
type_set_initialized (&s->this_type, pc);
}
static bool
state_mergeable_p (state *s, state *other, int max_locals)
{
int i;
if (s->stacktop != other->stacktop)
verify_fail ("stack sizes differ");
for (i = 0; i < s->stacktop; ++i)
if (! type_state_mergeable_p (&s->stack[i], &other->stack[i]))
return false;
for (i = 0; i < max_locals; ++i)
if (! type_state_mergeable_p (&s->locals[i], &other->locals[i]))
return false;
return true;
}
static void
state_reverify (state *s)
{
if (s->next == INVALID_STATE)
{
s->next = vfr->next_verify_state;
vfr->next_verify_state = s;
}
}
#ifdef VERIFY_DEBUG
static void
debug_print_state (state *s, const char *leader, int pc, int max_stack,
int max_locals)
{
int i;
debug_print ("%s [%4d]: [stack] ", leader, pc);
for (i = 0; i < s->stacktop; ++i)
type_print (&s->stack[i]);
for (; i < max_stack; ++i)
debug_print (".");
debug_print (" [local] ");
for (i = 0; i < max_locals; ++i)
type_print (&s->locals[i]);
debug_print (" | %p\n", s);
}
#else
static void
debug_print_state (state *s ATTRIBUTE_UNUSED,
const char *leader ATTRIBUTE_UNUSED,
int pc ATTRIBUTE_UNUSED, int max_stack ATTRIBUTE_UNUSED,
int max_locals ATTRIBUTE_UNUSED)
{
}
#endif
static type
pop_raw (void)
{
type r;
state *s = vfr->current_state;
if (s->stacktop <= 0)
verify_fail ("stack empty");
r = s->stack[--s->stacktop];
s->stackdepth -= type_depth (&r);
if (s->stackdepth < 0)
verify_fail_pc ("stack empty", vfr->start_PC);
return r;
}
static type
pop32 (void)
{
type r = pop_raw ();
if (type_iswide (&r))
verify_fail ("narrow pop of wide type");
return r;
}
static type
vfy_pop_type_t (type match)
{
type t;
vfy_promote_type (&match);
t = pop_raw ();
if (! types_compatible (&match, &t))
verify_fail ("incompatible type on stack");
return t;
}
static type
vfy_pop_type (type_val match)
{
type t = make_type (match);
return vfy_pop_type_t (t);
}
#define pop_type vfy_pop_type
#define pop_type_t vfy_pop_type_t
static type
pop_init_ref_t (type match)
{
type t = pop_raw ();
if (type_isreference (&t) && ! type_initialized (&t))
verify_fail ("initialized reference required");
else if (! types_compatible (&match, &t))
verify_fail ("incompatible type on stack");
return t;
}
static type
pop_init_ref (type_val match)
{
type t = make_type (match);
return pop_init_ref_t (t);
}
static type
pop_ref_or_return (void)
{
type t = pop_raw ();
if (! type_isreference (&t) && t.key != return_address_type)
verify_fail ("expected reference or return address on stack");
return t;
}
static void
vfy_push_type_t (type t)
{
int depth;
state *s = vfr->current_state;
promote_type (&t);
depth = type_depth (&t);
if (s->stackdepth + depth > vfr->current_method->max_stack)
verify_fail ("stack overflow");
s->stack[s->stacktop++] = t;
s->stackdepth += depth;
}
static void
vfy_push_type (type_val tval)
{
type t = make_type (tval);
vfy_push_type_t (t);
}
#define push_type vfy_push_type
#define push_type_t vfy_push_type_t
static void
set_variable (int index, type t)
{
int depth;
state *s = vfr->current_state;
promote_type (&t);
depth = type_depth (&t);
if (index > vfr->current_method->max_locals - depth)
verify_fail ("invalid local variable");
s->locals[index] = t;
if (depth == 2)
init_type_from_tag (&s->locals[index + 1], continuation_type);
if (index > 0 && type_iswide (&s->locals[index - 1]))
init_type_from_tag (&s->locals[index - 1], unsuitable_type);
}
static type
get_variable_t (int index, type *t)
{
state *s = vfr->current_state;
int depth = type_depth (t);
if (index > vfr->current_method->max_locals - depth)
verify_fail ("invalid local variable");
if (! types_compatible (t, &s->locals[index]))
verify_fail ("incompatible type in local variable");
if (depth == 2)
{
type cont = make_type (continuation_type);
if (! types_compatible (&s->locals[index + 1], &cont))
verify_fail ("invalid local variable");
}
return s->locals[index];
}
static type
get_variable (int index, type_val v)
{
type t = make_type (v);
return get_variable_t (index, &t);
}
static type
require_array_type_t (type array, type element)
{
type t;
if (type_isnull (&array))
return type_isreference (&element) ? make_type (null_type) : element;
if (! type_isarray (&array))
verify_fail ("array required");
t = type_array_element (&array);
if (! types_compatible (&element, &t))
{
bool ok = true;
if (element.key == byte_type)
{
type e2 = make_type (boolean_type);
ok = types_compatible (&e2, &t);
}
if (! ok)
verify_fail ("incompatible array element type");
}
return t;
}
static type
require_array_type (type array, type_val element)
{
type t = make_type (element);
return require_array_type_t (array, t);
}
static jint
get_byte (void)
{
if (vfr->PC >= vfr->current_method->code_length)
verify_fail ("premature end of bytecode");
return (jint) vfr->bytecode[vfr->PC++] & 0xff;
}
static jint
get_ushort (void)
{
jint b1 = get_byte ();
jint b2 = get_byte ();
return (jint) ((b1 << 8) | b2) & 0xffff;
}
static jint
get_short (void)
{
signed char b1 = (signed char) get_byte ();
jint b2 = get_byte ();
jshort s = (b1 << 8) | b2;
return (jint) s;
}
static jint
get_int (void)
{
jint b1 = get_byte ();
jint b2 = get_byte ();
jint b3 = get_byte ();
jint b4 = get_byte ();
jword result = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
return WORD_TO_INT (result);
}
static int
compute_jump (int offset)
{
int npc = vfr->start_PC + offset;
if (npc < 0 || npc >= vfr->current_method->code_length)
verify_fail_pc ("branch out of range", vfr->start_PC);
return npc;
}
static state *
add_new_state (int npc, state *old_state)
{
state_list *nlink;
vfy_method *current_method = vfr->current_method;
state *new_state = make_state_copy (old_state, current_method->max_stack,
current_method->max_locals);
debug_print ("== New state in add_new_state\n");
debug_print_state (new_state, "New", npc, current_method->max_stack,
current_method->max_locals);
nlink = vfy_alloc (sizeof (state_list));
nlink->val = new_state;
nlink->next = vfr->states[npc];
vfr->states[npc] = nlink;
new_state->pc = npc;
return new_state;
}
static void
merge_into (int npc, state *from_state)
{
state_list *iter;
bool applicable = false;
for (iter = vfr->states[npc]; iter != NULL; iter = iter->next)
{
state *new_state = iter->val;
vfy_method *current_method = vfr->current_method;
if (state_mergeable_p (new_state, from_state,
current_method->max_locals))
{
bool changed;
applicable = true;
debug_print ("== Merge states in merge_into\n");
debug_print_state (from_state, "Frm", vfr->start_PC, current_method->max_stack,
current_method->max_locals);
debug_print_state (new_state, " To", npc, current_method->max_stack,
current_method->max_locals);
changed = merge_states (new_state, from_state,
current_method->max_locals);
debug_print_state (new_state, "New", npc, current_method->max_stack,
current_method->max_locals);
if (changed)
state_reverify (new_state);
}
}
if (! applicable)
{
state *new_state = add_new_state (npc, from_state);
state_reverify (new_state);
}
}
static void
push_jump (int offset)
{
int npc = compute_jump (offset);
merge_into (npc, vfr->current_state);
}
static void
push_exception_jump (type t, int pc)
{
state s;
copy_state_with_stack (&s, vfr->current_state,
vfr->current_method->max_stack,
vfr->current_method->max_locals);
if (vfr->current_method->max_stack < 1)
verify_fail ("stack overflow at exception handler");
state_set_exception (&s, &t, vfr->current_method->max_stack);
merge_into (pc, &s);
}
static state *
pop_jump (void)
{
state *new_state = vfr->next_verify_state;
if (new_state == INVALID_STATE)
verify_fail ("programmer error in pop_jump");
if (new_state != NULL)
{
vfr->next_verify_state = new_state->next;
new_state->next = INVALID_STATE;
}
return new_state;
}
static void
invalidate_pc (void)
{
vfr->PC = NO_NEXT;
}
static void
note_branch_target (int pc)
{
if (pc < vfr->PC && ! (vfr->flags[pc] & FLAG_INSN_START))
verify_fail_pc ("branch not to instruction start", vfr->start_PC);
vfr->flags[pc] |= FLAG_BRANCH_TARGET;
}
static void
skip_padding (void)
{
while ((vfr->PC % 4) > 0)
if (get_byte () != 0)
verify_fail ("found nonzero padding byte");
}
static void
handle_ret_insn (int index)
{
type ret = make_type (return_address_type);
type ret_addr = get_variable_t (index, &ret);
int npc = type_get_pc (&ret_addr);
if (npc >= vfr->current_method->code_length)
verify_fail ("fell off end");
merge_into (npc, vfr->current_state);
invalidate_pc ();
}
static void handle_jsr_insn (int offset)
{
type ret_addr;
int npc = compute_jump (offset);
ret_addr = make_type (return_address_type);
type_set_return_address (&ret_addr, vfr->PC);
vfy_push_type_t (ret_addr);
merge_into (npc, vfr->current_state);
invalidate_pc ();
}
static vfy_jclass
construct_primitive_array_type (type_val prim)
{
vfy_jclass k = NULL;
switch (prim)
{
case boolean_type:
case char_type:
case float_type:
case double_type:
case byte_type:
case short_type:
case int_type:
case long_type:
k = vfy_get_primitive_type ((int) prim);
break;
case void_type:
case unsuitable_type:
case return_address_type:
case continuation_type:
case reference_type:
case null_type:
case uninitialized_reference_type:
default:
verify_fail ("unknown type in construct_primitive_array_type");
}
k = vfy_get_array_class (k);
return k;
}
static void
branch_prepass (void)
{
int i, pc;
vfr->flags = (char *) vfy_alloc (vfr->current_method->code_length);
for (i = 0; i < vfr->current_method->code_length; ++i)
vfr->flags[i] = 0;
vfr->PC = 0;
while (vfr->PC < vfr->current_method->code_length)
{
java_opcode opcode;
vfr->start_PC = vfr->PC;
vfr->flags[vfr->PC] |= FLAG_INSN_START;
opcode = (java_opcode) vfr->bytecode[vfr->PC++];
switch (opcode)
{
case op_nop:
case op_aconst_null:
case op_iconst_m1:
case op_iconst_0:
case op_iconst_1:
case op_iconst_2:
case op_iconst_3:
case op_iconst_4:
case op_iconst_5:
case op_lconst_0:
case op_lconst_1:
case op_fconst_0:
case op_fconst_1:
case op_fconst_2:
case op_dconst_0:
case op_dconst_1:
case op_iload_0:
case op_iload_1:
case op_iload_2:
case op_iload_3:
case op_lload_0:
case op_lload_1:
case op_lload_2:
case op_lload_3:
case op_fload_0:
case op_fload_1:
case op_fload_2:
case op_fload_3:
case op_dload_0:
case op_dload_1:
case op_dload_2:
case op_dload_3:
case op_aload_0:
case op_aload_1:
case op_aload_2:
case op_aload_3:
case op_iaload:
case op_laload:
case op_faload:
case op_daload:
case op_aaload:
case op_baload:
case op_caload:
case op_saload:
case op_istore_0:
case op_istore_1:
case op_istore_2:
case op_istore_3:
case op_lstore_0:
case op_lstore_1:
case op_lstore_2:
case op_lstore_3:
case op_fstore_0:
case op_fstore_1:
case op_fstore_2:
case op_fstore_3:
case op_dstore_0:
case op_dstore_1:
case op_dstore_2:
case op_dstore_3:
case op_astore_0:
case op_astore_1:
case op_astore_2:
case op_astore_3:
case op_iastore:
case op_lastore:
case op_fastore:
case op_dastore:
case op_aastore:
case op_bastore:
case op_castore:
case op_sastore:
case op_pop:
case op_pop2:
case op_dup:
case op_dup_x1:
case op_dup_x2:
case op_dup2:
case op_dup2_x1:
case op_dup2_x2:
case op_swap:
case op_iadd:
case op_isub:
case op_imul:
case op_idiv:
case op_irem:
case op_ishl:
case op_ishr:
case op_iushr:
case op_iand:
case op_ior:
case op_ixor:
case op_ladd:
case op_lsub:
case op_lmul:
case op_ldiv:
case op_lrem:
case op_lshl:
case op_lshr:
case op_lushr:
case op_land:
case op_lor:
case op_lxor:
case op_fadd:
case op_fsub:
case op_fmul:
case op_fdiv:
case op_frem:
case op_dadd:
case op_dsub:
case op_dmul:
case op_ddiv:
case op_drem:
case op_ineg:
case op_i2b:
case op_i2c:
case op_i2s:
case op_lneg:
case op_fneg:
case op_dneg:
case op_i2l:
case op_i2f:
case op_i2d:
case op_l2i:
case op_l2f:
case op_l2d:
case op_f2i:
case op_f2l:
case op_f2d:
case op_d2i:
case op_d2l:
case op_d2f:
case op_lcmp:
case op_fcmpl:
case op_fcmpg:
case op_dcmpl:
case op_dcmpg:
case op_monitorenter:
case op_monitorexit:
case op_ireturn:
case op_lreturn:
case op_freturn:
case op_dreturn:
case op_areturn:
case op_return:
case op_athrow:
case op_arraylength:
break;
case op_bipush:
case op_ldc:
case op_iload:
case op_lload:
case op_fload:
case op_dload:
case op_aload:
case op_istore:
case op_lstore:
case op_fstore:
case op_dstore:
case op_astore:
case op_ret:
case op_newarray:
get_byte ();
break;
case op_iinc:
case op_sipush:
case op_ldc_w:
case op_ldc2_w:
case op_getstatic:
case op_getfield:
case op_putfield:
case op_putstatic:
case op_new:
case op_anewarray:
case op_instanceof:
case op_checkcast:
case op_invokespecial:
case op_invokestatic:
case op_invokevirtual:
get_short ();
break;
case op_multianewarray:
get_short ();
get_byte ();
break;
case op_jsr:
case op_ifeq:
case op_ifne:
case op_iflt:
case op_ifge:
case op_ifgt:
case op_ifle:
case op_if_icmpeq:
case op_if_icmpne:
case op_if_icmplt:
case op_if_icmpge:
case op_if_icmpgt:
case op_if_icmple:
case op_if_acmpeq:
case op_if_acmpne:
case op_ifnull:
case op_ifnonnull:
case op_goto:
note_branch_target (compute_jump (get_short ()));
break;
case op_tableswitch:
{
jint low, hi;
skip_padding ();
note_branch_target (compute_jump (get_int ()));
low = get_int ();
hi = get_int ();
if (low > hi)
verify_fail_pc ("invalid tableswitch", vfr->start_PC);
for (i = low; i <= hi; ++i)
note_branch_target (compute_jump (get_int ()));
}
break;
case op_lookupswitch:
{
int npairs;
skip_padding ();
note_branch_target (compute_jump (get_int ()));
npairs = get_int ();
if (npairs < 0)
verify_fail_pc ("too few pairs in lookupswitch", vfr->start_PC);
while (npairs-- > 0)
{
get_int ();
note_branch_target (compute_jump (get_int ()));
}
}
break;
case op_invokeinterface:
get_short ();
get_byte ();
get_byte ();
break;
case op_wide:
{
opcode = (java_opcode) get_byte ();
get_short ();
if (opcode == op_iinc)
get_short ();
}
break;
case op_jsr_w:
case op_goto_w:
note_branch_target (compute_jump (get_int ()));
break;
#if 0
case op_putfield_1:
case op_putfield_2:
case op_putfield_4:
case op_putfield_8:
case op_putfield_a:
case op_putstatic_1:
case op_putstatic_2:
case op_putstatic_4:
case op_putstatic_8:
case op_putstatic_a:
case op_getfield_1:
case op_getfield_2s:
case op_getfield_2u:
case op_getfield_4:
case op_getfield_8:
case op_getfield_a:
case op_getstatic_1:
case op_getstatic_2s:
case op_getstatic_2u:
case op_getstatic_4:
case op_getstatic_8:
case op_getstatic_a:
#endif
default:
verify_fail_pc ("unrecognized instruction in branch_prepass",
vfr->start_PC);
}
for (pc = vfr->start_PC + 1; pc < vfr->PC; ++pc)
{
if ((vfr->flags[pc] & FLAG_BRANCH_TARGET))
verify_fail_pc ("branch to middle of instruction", pc);
}
}
for (i = 0; i < vfr->current_method->exc_count; ++i)
{
int handler, start, end, htype;
vfy_get_exception (vfr->exception, i, &handler, &start, &end, &htype);
if (! (vfr->flags[handler] & FLAG_INSN_START))
verify_fail_pc ("exception handler not at instruction start",
handler);
if (! (vfr->flags[start] & FLAG_INSN_START))
verify_fail_pc ("exception start not at instruction start", start);
if (end != vfr->current_method->code_length
&& ! (vfr->flags[end] & FLAG_INSN_START))
verify_fail_pc ("exception end not at instruction start", end);
vfr->flags[handler] |= FLAG_BRANCH_TARGET;
}
}
static void
check_pool_index (int index)
{
if (index < 0 || index >= vfy_get_constants_size (vfr->current_class))
verify_fail_pc ("constant pool index out of range", vfr->start_PC);
}
static type
check_class_constant (int index)
{
type t;
vfy_constants *pool;
check_pool_index (index);
pool = vfy_get_constants (vfr->current_class);
if (vfy_tag (pool, index) == JV_CONSTANT_ResolvedClass)
init_type_from_class (&t, vfy_get_pool_class (pool, index));
else if (vfy_tag (pool, index) == JV_CONSTANT_Class)
init_type_from_string (&t, vfy_get_pool_string (pool, index));
else
verify_fail_pc ("expected class constant", vfr->start_PC);
return t;
}
static type
check_constant (int index)
{
type t;
vfy_constants *pool;
check_pool_index (index);
pool = vfy_get_constants (vfr->current_class);
if (vfy_tag (pool, index) == JV_CONSTANT_ResolvedString
|| vfy_tag (pool, index) == JV_CONSTANT_String)
init_type_from_class (&t, vfy_string_type ());
else if (vfy_tag (pool, index) == JV_CONSTANT_Integer)
init_type_from_tag (&t, int_type);
else if (vfy_tag (pool, index) == JV_CONSTANT_Float)
init_type_from_tag (&t, float_type);
else
verify_fail_pc ("String, int, or float constant expected", vfr->start_PC);
return t;
}
static type
check_wide_constant (int index)
{
type t;
vfy_constants *pool;
check_pool_index (index);
pool = vfy_get_constants (vfr->current_class);
if (vfy_tag (pool, index) == JV_CONSTANT_Long)
init_type_from_tag (&t, long_type);
else if (vfy_tag (pool, index) == JV_CONSTANT_Double)
init_type_from_tag (&t, double_type);
else
verify_fail_pc ("long or double constant expected", vfr->start_PC);
return t;
}
static type
handle_field_or_method (int index, int expected,
vfy_string *name, vfy_string *fmtype)
{
vfy_uint_16 class_index, name_and_type_index;
vfy_uint_16 name_index, desc_index;
vfy_constants *pool;
check_pool_index (index);
pool = vfy_get_constants (vfr->current_class);
if (vfy_tag (pool, index) != expected)
verify_fail_pc ("didn't see expected constant", vfr->start_PC);
vfy_load_indexes (pool, index, &class_index, &name_and_type_index);
vfy_load_indexes (pool, name_and_type_index, &name_index, &desc_index);
*name = vfy_get_pool_string (pool, name_index);
*fmtype = vfy_get_pool_string (pool, desc_index);
return check_class_constant (class_index);
}
static type
check_field_constant (int index, type *class_type, bool putfield)
{
vfy_string name, field_type;
const char *typec;
int len;
type t;
type ct = handle_field_or_method (index,
JV_CONSTANT_Fieldref,
&name, &field_type);
if (class_type)
*class_type = ct;
typec = vfy_string_bytes (field_type);
len = vfy_string_length (field_type);
if (typec[0] == '[' || typec[0] == 'L')
init_type_from_string (&t, field_type);
else
init_type_from_tag (&t, get_type_val_for_signature (typec[0]));
if (putfield
&& ! type_initialized (&vfr->current_state->this_type)
&& vfr->current_state->this_type.pc == SELF
&& types_equal (&vfr->current_state->this_type, &ct)
&& vfy_class_has_field (vfr->current_class, name, field_type))
type_set_uninitialized (class_type, EITHER);
return t;
}
static type
check_method_constant (int index, bool is_interface,
vfy_string *method_name,
vfy_string *method_signature)
{
return handle_field_or_method (index,
(is_interface
? JV_CONSTANT_InterfaceMethodref
: JV_CONSTANT_Methodref),
method_name, method_signature);
}
static char *
get_one_type (char *p, type *t)
{
const char *start = p;
vfy_jclass k;
type_val rt;
char v;
int arraycount = 0;
while (*p == '[')
{
++arraycount;
++p;
}
v = *p++;
if (v == 'L')
{
vfy_string name;
while (*p != ';')
++p;
++p;
name = vfy_get_string (start, p - start);
*t = make_type_from_string (name);
return p;
}
rt = get_type_val_for_signature (v);
if (arraycount == 0)
{
type new_t = make_type (rt);
vfy_promote_type (&new_t);
*t = new_t;
return p;
}
k = construct_primitive_array_type (rt);
while (--arraycount > 0)
k = vfy_get_array_class (k);
*t = make_type_from_class (k);
return p;
}
static void
compute_argument_types (vfy_string signature, type *types)
{
int i;
char *p = (char *) vfy_string_bytes (signature);
++p;
i = 0;
while (*p != ')')
p = get_one_type (p, &types[i++]);
}
static type
compute_return_type (vfy_string signature)
{
char *p = (char *) vfy_string_bytes (signature);
type t;
while (*p != ')')
++p;
++p;
get_one_type (p, &t);
return t;
}
static void
check_return_type (type onstack)
{
type rt = compute_return_type (vfy_get_signature (vfr->current_method));
if (! types_compatible (&rt, &onstack))
verify_fail ("incompatible return type");
}
static bool
initialize_stack (void)
{
int arg_count, i;
int var = 0;
bool is_init = vfy_strings_equal (vfy_get_method_name (vfr->current_method),
vfy_init_name());
bool is_clinit = vfy_strings_equal (vfy_get_method_name (vfr->current_method),
vfy_clinit_name());
if (! vfy_is_static (vfr->current_method))
{
type kurr = make_type_from_class (vfr->current_class);
if (is_init)
{
type_set_uninitialized (&kurr, SELF);
is_init = true;
}
else if (is_clinit)
verify_fail ("<clinit> method must be static");
set_variable (0, kurr);
state_set_this_type (vfr->current_state, &kurr);
++var;
}
else
{
if (is_init)
verify_fail ("<init> method must be non-static");
}
arg_count = vfy_count_arguments (vfy_get_signature (vfr->current_method));
{
type *arg_types = (type *) vfy_alloc (arg_count * sizeof (type));
compute_argument_types (vfy_get_signature (vfr->current_method), arg_types);
for (i = 0; i < arg_count; ++i)
{
set_variable (var, arg_types[i]);
++var;
if (type_iswide (&arg_types[i]))
++var;
}
vfy_free (arg_types);
}
return is_init;
}
static void
verify_instructions_0 (void)
{
int i;
bool this_is_init;
vfr->current_state = make_state (vfr->current_method->max_stack,
vfr->current_method->max_locals);
vfr->PC = 0;
vfr->start_PC = 0;
this_is_init = initialize_stack ();
vfr->states = (state_list **) vfy_alloc (sizeof (state_list *)
* vfr->current_method->code_length);
for (i = 0; i < vfr->current_method->code_length; ++i)
vfr->states[i] = NULL;
vfr->next_verify_state = NULL;
while (true)
{
java_opcode opcode;
if (vfr->PC == NO_NEXT)
{
state *new_state = pop_jump ();
if (new_state == NULL)
break;
vfr->PC = new_state->pc;
debug_print ("== State pop from pending list\n");
copy_state (vfr->current_state, new_state,
vfr->current_method->max_stack, vfr->current_method->max_locals);
}
else
{
if (vfr->states[vfr->PC] != NULL)
{
merge_into (vfr->PC, vfr->current_state);
invalidate_pc ();
continue;
}
}
if (vfr->PC >= vfr->current_method->code_length)
verify_fail ("fell off end");
vfr->flags[vfr->PC] |= FLAG_INSN_SEEN;
if (vfr->states[vfr->PC] == NULL
&& (vfr->PC == 0 || (vfr->flags[vfr->PC] & FLAG_BRANCH_TARGET) != 0))
add_new_state (vfr->PC, vfr->current_state);
vfr->start_PC = vfr->PC;
for (i = 0; i < vfr->current_method->exc_count; ++i)
{
int hpc, start, end, htype;
vfy_get_exception (vfr->exception, i, &hpc, &start, &end, &htype);
if (vfr->PC >= start && vfr->PC < end)
{
type handler = make_type_from_class (vfy_throwable_type ());
if (htype != 0)
handler = check_class_constant (htype);
push_exception_jump (handler, hpc);
}
}
debug_print_state (vfr->current_state, " ", vfr->PC,
vfr->current_method->max_stack,
vfr->current_method->max_locals);
opcode = (java_opcode) vfr->bytecode[vfr->PC++];
switch (opcode)
{
case op_nop:
break;
case op_aconst_null:
push_type (null_type);
break;
case op_iconst_m1:
case op_iconst_0:
case op_iconst_1:
case op_iconst_2:
case op_iconst_3:
case op_iconst_4:
case op_iconst_5:
push_type (int_type);
break;
case op_lconst_0:
case op_lconst_1:
push_type (long_type);
break;
case op_fconst_0:
case op_fconst_1:
case op_fconst_2:
push_type (float_type);
break;
case op_dconst_0:
case op_dconst_1:
push_type (double_type);
break;
case op_bipush:
get_byte ();
push_type (int_type);
break;
case op_sipush:
get_short ();
push_type (int_type);
break;
case op_ldc:
push_type_t (check_constant (get_byte ()));
break;
case op_ldc_w:
push_type_t (check_constant (get_ushort ()));
break;
case op_ldc2_w:
push_type_t (check_wide_constant (get_ushort ()));
break;
case op_iload:
push_type_t (get_variable (get_byte (), int_type));
break;
case op_lload:
push_type_t (get_variable (get_byte (), long_type));
break;
case op_fload:
push_type_t (get_variable (get_byte (), float_type));
break;
case op_dload:
push_type_t (get_variable (get_byte (), double_type));
break;
case op_aload:
push_type_t (get_variable (get_byte (), reference_type));
break;
case op_iload_0:
case op_iload_1:
case op_iload_2:
case op_iload_3:
push_type_t (get_variable (opcode - op_iload_0, int_type));
break;
case op_lload_0:
case op_lload_1:
case op_lload_2:
case op_lload_3:
push_type_t (get_variable (opcode - op_lload_0, long_type));
break;
case op_fload_0:
case op_fload_1:
case op_fload_2:
case op_fload_3:
push_type_t (get_variable (opcode - op_fload_0, float_type));
break;
case op_dload_0:
case op_dload_1:
case op_dload_2:
case op_dload_3:
push_type_t (get_variable (opcode - op_dload_0, double_type));
break;
case op_aload_0:
case op_aload_1:
case op_aload_2:
case op_aload_3:
push_type_t (get_variable (opcode - op_aload_0, reference_type));
break;
case op_iaload:
pop_type (int_type);
push_type_t (require_array_type (pop_init_ref (reference_type),
int_type));
break;
case op_laload:
pop_type (int_type);
push_type_t (require_array_type (pop_init_ref (reference_type),
long_type));
break;
case op_faload:
pop_type (int_type);
push_type_t (require_array_type (pop_init_ref (reference_type),
float_type));
break;
case op_daload:
pop_type (int_type);
push_type_t (require_array_type (pop_init_ref (reference_type),
double_type));
break;
case op_aaload:
pop_type (int_type);
push_type_t (require_array_type (pop_init_ref (reference_type),
reference_type));
break;
case op_baload:
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), byte_type);
push_type (int_type);
break;
case op_caload:
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), char_type);
push_type (int_type);
break;
case op_saload:
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), short_type);
push_type (int_type);
break;
case op_istore:
set_variable (get_byte (), pop_type (int_type));
break;
case op_lstore:
set_variable (get_byte (), pop_type (long_type));
break;
case op_fstore:
set_variable (get_byte (), pop_type (float_type));
break;
case op_dstore:
set_variable (get_byte (), pop_type (double_type));
break;
case op_astore:
set_variable (get_byte (), pop_ref_or_return ());
break;
case op_istore_0:
case op_istore_1:
case op_istore_2:
case op_istore_3:
set_variable (opcode - op_istore_0, pop_type (int_type));
break;
case op_lstore_0:
case op_lstore_1:
case op_lstore_2:
case op_lstore_3:
set_variable (opcode - op_lstore_0, pop_type (long_type));
break;
case op_fstore_0:
case op_fstore_1:
case op_fstore_2:
case op_fstore_3:
set_variable (opcode - op_fstore_0, pop_type (float_type));
break;
case op_dstore_0:
case op_dstore_1:
case op_dstore_2:
case op_dstore_3:
set_variable (opcode - op_dstore_0, pop_type (double_type));
break;
case op_astore_0:
case op_astore_1:
case op_astore_2:
case op_astore_3:
set_variable (opcode - op_astore_0, pop_ref_or_return ());
break;
case op_iastore:
pop_type (int_type);
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), int_type);
break;
case op_lastore:
pop_type (long_type);
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), long_type);
break;
case op_fastore:
pop_type (float_type);
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), float_type);
break;
case op_dastore:
pop_type (double_type);
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), double_type);
break;
case op_aastore:
pop_type (reference_type);
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), reference_type);
break;
case op_bastore:
pop_type (int_type);
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), byte_type);
break;
case op_castore:
pop_type (int_type);
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), char_type);
break;
case op_sastore:
pop_type (int_type);
pop_type (int_type);
require_array_type (pop_init_ref (reference_type), short_type);
break;
case op_pop:
pop32 ();
break;
case op_pop2:
{
type t = pop_raw ();
if (! type_iswide (&t))
pop32 ();
}
break;
case op_dup:
{
type t = pop32 ();
push_type_t (t);
push_type_t (t);
}
break;
case op_dup_x1:
{
type t1 = pop32 ();
type t2 = pop32 ();
push_type_t (t1);
push_type_t (t2);
push_type_t (t1);
}
break;
case op_dup_x2:
{
type t1 = pop32 ();
type t2 = pop_raw ();
if (! type_iswide (&t2))
{
type t3 = pop32 ();
push_type_t (t1);
push_type_t (t3);
}
else
push_type_t (t1);
push_type_t (t2);
push_type_t (t1);
}
break;
case op_dup2:
{
type t = pop_raw ();
if (! type_iswide (&t))
{
type t2 = pop32 ();
push_type_t (t2);
push_type_t (t);
push_type_t (t2);
}
else
push_type_t (t);
push_type_t (t);
}
break;
case op_dup2_x1:
{
type t1 = pop_raw ();
type t2 = pop32 ();
if (! type_iswide (&t1))
{
type t3 = pop32 ();
push_type_t (t2);
push_type_t (t1);
push_type_t (t3);
}
else
push_type_t (t1);
push_type_t (t2);
push_type_t (t1);
}
break;
case op_dup2_x2:
{
type t1 = pop_raw ();
if (type_iswide (&t1))
{
type t2 = pop_raw ();
if (type_iswide (&t2))
{
push_type_t (t1);
push_type_t (t2);
}
else
{
type t3 = pop32 ();
push_type_t (t1);
push_type_t (t3);
push_type_t (t2);
}
push_type_t (t1);
}
else
{
type t2 = pop32 ();
type t3 = pop_raw ();
if (type_iswide (&t3))
{
push_type_t (t2);
push_type_t (t1);
}
else
{
type t4 = pop32 ();
push_type_t (t2);
push_type_t (t1);
push_type_t (t4);
}
push_type_t (t3);
push_type_t (t2);
push_type_t (t1);
}
}
break;
case op_swap:
{
type t1 = pop32 ();
type t2 = pop32 ();
push_type_t (t1);
push_type_t (t2);
}
break;
case op_iadd:
case op_isub:
case op_imul:
case op_idiv:
case op_irem:
case op_ishl:
case op_ishr:
case op_iushr:
case op_iand:
case op_ior:
case op_ixor:
pop_type (int_type);
push_type_t (pop_type (int_type));
break;
case op_ladd:
case op_lsub:
case op_lmul:
case op_ldiv:
case op_lrem:
case op_land:
case op_lor:
case op_lxor:
pop_type (long_type);
push_type_t (pop_type (long_type));
break;
case op_lshl:
case op_lshr:
case op_lushr:
pop_type (int_type);
push_type_t (pop_type (long_type));
break;
case op_fadd:
case op_fsub:
case op_fmul:
case op_fdiv:
case op_frem:
pop_type (float_type);
push_type_t (pop_type (float_type));
break;
case op_dadd:
case op_dsub:
case op_dmul:
case op_ddiv:
case op_drem:
pop_type (double_type);
push_type_t (pop_type (double_type));
break;
case op_ineg:
case op_i2b:
case op_i2c:
case op_i2s:
push_type_t (pop_type (int_type));
break;
case op_lneg:
push_type_t (pop_type (long_type));
break;
case op_fneg:
push_type_t (pop_type (float_type));
break;
case op_dneg:
push_type_t (pop_type (double_type));
break;
case op_iinc:
get_variable (get_byte (), int_type);
get_byte ();
break;
case op_i2l:
pop_type (int_type);
push_type (long_type);
break;
case op_i2f:
pop_type (int_type);
push_type (float_type);
break;
case op_i2d:
pop_type (int_type);
push_type (double_type);
break;
case op_l2i:
pop_type (long_type);
push_type (int_type);
break;
case op_l2f:
pop_type (long_type);
push_type (float_type);
break;
case op_l2d:
pop_type (long_type);
push_type (double_type);
break;
case op_f2i:
pop_type (float_type);
push_type (int_type);
break;
case op_f2l:
pop_type (float_type);
push_type (long_type);
break;
case op_f2d:
pop_type (float_type);
push_type (double_type);
break;
case op_d2i:
pop_type (double_type);
push_type (int_type);
break;
case op_d2l:
pop_type (double_type);
push_type (long_type);
break;
case op_d2f:
pop_type (double_type);
push_type (float_type);
break;
case op_lcmp:
pop_type (long_type);
pop_type (long_type);
push_type (int_type);
break;
case op_fcmpl:
case op_fcmpg:
pop_type (float_type);
pop_type (float_type);
push_type (int_type);
break;
case op_dcmpl:
case op_dcmpg:
pop_type (double_type);
pop_type (double_type);
push_type (int_type);
break;
case op_ifeq:
case op_ifne:
case op_iflt:
case op_ifge:
case op_ifgt:
case op_ifle:
pop_type (int_type);
push_jump (get_short ());
break;
case op_if_icmpeq:
case op_if_icmpne:
case op_if_icmplt:
case op_if_icmpge:
case op_if_icmpgt:
case op_if_icmple:
pop_type (int_type);
pop_type (int_type);
push_jump (get_short ());
break;
case op_if_acmpeq:
case op_if_acmpne:
pop_type (reference_type);
pop_type (reference_type);
push_jump (get_short ());
break;
case op_goto:
push_jump (get_short ());
invalidate_pc ();
break;
case op_jsr:
handle_jsr_insn (get_short ());
break;
case op_ret:
handle_ret_insn (get_byte ());
break;
case op_tableswitch:
{
int i;
jint low, high;
pop_type (int_type);
skip_padding ();
push_jump (get_int ());
low = get_int ();
high = get_int ();
for (i = low; i <= high; ++i)
push_jump (get_int ());
invalidate_pc ();
}
break;
case op_lookupswitch:
{
int i;
jint npairs, lastkey;
pop_type (int_type);
skip_padding ();
push_jump (get_int ());
npairs = get_int ();
lastkey = 0;
for (i = 0; i < npairs; ++i)
{
jint key = get_int ();
if (i > 0 && key <= lastkey)
verify_fail_pc ("lookupswitch pairs unsorted", vfr->start_PC);
lastkey = key;
push_jump (get_int ());
}
invalidate_pc ();
}
break;
case op_ireturn:
check_return_type (pop_type (int_type));
invalidate_pc ();
break;
case op_lreturn:
check_return_type (pop_type (long_type));
invalidate_pc ();
break;
case op_freturn:
check_return_type (pop_type (float_type));
invalidate_pc ();
break;
case op_dreturn:
check_return_type (pop_type (double_type));
invalidate_pc ();
break;
case op_areturn:
check_return_type (pop_init_ref (reference_type));
invalidate_pc ();
break;
case op_return:
if (this_is_init)
state_check_this_initialized (vfr->current_state);
check_return_type (make_type (void_type));
invalidate_pc ();
break;
case op_getstatic:
push_type_t (check_field_constant (get_ushort (), NULL, false));
break;
case op_putstatic:
pop_type_t (check_field_constant (get_ushort (), NULL, false));
break;
case op_getfield:
{
type klass;
type field = check_field_constant (get_ushort (), &klass, false);
pop_type_t (klass);
push_type_t (field);
}
break;
case op_putfield:
{
type klass;
type field = check_field_constant (get_ushort (), &klass, true);
pop_type_t (field);
pop_type_t (klass);
}
break;
case op_invokevirtual:
case op_invokespecial:
case op_invokestatic:
case op_invokeinterface:
{
vfy_string method_name, method_signature;
const char *namec;
int i, arg_count;
type rt;
bool is_init = false;
type class_type
= check_method_constant (get_ushort (),
opcode == op_invokeinterface,
&method_name,
&method_signature);
int nargs = 0;
if (opcode == op_invokeinterface)
{
nargs = get_byte ();
if (get_byte () != 0)
verify_fail ("invokeinterface dummy byte is wrong");
}
namec = vfy_string_bytes (method_name);
if (vfy_strings_equal (method_name, vfy_init_name()))
{
is_init = true;
if (opcode != op_invokespecial)
verify_fail ("can't invoke <init>");
}
else if (namec[0] == '<')
verify_fail ("can't invoke method starting with `<'");
arg_count = vfy_count_arguments (method_signature);
{
type *arg_types = (type *) vfy_alloc (arg_count * sizeof (type));
compute_argument_types (method_signature, arg_types);
for (i = arg_count - 1; i >= 0; --i)
{
nargs -= type_depth (&arg_types[i]);
pop_init_ref_t (arg_types[i]);
}
vfy_free (arg_types);
}
if (opcode == op_invokeinterface
&& nargs != 1)
verify_fail ("wrong argument count for invokeinterface");
if (opcode != op_invokestatic)
{
type raw;
type t = class_type;
if (is_init)
{
type_set_uninitialized (&t, UNINIT);
}
raw = pop_raw ();
if (! types_compatible (&t, &raw))
verify_fail ("incompatible type on stack");
if (is_init)
state_set_initialized (vfr->current_state,
type_get_pc (&raw), vfr->current_method->max_locals);
}
rt = compute_return_type (method_signature);
if (! type_isvoid (&rt))
push_type_t (rt);
}
break;
case op_new:
{
type t = check_class_constant (get_ushort ());
if (type_isarray (&t) || type_isinterface (&t)
|| type_isabstract (&t))
verify_fail ("type is array, interface, or abstract");
type_set_uninitialized (&t, vfr->start_PC);
push_type_t (t);
}
break;
case op_newarray:
{
int atype = get_byte ();
type t;
if (atype < boolean_type || atype > long_type)
verify_fail_pc ("type not primitive", vfr->start_PC);
pop_type (int_type);
init_type_from_class (&t, construct_primitive_array_type (atype));
push_type_t (t);
}
break;
case op_anewarray:
{
type t;
pop_type (int_type);
t = check_class_constant (get_ushort ());
push_type_t (type_to_array (&t));
}
break;
case op_arraylength:
{
type t = pop_init_ref (reference_type);
if (! type_isarray (&t) && ! type_isnull (&t))
verify_fail ("array type expected");
push_type (int_type);
}
break;
case op_athrow:
pop_type_t (make_type_from_class (vfy_throwable_type ()));
invalidate_pc ();
break;
case op_checkcast:
pop_init_ref (reference_type);
push_type_t (check_class_constant (get_ushort ()));
break;
case op_instanceof:
pop_init_ref (reference_type);
check_class_constant (get_ushort ());
push_type (int_type);
break;
case op_monitorenter:
pop_init_ref (reference_type);
break;
case op_monitorexit:
pop_init_ref (reference_type);
break;
case op_wide:
{
switch (get_byte ())
{
case op_iload:
push_type_t (get_variable (get_ushort (), int_type));
break;
case op_lload:
push_type_t (get_variable (get_ushort (), long_type));
break;
case op_fload:
push_type_t (get_variable (get_ushort (), float_type));
break;
case op_dload:
push_type_t (get_variable (get_ushort (), double_type));
break;
case op_aload:
push_type_t (get_variable (get_ushort (), reference_type));
break;
case op_istore:
set_variable (get_ushort (), pop_type (int_type));
break;
case op_lstore:
set_variable (get_ushort (), pop_type (long_type));
break;
case op_fstore:
set_variable (get_ushort (), pop_type (float_type));
break;
case op_dstore:
set_variable (get_ushort (), pop_type (double_type));
break;
case op_astore:
set_variable (get_ushort (), pop_init_ref (reference_type));
break;
case op_ret:
handle_ret_insn (get_short ());
break;
case op_iinc:
get_variable (get_ushort (), int_type);
get_short ();
break;
default:
verify_fail_pc ("unrecognized wide instruction", vfr->start_PC);
}
}
break;
case op_multianewarray:
{
int i;
type atype = check_class_constant (get_ushort ());
int dim = get_byte ();
if (dim < 1)
verify_fail_pc ("too few dimensions to multianewarray", vfr->start_PC);
type_verify_dimensions (&atype, dim);
for (i = 0; i < dim; ++i)
pop_type (int_type);
push_type_t (atype);
}
break;
case op_ifnull:
case op_ifnonnull:
pop_type (reference_type);
push_jump (get_short ());
break;
case op_goto_w:
push_jump (get_int ());
invalidate_pc ();
break;
case op_jsr_w:
handle_jsr_insn (get_int ());
break;
default:
verify_fail_pc ("unrecognized instruction in verify_instructions_0",
vfr->start_PC);
}
}
}
static vfy_jclass
collapse_type (type *t)
{
switch (t->key)
{
case void_type:
case boolean_type:
case char_type:
case float_type:
case double_type:
case byte_type:
case short_type:
case int_type:
case long_type:
return vfy_get_primitive_type (t->key);
case unsuitable_type:
case continuation_type:
return vfy_unsuitable_type ();
case return_address_type:
return vfy_return_address_type ();
case null_type:
return vfy_null_type ();
case reference_type:
case uninitialized_reference_type:
return vfy_object_type ();
}
abort ();
}
static void
verify_instructions (void)
{
int i;
branch_prepass ();
verify_instructions_0 ();
for (i = 0; i < vfr->current_method->code_length; ++i)
{
int j, slot;
struct state *curr;
if ((vfr->flags[i] & FLAG_INSN_SEEN) != 0)
vfy_note_instruction_seen (i);
if (! vfr->states[i])
continue;
curr = vfr->states[i]->val;
vfy_note_stack_depth (vfr->current_method, i, curr->stackdepth);
for (j = 0; j < vfr->current_method->max_locals; ++j)
vfy_note_local_type (vfr->current_method, i, j,
collapse_type (&curr->locals[j]));
for (slot = j = 0; j < curr->stacktop; ++j, ++slot)
{
vfy_note_stack_type (vfr->current_method, i, slot,
collapse_type (&curr->stack[j]));
if (type_iswide (&curr->stack[j]))
{
++slot;
vfy_note_stack_type (vfr->current_method, i, slot,
vfy_unsuitable_type ());
}
}
if (slot != curr->stackdepth)
abort ();
}
}
static void
make_verifier_context (vfy_method *m)
{
vfr = (verifier_context *) vfy_alloc (sizeof (struct verifier_context));
vfr->current_method = m;
vfr->bytecode = vfy_get_bytecode (m);
vfr->exception = vfy_get_exceptions (m);
vfr->current_class = m->defining_class;
vfr->states = NULL;
vfr->flags = NULL;
vfr->utf8_list = NULL;
vfr->isect_list = NULL;
}
static void
free_verifier_context (void)
{
vfy_string_list *utf8_list;
ref_intersection *isect_list;
if (vfr->flags)
vfy_free (vfr->flags);
utf8_list = vfr->utf8_list;
while (utf8_list != NULL)
{
vfy_string_list *n = utf8_list->next;
vfy_free (utf8_list);
utf8_list = n;
}
isect_list = vfr->isect_list;
while (isect_list != NULL)
{
ref_intersection *next = isect_list->alloc_next;
vfy_free (isect_list);
isect_list = next;
}
if (vfr->states != NULL)
{
int i;
for (i = 0; i < vfr->current_method->code_length; ++i)
{
state_list *iter = vfr->states[i];
while (iter != NULL)
{
state_list *next = iter->next;
free_state (iter->val);
vfy_free (iter->val);
vfy_free (iter);
iter = next;
}
}
vfy_free (vfr->states);
}
vfy_free (vfr);
}
int
verify_method (vfy_method *meth)
{
debug_print ("verify_method (%s) %i\n", vfy_string_bytes (meth->name),
meth->code_length);
if (vfr != NULL)
verify_fail ("verifier re-entered");
make_verifier_context (meth);
verify_instructions ();
free_verifier_context ();
vfr = NULL;
return 1;
}