# HG changeset patch # User kamg # Date 1479275124 0 # Node ID 6088554a147cdeb7808f0e31e7f8104928b14fcf # Parent 83a11fe60ae1a5c8534b33217644d1f31339d78a 7116786: RFE: Detailed information on VerifyErrors Summary: Provide additional detail in VerifyError messages Reviewed-by: sspitsyn, acorn diff -r 83a11fe60ae1 -r 6088554a147c src/os/solaris/vm/dtraceJSDT_solaris.cpp --- a/src/os/solaris/vm/dtraceJSDT_solaris.cpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/os/solaris/vm/dtraceJSDT_solaris.cpp Wed Nov 16 05:45:24 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2012, 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 @@ -626,45 +626,6 @@ } } -/** - * This prints out hex data in a 'windbg' or 'xxd' form, where each line is: - * : 8 * - * example: - * 0000000: 7f44 4f46 0102 0102 0000 0000 0000 0000 .DOF............ - * 0000010: 0000 0000 0000 0040 0000 0020 0000 0005 .......@... .... - * 0000020: 0000 0000 0000 0040 0000 0000 0000 015d .......@.......] - * ... - */ -static void printDOFRawData(void* dof) { - size_t size = ((dof_hdr_t*)dof)->dofh_loadsz; - size_t limit = (size + 16) / 16 * 16; - for (size_t i = 0; i < limit; ++i) { - if (i % 16 == 0) { - tty->print("%07x:", i); - } - if (i % 2 == 0) { - tty->print(" "); - } - if (i < size) { - tty->print("%02x", ((unsigned char*)dof)[i]); - } else { - tty->print(" "); - } - if ((i + 1) % 16 == 0) { - tty->print(" "); - for (size_t j = 0; j < 16; ++j) { - size_t idx = i + j - 15; - char c = ((char*)dof)[idx]; - if (idx < size) { - tty->print("%c", c >= 32 && c <= 126 ? c : '.'); - } - } - tty->print_cr(""); - } - } - tty->print_cr(""); -} - static void printDOFHelper(dof_helper_t* helper) { tty->print_cr("// dof_helper_t {"); tty->print_cr("// dofhp_mod = \"%s\"", helper->dofhp_mod); @@ -672,7 +633,8 @@ tty->print_cr("// dofhp_dof = 0x%016llx", helper->dofhp_dof); printDOF((void*)helper->dofhp_dof); tty->print_cr("// }"); - printDOFRawData((void*)helper->dofhp_dof); + size_t len = ((dof_hdr_t*)helper)->dofh_loadsz; + tty->print_data((void*)helper->dofhp_dof, len, true); } #else // ndef HAVE_DTRACE_H diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/classfile/stackMapFrame.cpp --- a/src/share/vm/classfile/stackMapFrame.cpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/classfile/stackMapFrame.cpp Wed Nov 16 05:45:24 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2012, 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 @@ -32,9 +32,9 @@ #include "utilities/globalDefinitions.hpp" StackMapFrame::StackMapFrame(u2 max_locals, u2 max_stack, ClassVerifier* v) : - _offset(0), _locals_size(0), _stack_size(0), _flags(0), - _max_locals(max_locals), _max_stack(max_stack), - _verifier(v) { + _offset(0), _locals_size(0), _stack_size(0), + _stack_mark(0), _flags(0), _max_locals(max_locals), + _max_stack(max_stack), _verifier(v) { Thread* thr = v->thread(); _locals = NEW_RESOURCE_ARRAY_IN_THREAD(thr, VerificationType, max_locals); _stack = NEW_RESOURCE_ARRAY_IN_THREAD(thr, VerificationType, max_stack); @@ -157,17 +157,17 @@ } } - -bool StackMapFrame::is_assignable_to( +// Returns the location of the first mismatch, or 'len' if there are no +// mismatches +int StackMapFrame::is_assignable_to( VerificationType* from, VerificationType* to, int32_t len, TRAPS) const { - for (int32_t i = 0; i < len; i++) { - bool subtype = to[i].is_assignable_from( - from[i], verifier(), THREAD); - if (!subtype) { - return false; + int32_t i = 0; + for (i = 0; i < len; i++) { + if (!to[i].is_assignable_from(from[i], verifier(), THREAD)) { + break; } } - return true; + return i; } bool StackMapFrame::has_flag_match_exception( @@ -209,50 +209,84 @@ } bool StackMapFrame::is_assignable_to( - const StackMapFrame* target, bool is_exception_handler, TRAPS) const { - if (_max_locals != target->max_locals() || - _stack_size != target->stack_size()) { + const StackMapFrame* target, bool is_exception_handler, + ErrorContext* ctx, TRAPS) const { + if (_max_locals != target->max_locals()) { + *ctx = ErrorContext::locals_size_mismatch( + _offset, (StackMapFrame*)this, (StackMapFrame*)target); + return false; + } + if (_stack_size != target->stack_size()) { + *ctx = ErrorContext::stack_size_mismatch( + _offset, (StackMapFrame*)this, (StackMapFrame*)target); return false; } // Only need to compare type elements up to target->locals() or target->stack(). // The remaining type elements in this state can be ignored because they are // assignable to bogus type. - bool match_locals = is_assignable_to( - _locals, target->locals(), target->locals_size(), CHECK_false); - bool match_stack = is_assignable_to( - _stack, target->stack(), _stack_size, CHECK_false); + int mismatch_loc; + mismatch_loc = is_assignable_to( + _locals, target->locals(), target->locals_size(), THREAD); + if (mismatch_loc != target->locals_size()) { + *ctx = ErrorContext::bad_type(target->offset(), + TypeOrigin::local(mismatch_loc, (StackMapFrame*)this), + TypeOrigin::sm_local(mismatch_loc, (StackMapFrame*)target)); + return false; + } + mismatch_loc = is_assignable_to(_stack, target->stack(), _stack_size, THREAD); + if (mismatch_loc != _stack_size) { + *ctx = ErrorContext::bad_type(target->offset(), + TypeOrigin::stack(mismatch_loc, (StackMapFrame*)this), + TypeOrigin::sm_stack(mismatch_loc, (StackMapFrame*)target)); + return false; + } + bool match_flags = (_flags | target->flags()) == target->flags(); - - return match_locals && match_stack && - (match_flags || (is_exception_handler && has_flag_match_exception(target))); + if (match_flags || is_exception_handler && has_flag_match_exception(target)) { + return true; + } else { + *ctx = ErrorContext::bad_flags(target->offset(), + (StackMapFrame*)this, (StackMapFrame*)target); + return false; + } } VerificationType StackMapFrame::pop_stack_ex(VerificationType type, TRAPS) { if (_stack_size <= 0) { - verifier()->verify_error(_offset, "Operand stack underflow"); + verifier()->verify_error( + ErrorContext::stack_underflow(_offset, this), + "Operand stack underflow"); return VerificationType::bogus_type(); } VerificationType top = _stack[--_stack_size]; bool subtype = type.is_assignable_from( top, verifier(), CHECK_(VerificationType::bogus_type())); if (!subtype) { - verifier()->verify_error(_offset, "Bad type on operand stack"); + verifier()->verify_error( + ErrorContext::bad_type(_offset, stack_top_ctx(), + TypeOrigin::implicit(type)), + "Bad type on operand stack"); return VerificationType::bogus_type(); } - NOT_PRODUCT( _stack[_stack_size] = VerificationType::bogus_type(); ) return top; } VerificationType StackMapFrame::get_local( int32_t index, VerificationType type, TRAPS) { if (index >= _max_locals) { - verifier()->verify_error(_offset, "Local variable table overflow"); + verifier()->verify_error( + ErrorContext::bad_local_index(_offset, index), + "Local variable table overflow"); return VerificationType::bogus_type(); } bool subtype = type.is_assignable_from(_locals[index], verifier(), CHECK_(VerificationType::bogus_type())); if (!subtype) { - verifier()->verify_error(_offset, "Bad local variable type"); + verifier()->verify_error( + ErrorContext::bad_type(_offset, + TypeOrigin::local(index, this), + TypeOrigin::implicit(type)), + "Bad local variable type"); return VerificationType::bogus_type(); } if(index >= _locals_size) { _locals_size = index + 1; } @@ -264,23 +298,37 @@ assert(type1.is_long() || type1.is_double(), "must be long/double"); assert(type2.is_long2() || type2.is_double2(), "must be long/double_2"); if (index >= _locals_size - 1) { - verifier()->verify_error(_offset, "get long/double overflows locals"); + verifier()->verify_error( + ErrorContext::bad_local_index(_offset, index), + "get long/double overflows locals"); return; } - bool subtype1 = type1.is_assignable_from( - _locals[index], verifier(), CHECK); - bool subtype2 = type2.is_assignable_from( - _locals[index+1], verifier(), CHECK); - if (!subtype1 || !subtype2) { - verifier()->verify_error(_offset, "Bad local variable type"); - return; + bool subtype = type1.is_assignable_from(_locals[index], verifier(), CHECK); + if (!subtype) { + verifier()->verify_error( + ErrorContext::bad_type(_offset, + TypeOrigin::local(index, this), TypeOrigin::implicit(type1)), + "Bad local variable type"); + } else { + subtype = type2.is_assignable_from(_locals[index + 1], verifier(), CHECK); + if (!subtype) { + /* Unreachable? All local store routines convert a split long or double + * into a TOP during the store. So we should never end up seeing an + * orphaned half. */ + verifier()->verify_error( + ErrorContext::bad_type(_offset, + TypeOrigin::local(index + 1, this), TypeOrigin::implicit(type2)), + "Bad local variable type"); + } } } void StackMapFrame::set_local(int32_t index, VerificationType type, TRAPS) { assert(!type.is_check(), "Must be a real type"); if (index >= _max_locals) { - verifier()->verify_error("Local variable table overflow", _offset); + verifier()->verify_error( + ErrorContext::bad_local_index(_offset, index), + "Local variable table overflow"); return; } // If type at index is double or long, set the next location to be unusable @@ -310,7 +358,9 @@ assert(type1.is_long() || type1.is_double(), "must be long/double"); assert(type2.is_long2() || type2.is_double2(), "must be long/double_2"); if (index >= _max_locals - 1) { - verifier()->verify_error("Local variable table overflow", _offset); + verifier()->verify_error( + ErrorContext::bad_local_index(_offset, index), + "Local variable table overflow"); return; } // If type at index+1 is double or long, set the next location to be unusable @@ -336,21 +386,30 @@ } } -#ifndef PRODUCT - -void StackMapFrame::print() const { - tty->print_cr("stackmap_frame[%d]:", _offset); - tty->print_cr("flags = 0x%x", _flags); - tty->print("locals[%d] = { ", _locals_size); - for (int32_t i = 0; i < _locals_size; i++) { - _locals[i].print_on(tty); - } - tty->print_cr(" }"); - tty->print("stack[%d] = { ", _stack_size); - for (int32_t j = 0; j < _stack_size; j++) { - _stack[j].print_on(tty); - } - tty->print_cr(" }"); +TypeOrigin StackMapFrame::stack_top_ctx() { + return TypeOrigin::stack(_stack_size, this); } -#endif +void StackMapFrame::print_on(outputStream* str) const { + str->indent().print_cr("bci: @%d", _offset); + str->indent().print_cr("flags: {%s }", + flag_this_uninit() ? " flagThisUninit" : ""); + str->indent().print("locals: {"); + for (int32_t i = 0; i < _locals_size; ++i) { + str->print(" "); + _locals[i].print_on(str); + if (i != _locals_size - 1) { + str->print(","); + } + } + str->print_cr(" }"); + str->indent().print("stack: {"); + for (int32_t j = 0; j < _stack_size; ++j) { + str->print(" "); + _stack[j].print_on(str); + if (j != _stack_size - 1) { + str->print(","); + } + } + str->print_cr(" }"); +} diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/classfile/stackMapFrame.hpp --- a/src/share/vm/classfile/stackMapFrame.hpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/classfile/stackMapFrame.hpp Wed Nov 16 05:45:24 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2012, 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 @@ -34,6 +34,8 @@ // A StackMapFrame represents one frame in the stack map attribute. +class TypeContext; + enum { FLAG_THIS_UNINIT = 0x01 }; @@ -47,6 +49,10 @@ int32_t _locals_size; // number of valid type elements in _locals int32_t _stack_size; // number of valid type elements in _stack + int32_t _stack_mark; // Records the size of the stack prior to an + // instruction modification, to allow rewinding + // when/if an error occurs. + int32_t _max_locals; int32_t _max_stack; @@ -56,6 +62,31 @@ ClassVerifier* _verifier; // the verifier verifying this method + StackMapFrame(const StackMapFrame& cp) : + _offset(cp._offset), _locals_size(cp._locals_size), + _stack_size(cp._stack_size), _stack_mark(cp._stack_mark), + _max_locals(cp._max_locals), _max_stack(cp._max_stack), + _flags(cp._flags) { + _locals = NEW_RESOURCE_ARRAY(VerificationType, _max_locals); + for (int i = 0; i < _max_locals; ++i) { + if (i < _locals_size) { + _locals[i] = cp._locals[i]; + } else { + _locals[i] = VerificationType::bogus_type(); + } + } + int ss = MAX2(_stack_size, _stack_mark); + _stack = NEW_RESOURCE_ARRAY(VerificationType, _max_stack); + for (int i = 0; i < _max_stack; ++i) { + if (i < ss) { + _stack[i] = cp._stack[i]; + } else { + _stack[i] = VerificationType::bogus_type(); + } + } + _verifier = NULL; + } + public: // constructors @@ -77,16 +108,21 @@ ClassVerifier* v) : _offset(offset), _flags(flags), _locals_size(locals_size), _stack_size(stack_size), + _stack_mark(-1), _max_locals(max_locals), _max_stack(max_stack), _locals(locals), _stack(stack), _verifier(v) { } + static StackMapFrame* copy(StackMapFrame* smf) { + return new StackMapFrame(*smf); + } + inline void set_offset(int32_t offset) { _offset = offset; } inline void set_verifier(ClassVerifier* v) { _verifier = v; } inline void set_flags(u1 flags) { _flags = flags; } inline void set_locals_size(u2 locals_size) { _locals_size = locals_size; } - inline void set_stack_size(u2 stack_size) { _stack_size = stack_size; } + inline void set_stack_size(u2 stack_size) { _stack_size = _stack_mark = stack_size; } inline void clear_stack() { _stack_size = 0; } inline int32_t offset() const { return _offset; } inline ClassVerifier* verifier() const { return _verifier; } @@ -134,14 +170,37 @@ void copy_stack(const StackMapFrame* src); // Return true if this stack map frame is assignable to target. - bool is_assignable_to(const StackMapFrame* target, - bool is_exception_handler, TRAPS) const; + bool is_assignable_to( + const StackMapFrame* target, bool is_exception_handler, + ErrorContext* ctx, TRAPS) const; + + inline void set_mark() { +#ifdef DEBUG + // Put bogus type to indicate it's no longer valid. + if (_stack_mark != -1) { + for (int i = _stack_mark; i >= _stack_size; --i) { + _stack[i] = VerificationType::bogus_type(); + } + } +#endif // def DEBUG + _stack_mark = _stack_size; + } + + // Used when an error occurs and we want to reset the stack to the state + // it was before operands were popped off. + void restore() { + if (_stack_mark != -1) { + _stack_size = _stack_mark; + } + } // Push type into stack type array. inline void push_stack(VerificationType type, TRAPS) { assert(!type.is_check(), "Must be a real type"); if (_stack_size >= _max_stack) { - verifier()->verify_error(_offset, "Operand stack overflow"); + verifier()->verify_error( + ErrorContext::stack_overflow(_offset, this), + "Operand stack overflow"); return; } _stack[_stack_size++] = type; @@ -152,7 +211,9 @@ assert(type1.is_long() || type1.is_double(), "must be long/double"); assert(type2.is_long2() || type2.is_double2(), "must be long/double_2"); if (_stack_size >= _max_stack - 1) { - verifier()->verify_error(_offset, "Operand stack overflow"); + verifier()->verify_error( + ErrorContext::stack_overflow(_offset, this), + "Operand stack overflow"); return; } _stack[_stack_size++] = type1; @@ -162,13 +223,12 @@ // Pop and return the top type on stack without verifying. inline VerificationType pop_stack(TRAPS) { if (_stack_size <= 0) { - verifier()->verify_error(_offset, "Operand stack underflow"); + verifier()->verify_error( + ErrorContext::stack_underflow(_offset, this), + "Operand stack underflow"); return VerificationType::bogus_type(); } - // Put bogus type to indicate it's no longer valid. - // Added to make it consistent with the other pop_stack method. VerificationType top = _stack[--_stack_size]; - NOT_PRODUCT( _stack[_stack_size] = VerificationType::bogus_type(); ) return top; } @@ -180,8 +240,7 @@ bool subtype = type.is_assignable_from( top, verifier(), CHECK_(VerificationType::bogus_type())); if (subtype) { - _stack_size --; - NOT_PRODUCT( _stack[_stack_size] = VerificationType::bogus_type(); ) + --_stack_size; return top; } } @@ -199,8 +258,6 @@ bool subtype2 = type2.is_assignable_from(top2, verifier(), CHECK); if (subtype1 && subtype2) { _stack_size -= 2; - NOT_PRODUCT( _stack[_stack_size] = VerificationType::bogus_type(); ) - NOT_PRODUCT( _stack[_stack_size+1] = VerificationType::bogus_type(); ) return; } } @@ -208,6 +265,14 @@ pop_stack_ex(type2, THREAD); } + VerificationType local_at(int index) { + return _locals[index]; + } + + VerificationType stack_at(int index) { + return _stack[index]; + } + // Uncommon case that throws exceptions. VerificationType pop_stack_ex(VerificationType type, TRAPS); @@ -226,13 +291,14 @@ // Private auxiliary method used only in is_assignable_to(StackMapFrame). // Returns true if src is assignable to target. - bool is_assignable_to( + int is_assignable_to( VerificationType* src, VerificationType* target, int32_t len, TRAPS) const; bool has_flag_match_exception(const StackMapFrame* target) const; - // Debugging - void print() const PRODUCT_RETURN; + TypeOrigin stack_top_ctx(); + + void print_on(outputStream* str) const; }; #endif // SHARE_VM_CLASSFILE_STACKMAPFRAME_HPP diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/classfile/stackMapTable.cpp --- a/src/share/vm/classfile/stackMapTable.cpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/classfile/stackMapTable.cpp Wed Nov 16 05:45:24 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2012, 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 @@ -46,7 +46,9 @@ _frame_array[i] = frame; int offset = frame->offset(); if (offset >= code_len || code_data[offset] == 0) { - frame->verifier()->verify_error("StackMapTable error: bad offset"); + frame->verifier()->verify_error( + ErrorContext::bad_stackmap(i, frame), + "StackMapTable error: bad offset"); return; } pre_frame = frame; @@ -68,12 +70,9 @@ bool StackMapTable::match_stackmap( StackMapFrame* frame, int32_t target, - bool match, bool update, bool handler, TRAPS) const { + bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const { int index = get_index_from_offset(target); - - return match_stackmap( - frame, target, index, match, - update, handler, CHECK_VERIFY_(frame->verifier(), false)); + return match_stackmap(frame, target, index, match, update, handler, ctx, THREAD); } // Match and/or update current_frame to the frame in stackmap table with @@ -90,21 +89,21 @@ // unconditional branch: true true false bool StackMapTable::match_stackmap( StackMapFrame* frame, int32_t target, int32_t frame_index, - bool match, bool update, bool handler, TRAPS) const { + bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const { if (frame_index < 0 || frame_index >= _frame_count) { - frame->verifier()->verify_error(frame->offset(), - "Expecting a stackmap frame at branch target %d", target); + *ctx = ErrorContext::missing_stackmap(frame->offset()); + frame->verifier()->verify_error( + *ctx, "Expecting a stackmap frame at branch target %d", target); return false; } + StackMapFrame *stackmap_frame = _frame_array[frame_index]; bool result = true; - StackMapFrame *stackmap_frame = _frame_array[frame_index]; if (match) { // Has direct control flow from last instruction, need to match the two // frames. - result = frame->is_assignable_to( - stackmap_frame, handler, - CHECK_VERIFY_(frame->verifier(), false)); + result = frame->is_assignable_to(stackmap_frame, handler, + ctx, CHECK_VERIFY_(frame->verifier(), result)); } if (update) { // Use the frame in stackmap table as current frame @@ -125,11 +124,12 @@ void StackMapTable::check_jump_target( StackMapFrame* frame, int32_t target, TRAPS) const { + ErrorContext ctx; bool match = match_stackmap( - frame, target, true, false, false, CHECK_VERIFY(frame->verifier())); + frame, target, true, false, false, &ctx, CHECK_VERIFY(frame->verifier())); if (!match || (target < 0 || target >= _code_length)) { - frame->verifier()->verify_error(frame->offset(), - "Inconsistent stackmap frames at branch target %d", target); + frame->verifier()->verify_error(ctx, + "Inconsistent stackmap frames at branch target %d", target); return; } // check if uninitialized objects exist on backward branches @@ -139,25 +139,25 @@ void StackMapTable::check_new_object( const StackMapFrame* frame, int32_t target, TRAPS) const { if (frame->offset() > target && frame->has_new_object()) { - frame->verifier()->verify_error(frame->offset(), - "Uninitialized object exists on backward branch %d", target); + frame->verifier()->verify_error( + ErrorContext::bad_code(frame->offset()), + "Uninitialized object exists on backward branch %d", target); return; } } -#ifndef PRODUCT - -void StackMapTable::print() const { - tty->print_cr("StackMapTable: frame_count = %d", _frame_count); - tty->print_cr("table = { "); - for (int32_t i = 0; i < _frame_count; i++) { - _frame_array[i]->print(); +void StackMapTable::print_on(outputStream* str) const { + str->indent().print_cr("StackMapTable: frame_count = %d", _frame_count); + str->indent().print_cr("table = { "); + { + streamIndentor si(str); + for (int32_t i = 0; i < _frame_count; ++i) { + _frame_array[i]->print_on(str); + } } - tty->print_cr(" }"); + str->print_cr(" }"); } -#endif - int32_t StackMapReader::chop( VerificationType* locals, int32_t length, int32_t chops) { if (locals == NULL) return -1; diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/classfile/stackMapTable.hpp --- a/src/share/vm/classfile/stackMapTable.hpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/classfile/stackMapTable.hpp Wed Nov 16 05:45:24 2016 +0000 @@ -26,6 +26,7 @@ #define SHARE_VM_CLASSFILE_STACKMAPTABLE_HPP #include "classfile/stackMapFrame.hpp" +#include "classfile/verifier.hpp" #include "memory/allocation.hpp" #include "oops/constantPoolOop.hpp" #include "oops/methodOop.hpp" @@ -73,12 +74,12 @@ // specified offset. Return true if the two frames match. bool match_stackmap( StackMapFrame* current_frame, int32_t offset, - bool match, bool update, bool handler, TRAPS) const; + bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const; // Match and/or update current_frame to the frame in stackmap table with // specified offset and frame index. Return true if the two frames match. bool match_stackmap( StackMapFrame* current_frame, int32_t offset, int32_t frame_index, - bool match, bool update, bool handler, TRAPS) const; + bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const; // Check jump instructions. Make sure there are no uninitialized // instances on backward branch. @@ -93,8 +94,7 @@ void check_new_object( const StackMapFrame* frame, int32_t target, TRAPS) const; - // Debugging - void print() const PRODUCT_RETURN; + void print_on(outputStream* str) const; }; class StackMapStream : StackObj { diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/classfile/stackMapTableFormat.hpp --- a/src/share/vm/classfile/stackMapTableFormat.hpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/classfile/stackMapTableFormat.hpp Wed Nov 16 05:45:24 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2012, 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 @@ -135,7 +135,6 @@ !is_object() && !is_uninitialized())); } -#ifdef ASSERT void print_on(outputStream* st) { switch (tag()) { case ITEM_Top: st->print("Top"); break; @@ -154,14 +153,13 @@ assert(false, "Bad verification_type_info"); } } -#endif }; #define FOR_EACH_STACKMAP_FRAME_TYPE(macro, arg1, arg2) \ macro(same_frame, arg1, arg2) \ macro(same_frame_extended, arg1, arg2) \ - macro(same_frame_1_stack_item_frame, arg1, arg2) \ - macro(same_frame_1_stack_item_extended, arg1, arg2) \ + macro(same_locals_1_stack_item_frame, arg1, arg2) \ + macro(same_locals_1_stack_item_extended, arg1, arg2) \ macro(chop_frame, arg1, arg2) \ macro(append_frame, arg1, arg2) \ macro(full_frame, arg1, arg2) @@ -203,9 +201,8 @@ // that we don't read past a particular memory limit. It returns false // if any part of the data structure is outside the specified memory bounds. inline bool verify(address start, address end) const; -#ifdef ASSERT - inline void print_on(outputStream* st) const; -#endif + + inline void print_on(outputStream* st, int current_offset) const; // Create as_xxx and is_xxx methods for the subtypes #define FRAME_TYPE_DECL(stackmap_frame_type, arg1, arg2) \ @@ -263,11 +260,9 @@ return true; } -#ifdef ASSERT - void print_on(outputStream* st) const { - st->print("same_frame(%d)", offset_delta()); + void print_on(outputStream* st, int current_offset = -1) const { + st->print("same_frame(@%d)", offset_delta() + current_offset); } -#endif }; class same_frame_extended : public stack_map_frame { @@ -311,14 +306,12 @@ return frame_type_addr() + size() <= end; } -#ifdef ASSERT - void print_on(outputStream* st) const { - st->print("same_frame_extended(%d)", offset_delta()); + void print_on(outputStream* st, int current_offset = -1) const { + st->print("same_frame_extended(@%d)", offset_delta() + current_offset); } -#endif }; -class same_frame_1_stack_item_frame : public stack_map_frame { +class same_locals_1_stack_item_frame : public stack_map_frame { private: address type_addr() const { return frame_type_addr() + sizeof(u1); } @@ -332,14 +325,14 @@ return tag >= 64 && tag < 128; } - static same_frame_1_stack_item_frame* at(address addr) { + static same_locals_1_stack_item_frame* at(address addr) { assert(is_frame_type(*addr), "Wrong frame id"); - return (same_frame_1_stack_item_frame*)addr; + return (same_locals_1_stack_item_frame*)addr; } - static same_frame_1_stack_item_frame* create_at( + static same_locals_1_stack_item_frame* create_at( address addr, int offset_delta, verification_type_info* vti) { - same_frame_1_stack_item_frame* sm = (same_frame_1_stack_item_frame*)addr; + same_locals_1_stack_item_frame* sm = (same_locals_1_stack_item_frame*)addr; sm->set_offset_delta(offset_delta); if (vti != NULL) { sm->set_type(vti); @@ -382,16 +375,15 @@ return types()->verify(start, end); } -#ifdef ASSERT - void print_on(outputStream* st) const { - st->print("same_frame_1_stack_item_frame(%d,", offset_delta()); + void print_on(outputStream* st, int current_offset = -1) const { + st->print("same_locals_1_stack_item_frame(@%d,", + offset_delta() + current_offset); types()->print_on(st); st->print(")"); } -#endif }; -class same_frame_1_stack_item_extended : public stack_map_frame { +class same_locals_1_stack_item_extended : public stack_map_frame { private: address offset_delta_addr() const { return frame_type_addr() + sizeof(u1); } address type_addr() const { return offset_delta_addr() + sizeof(u2); } @@ -403,15 +395,15 @@ return tag == _frame_id; } - static same_frame_1_stack_item_extended* at(address addr) { + static same_locals_1_stack_item_extended* at(address addr) { assert(is_frame_type(*addr), "Wrong frame id"); - return (same_frame_1_stack_item_extended*)addr; + return (same_locals_1_stack_item_extended*)addr; } - static same_frame_1_stack_item_extended* create_at( + static same_locals_1_stack_item_extended* create_at( address addr, int offset_delta, verification_type_info* vti) { - same_frame_1_stack_item_extended* sm = - (same_frame_1_stack_item_extended*)addr; + same_locals_1_stack_item_extended* sm = + (same_locals_1_stack_item_extended*)addr; sm->set_frame_type(_frame_id); sm->set_offset_delta(offset_delta); if (vti != NULL) { @@ -448,13 +440,12 @@ return type_addr() < end && types()->verify(start, end); } -#ifdef ASSERT - void print_on(outputStream* st) const { - st->print("same_frame_1_stack_item_extended(%d,", offset_delta()); + void print_on(outputStream* st, int current_offset = -1) const { + st->print("same_locals_1_stack_item_extended(@%d,", + offset_delta() + current_offset); types()->print_on(st); st->print(")"); } -#endif }; class chop_frame : public stack_map_frame { @@ -517,11 +508,9 @@ return frame_type_addr() + size() <= end; } -#ifdef ASSERT - void print_on(outputStream* st) const { - st->print("chop_frame(%d,%d)", offset_delta(), chops()); + void print_on(outputStream* st, int current_offset = -1) const { + st->print("chop_frame(@%d,%d)", offset_delta() + current_offset, chops()); } -#endif }; class append_frame : public stack_map_frame { @@ -618,9 +607,8 @@ return false; } -#ifdef ASSERT - void print_on(outputStream* st) const { - st->print("append_frame(%d,", offset_delta()); + void print_on(outputStream* st, int current_offset = -1) const { + st->print("append_frame(@%d,", offset_delta() + current_offset); verification_type_info* vti = types(); for (int i = 0; i < number_of_types(); ++i) { vti->print_on(st); @@ -631,7 +619,6 @@ } st->print(")"); } -#endif }; class full_frame : public stack_map_frame { @@ -774,9 +761,8 @@ return true; } -#ifdef ASSERT - void print_on(outputStream* st) const { - st->print("full_frame(%d,{", offset_delta()); + void print_on(outputStream* st, int current_offset = -1) const { + st->print("full_frame(@%d,{", offset_delta() + current_offset); verification_type_info* vti = locals(); for (int i = 0; i < num_locals(); ++i) { vti->print_on(st); @@ -798,7 +784,6 @@ } st->print("})"); } -#endif }; #define VIRTUAL_DISPATCH(stack_frame_type, func_name, args) \ @@ -852,11 +837,9 @@ return false; } -#ifdef ASSERT -void stack_map_frame::print_on(outputStream* st) const { - FOR_EACH_STACKMAP_FRAME_TYPE(VOID_VIRTUAL_DISPATCH, print_on, (st)); +void stack_map_frame::print_on(outputStream* st, int offs = -1) const { + FOR_EACH_STACKMAP_FRAME_TYPE(VOID_VIRTUAL_DISPATCH, print_on, (st, offs)); } -#endif #undef VIRTUAL_DISPATCH #undef VOID_VIRTUAL_DISPATCH @@ -873,16 +856,46 @@ FOR_EACH_STACKMAP_FRAME_TYPE(AS_SUBTYPE_DEF, x, x) #undef AS_SUBTYPE_DEF +class stack_map_table { + private: + address number_of_entries_addr() const { + return (address)this; + } + address entries_addr() const { + return number_of_entries_addr() + sizeof(u2); + } + + protected: + // No constructors - should be 'private', but GCC issues a warning if it is + stack_map_table() {} + stack_map_table(const stack_map_table&) {} + + public: + + static stack_map_table* at(address addr) { + return (stack_map_table*)addr; + } + + u2 number_of_entries() const { + return Bytes::get_Java_u2(number_of_entries_addr()); + } + stack_map_frame* entries() const { + return stack_map_frame::at(entries_addr()); + } + + void set_number_of_entries(u2 num) { + Bytes::put_Java_u2(number_of_entries_addr(), num); + } +}; + class stack_map_table_attribute { private: address name_index_addr() const { return (address)this; } address attribute_length_addr() const { return name_index_addr() + sizeof(u2); } - address number_of_entries_addr() const { + address stack_map_table_addr() const { return attribute_length_addr() + sizeof(u4); } - address entries_addr() const { - return number_of_entries_addr() + sizeof(u2); } protected: // No constructors - should be 'private', but GCC issues a warning if it is @@ -896,17 +909,11 @@ } u2 name_index() const { - return Bytes::get_Java_u2(name_index_addr()); } + return Bytes::get_Java_u2(name_index_addr()); } u4 attribute_length() const { - return Bytes::get_Java_u4(attribute_length_addr()); } - u2 number_of_entries() const { - return Bytes::get_Java_u2(number_of_entries_addr()); } - stack_map_frame* entries() const { - return stack_map_frame::at(entries_addr()); - } - - static size_t header_size() { - return sizeof(u2) + sizeof(u4); + return Bytes::get_Java_u4(attribute_length_addr()); } + stack_map_table* table() const { + return stack_map_table::at(stack_map_table_addr()); } void set_name_index(u2 idx) { @@ -915,9 +922,8 @@ void set_attribute_length(u4 len) { Bytes::put_Java_u4(attribute_length_addr(), len); } - void set_number_of_entries(u2 num) { - Bytes::put_Java_u2(number_of_entries_addr(), num); - } }; +#undef FOR_EACH_STACKMAP_FRAME_TYPE + #endif // SHARE_VM_CLASSFILE_STACKMAPTABLEFORMAT_HPP diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/classfile/verificationType.cpp --- a/src/share/vm/classfile/verificationType.cpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/classfile/verificationType.cpp Wed Nov 16 05:45:24 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2012, 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 @@ -110,34 +110,34 @@ } } -#ifndef PRODUCT - void VerificationType::print_on(outputStream* st) const { switch (_u._data) { - case Bogus: st->print(" bogus "); break; - case Category1: st->print(" category1 "); break; - case Category2: st->print(" category2 "); break; - case Category2_2nd: st->print(" category2_2nd "); break; - case Boolean: st->print(" boolean "); break; - case Byte: st->print(" byte "); break; - case Short: st->print(" short "); break; - case Char: st->print(" char "); break; - case Integer: st->print(" integer "); break; - case Float: st->print(" float "); break; - case Long: st->print(" long "); break; - case Double: st->print(" double "); break; - case Long_2nd: st->print(" long_2nd "); break; - case Double_2nd: st->print(" double_2nd "); break; - case Null: st->print(" null "); break; + case Bogus: st->print("top"); break; + case Category1: st->print("category1"); break; + case Category2: st->print("category2"); break; + case Category2_2nd: st->print("category2_2nd"); break; + case Boolean: st->print("boolean"); break; + case Byte: st->print("byte"); break; + case Short: st->print("short"); break; + case Char: st->print("char"); break; + case Integer: st->print("integer"); break; + case Float: st->print("float"); break; + case Long: st->print("long"); break; + case Double: st->print("double"); break; + case Long_2nd: st->print("long_2nd"); break; + case Double_2nd: st->print("double_2nd"); break; + case Null: st->print("null"); break; + case ReferenceQuery: st->print("reference type"); break; + case Category1Query: st->print("category1 type"); break; + case Category2Query: st->print("category2 type"); break; + case Category2_2ndQuery: st->print("category2_2nd type"); break; default: if (is_uninitialized_this()) { - st->print(" uninitializedThis "); + st->print("uninitializedThis"); } else if (is_uninitialized()) { - st->print(" uninitialized %d ", bci()); + st->print("uninitialized %d", bci()); } else { - st->print(" class %s ", name()->as_klass_external_name()); + name()->print_value_on(st); } } } - -#endif diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/classfile/verificationType.hpp --- a/src/share/vm/classfile/verificationType.hpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/classfile/verificationType.hpp Wed Nov 16 05:45:24 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2012, 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 @@ -157,7 +157,7 @@ // For reference types, store the actual Symbol static VerificationType reference_type(Symbol* sh) { - assert(((uintptr_t)sh & 0x3) == 0, "Oops must be aligned"); + assert(((uintptr_t)sh & 0x3) == 0, "Symbols must be aligned"); // If the above assert fails in the future because oop* isn't aligned, // then this type encoding system will have to change to have a tag value // to descriminate between oops and primitives. @@ -303,7 +303,7 @@ return index; } - void print_on(outputStream* st) const PRODUCT_RETURN; + void print_on(outputStream* st) const; private: diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/classfile/verifier.cpp --- a/src/share/vm/classfile/verifier.cpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/classfile/verifier.cpp Wed Nov 16 05:45:24 2016 +0000 @@ -26,9 +26,12 @@ #include "classfile/classFileStream.hpp" #include "classfile/javaClasses.hpp" #include "classfile/stackMapTable.hpp" +#include "classfile/stackMapFrame.hpp" +#include "classfile/stackMapTableFormat.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/verifier.hpp" #include "classfile/vmSymbols.hpp" +#include "interpreter/bytecodes.hpp" #include "interpreter/bytecodeStream.hpp" #include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" @@ -110,8 +113,11 @@ Symbol* exception_name = NULL; const size_t message_buffer_len = klass->name()->utf8_length() + 1024; char* message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len); + char* exception_message = message_buffer; const char* klassName = klass->external_name(); + bool can_failover = FailOverToOldVerifier && + klass->major_version() < NOFAILOVER_MAJOR_VERSION; // If the class should be verified, first see if we can use the split // verifier. If not, or if verification fails and FailOverToOldVerifier @@ -122,27 +128,28 @@ } if (UseSplitVerifier && klass->major_version() >= STACKMAP_ATTRIBUTE_MAJOR_VERSION) { - ClassVerifier split_verifier( - klass, message_buffer, message_buffer_len, THREAD); - split_verifier.verify_class(THREAD); - exception_name = split_verifier.result(); - if (klass->major_version() < NOFAILOVER_MAJOR_VERSION && - FailOverToOldVerifier && !HAS_PENDING_EXCEPTION && + ClassVerifier split_verifier(klass, THREAD); + split_verifier.verify_class(THREAD); + exception_name = split_verifier.result(); + if (can_failover && !HAS_PENDING_EXCEPTION && (exception_name == vmSymbols::java_lang_VerifyError() || exception_name == vmSymbols::java_lang_ClassFormatError())) { - if (TraceClassInitialization) { + if (TraceClassInitialization || VerboseVerification) { tty->print_cr( "Fail over class verification to old verifier for: %s", klassName); } exception_name = inference_verify( klass, message_buffer, message_buffer_len, THREAD); } + if (exception_name != NULL) { + exception_message = split_verifier.exception_message(); + } } else { exception_name = inference_verify( klass, message_buffer, message_buffer_len, THREAD); } - if (TraceClassInitialization) { + if (TraceClassInitialization || VerboseVerification) { if (HAS_PENDING_EXCEPTION) { tty->print("Verification for %s has", klassName); tty->print_cr(" exception pending %s ", @@ -173,7 +180,7 @@ kls = kls->super(); } message_buffer[message_buffer_len - 1] = '\0'; // just to be sure - THROW_MSG_(exception_name, message_buffer, false); + THROW_MSG_(exception_name, exception_message, false); } } @@ -221,7 +228,7 @@ } ResourceMark rm(THREAD); - if (ClassVerifier::_verify_verbose) { + if (VerboseVerification) { tty->print_cr("Verifying class %s with old format", klass->external_name()); } @@ -265,14 +272,252 @@ } } +TypeOrigin TypeOrigin::null() { + return TypeOrigin(); +} +TypeOrigin TypeOrigin::local(u2 index, StackMapFrame* frame) { + assert(frame != NULL, "Must have a frame"); + return TypeOrigin(CF_LOCALS, index, StackMapFrame::copy(frame), + frame->local_at(index)); +} +TypeOrigin TypeOrigin::stack(u2 index, StackMapFrame* frame) { + assert(frame != NULL, "Must have a frame"); + return TypeOrigin(CF_STACK, index, StackMapFrame::copy(frame), + frame->stack_at(index)); +} +TypeOrigin TypeOrigin::sm_local(u2 index, StackMapFrame* frame) { + assert(frame != NULL, "Must have a frame"); + return TypeOrigin(SM_LOCALS, index, StackMapFrame::copy(frame), + frame->local_at(index)); +} +TypeOrigin TypeOrigin::sm_stack(u2 index, StackMapFrame* frame) { + assert(frame != NULL, "Must have a frame"); + return TypeOrigin(SM_STACK, index, StackMapFrame::copy(frame), + frame->stack_at(index)); +} +TypeOrigin TypeOrigin::bad_index(u2 index) { + return TypeOrigin(BAD_INDEX, index, NULL, VerificationType::bogus_type()); +} +TypeOrigin TypeOrigin::cp(u2 index, VerificationType vt) { + return TypeOrigin(CONST_POOL, index, NULL, vt); +} +TypeOrigin TypeOrigin::signature(VerificationType vt) { + return TypeOrigin(SIG, 0, NULL, vt); +} +TypeOrigin TypeOrigin::implicit(VerificationType t) { + return TypeOrigin(IMPLICIT, 0, NULL, t); +} +TypeOrigin TypeOrigin::frame(StackMapFrame* frame) { + return TypeOrigin(FRAME_ONLY, 0, StackMapFrame::copy(frame), + VerificationType::bogus_type()); +} + +void TypeOrigin::reset_frame() { + if (_frame != NULL) { + _frame->restore(); + } +} + +void TypeOrigin::details(outputStream* ss) const { + _type.print_on(ss); + switch (_origin) { + case CF_LOCALS: + ss->print(" (current frame, locals[%d])", _index); + break; + case CF_STACK: + ss->print(" (current frame, stack[%d])", _index); + break; + case SM_LOCALS: + ss->print(" (stack map, locals[%d])", _index); + break; + case SM_STACK: + ss->print(" (stack map, stack[%d])", _index); + break; + case CONST_POOL: + ss->print(" (constant pool %d)", _index); + break; + case SIG: + ss->print(" (from method signature)"); + break; + case IMPLICIT: + case FRAME_ONLY: + case NONE: + default: + ; + } +} + +#ifdef ASSERT +void TypeOrigin::print_on(outputStream* str) const { + str->print("{%d,%d,%p:", _origin, _index, _frame); + if (_frame != NULL) { + _frame->print_on(str); + } else { + str->print("null"); + } + str->print(","); + _type.print_on(str); + str->print("}"); +} +#endif + +void ErrorContext::details(outputStream* ss, methodOop method) const { + if (is_valid()) { + ss->print_cr(""); + ss->print_cr("Exception Details:"); + location_details(ss, method); + reason_details(ss); + frame_details(ss); + bytecode_details(ss, method); + handler_details(ss, method); + stackmap_details(ss, method); + } +} + +void ErrorContext::reason_details(outputStream* ss) const { + streamIndentor si(ss); + ss->indent().print_cr("Reason:"); + streamIndentor si2(ss); + ss->indent().print(""); + switch (_fault) { + case INVALID_BYTECODE: + ss->print("Error exists in the bytecode"); + break; + case WRONG_TYPE: + if (_expected.is_valid()) { + ss->print("Type "); + _type.details(ss); + ss->print(" is not assignable to "); + _expected.details(ss); + } else { + ss->print("Invalid type: "); + _type.details(ss); + } + break; + case FLAGS_MISMATCH: + if (_expected.is_valid()) { + ss->print("Current frame's flags are not assignable " + "to stack map frame's."); + } else { + ss->print("Current frame's flags are invalid in this context."); + } + break; + case BAD_CP_INDEX: + ss->print("Constant pool index %d is invalid", _type.index()); + break; + case BAD_LOCAL_INDEX: + ss->print("Local index %d is invalid", _type.index()); + break; + case LOCALS_SIZE_MISMATCH: + ss->print("Current frame's local size doesn't match stackmap."); + break; + case STACK_SIZE_MISMATCH: + ss->print("Current frame's stack size doesn't match stackmap."); + break; + case STACK_OVERFLOW: + ss->print("Exceeded max stack size."); + break; + case STACK_UNDERFLOW: + ss->print("Attempt to pop empty stack."); + break; + case MISSING_STACKMAP: + ss->print("Expected stackmap frame at this location."); + break; + case BAD_STACKMAP: + ss->print("Invalid stackmap specification."); + break; + case UNKNOWN: + default: + ShouldNotReachHere(); + ss->print_cr("Unknown"); + } + ss->print_cr(""); +} + +void ErrorContext::location_details(outputStream* ss, methodOop method) const { + if (_bci != -1 && method != NULL) { + streamIndentor si(ss); + const char* bytecode_name = ""; + if (method->validate_bci_from_bcx(_bci) != -1) { + Bytecodes::Code code = Bytecodes::code_or_bp_at(method->bcp_from(_bci)); + if (Bytecodes::is_defined(code)) { + bytecode_name = Bytecodes::name(code); + } else { + bytecode_name = ""; + } + } + instanceKlass* ik = instanceKlass::cast(method->method_holder()); + ss->indent().print_cr("Location:"); + streamIndentor si2(ss); + ss->indent().print_cr("%s.%s%s @%d: %s", + ik->name()->as_C_string(), method->name()->as_C_string(), + method->signature()->as_C_string(), _bci, bytecode_name); + } +} + +void ErrorContext::frame_details(outputStream* ss) const { + streamIndentor si(ss); + if (_type.is_valid() && _type.frame() != NULL) { + ss->indent().print_cr("Current Frame:"); + streamIndentor si2(ss); + _type.frame()->print_on(ss); + } + if (_expected.is_valid() && _expected.frame() != NULL) { + ss->indent().print_cr("Stackmap Frame:"); + streamIndentor si2(ss); + _expected.frame()->print_on(ss); + } +} + +void ErrorContext::bytecode_details(outputStream* ss, methodOop method) const { + if (method != NULL) { + streamIndentor si(ss); + ss->indent().print_cr("Bytecode:"); + streamIndentor si2(ss); + ss->print_data(method->code_base(), method->code_size(), false); + } +} + +void ErrorContext::handler_details(outputStream* ss, methodOop method) const { + if (method != NULL) { + streamIndentor si(ss); + ExceptionTable table(method); + if (table.length() > 0) { + ss->indent().print_cr("Exception Handler Table:"); + streamIndentor si2(ss); + for (int i = 0; i < table.length(); ++i) { + ss->indent().print_cr("bci [%d, %d] => handler: %d", table.start_pc(i), + table.end_pc(i), table.handler_pc(i)); + } + } + } +} + +void ErrorContext::stackmap_details(outputStream* ss, methodOop method) const { + if (method != NULL && method->has_stackmap_table()) { + streamIndentor si(ss); + ss->indent().print_cr("Stackmap Table:"); + typeArrayOop data = method->stackmap_data(); + stack_map_table* sm_table = + stack_map_table::at((address)data->byte_at_addr(0)); + stack_map_frame* sm_frame = sm_table->entries(); + streamIndentor si2(ss); + int current_offset = -1; + for (u2 i = 0; i < sm_table->number_of_entries(); ++i) { + ss->indent(); + sm_frame->print_on(ss, current_offset); + ss->print_cr(""); + current_offset += sm_frame->offset_delta(); + sm_frame = sm_frame->next(); + } + } +} + // Methods in ClassVerifier -bool ClassVerifier::_verify_verbose = false; - ClassVerifier::ClassVerifier( - instanceKlassHandle klass, char* msg, size_t msg_len, TRAPS) - : _thread(THREAD), _exception_type(NULL), _message(msg), - _message_buffer_len(msg_len), _klass(klass) { + instanceKlassHandle klass, TRAPS) + : _thread(THREAD), _exception_type(NULL), _message(NULL), _klass(klass) { _this_type = VerificationType::reference_type(klass->name()); // Create list to hold symbols in reference area. _symbols = new GrowableArray(100, 0, NULL); @@ -290,8 +535,14 @@ return VerificationType::reference_type(vmSymbols::java_lang_Object()); } +TypeOrigin ClassVerifier::ref_ctx(const char* sig, TRAPS) { + VerificationType vt = VerificationType::reference_type( + create_temporary_symbol(sig, (int)strlen(sig), THREAD)); + return TypeOrigin::implicit(vt); +} + void ClassVerifier::verify_class(TRAPS) { - if (_verify_verbose) { + if (VerboseVerification) { tty->print_cr("Verifying class %s with new format", _klass->external_name()); } @@ -312,7 +563,7 @@ verify_method(methodHandle(THREAD, m), CHECK_VERIFY(this)); } - if (_verify_verbose || TraceClassInitialization) { + if (VerboseVerification || TraceClassInitialization) { if (was_recursively_verified()) tty->print_cr("Recursive verification detected for: %s", _klass->external_name()); @@ -321,7 +572,7 @@ void ClassVerifier::verify_method(methodHandle m, TRAPS) { _method = m; // initialize _method - if (_verify_verbose) { + if (VerboseVerification) { tty->print_cr("Verifying method %s", m->name_and_sig_as_C_string()); } @@ -368,8 +619,8 @@ StackMapTable stackmap_table(&reader, ¤t_frame, max_locals, max_stack, code_data, code_length, CHECK_VERIFY(this)); - if (_verify_verbose) { - stackmap_table.print(); + if (VerboseVerification) { + stackmap_table.print_on(tty); } RawBytecodeStream bcs(m); @@ -389,6 +640,7 @@ // Set current frame's offset to bci current_frame.set_offset(bci); + current_frame.set_mark(); // Make sure every offset in stackmap table point to the beginning to // an instruction. Match current_frame to stackmap_table entry with @@ -397,6 +649,7 @@ stackmap_index, bci, ¤t_frame, &stackmap_table, no_control_flow, CHECK_VERIFY(this)); + bool this_uninit = false; // Set to true when invokespecial initialized 'this' // Merge with the next instruction @@ -407,8 +660,8 @@ VerificationType atype; #ifndef PRODUCT - if (_verify_verbose) { - current_frame.print(); + if (VerboseVerification) { + current_frame.print_on(tty); tty->print_cr("offset = %d, opcode = %s", bci, Bytecodes::name(opcode)); } #endif @@ -421,7 +674,10 @@ opcode != Bytecodes::_lstore && opcode != Bytecodes::_fload && opcode != Bytecodes::_dload && opcode != Bytecodes::_fstore && opcode != Bytecodes::_dstore) { - verify_error(bci, "Bad wide instruction"); + /* Unreachable? RawBytecodeStream's raw_next() returns 'illegal' + * if we encounter a wide instruction that modifies an invalid + * opcode (not one of the ones listed above) */ + verify_error(ErrorContext::bad_code(bci), "Bad wide instruction"); return; } } @@ -533,7 +789,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_int_array()) { - verify_error(bci, bad_type_msg, "iaload"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[I", THREAD)), + bad_type_msg, "iaload"); return; } current_frame.push_stack( @@ -545,7 +803,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_bool_array() && !atype.is_byte_array()) { - verify_error(bci, bad_type_msg, "baload"); + verify_error( + ErrorContext::bad_type(bci, current_frame.stack_top_ctx()), + bad_type_msg, "baload"); return; } current_frame.push_stack( @@ -557,7 +817,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_char_array()) { - verify_error(bci, bad_type_msg, "caload"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[C", THREAD)), + bad_type_msg, "caload"); return; } current_frame.push_stack( @@ -569,7 +831,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_short_array()) { - verify_error(bci, bad_type_msg, "saload"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[S", THREAD)), + bad_type_msg, "saload"); return; } current_frame.push_stack( @@ -581,7 +845,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_long_array()) { - verify_error(bci, bad_type_msg, "laload"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[J", THREAD)), + bad_type_msg, "laload"); return; } current_frame.push_stack_2( @@ -594,7 +860,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_float_array()) { - verify_error(bci, bad_type_msg, "faload"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[F", THREAD)), + bad_type_msg, "faload"); return; } current_frame.push_stack( @@ -606,7 +874,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_double_array()) { - verify_error(bci, bad_type_msg, "daload"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[D", THREAD)), + bad_type_msg, "daload"); return; } current_frame.push_stack_2( @@ -619,7 +889,10 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_reference_array()) { - verify_error(bci, bad_type_msg, "aaload"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), + TypeOrigin::implicit(VerificationType::reference_check())), + bad_type_msg, "aaload"); return; } if (atype.is_null()) { @@ -690,7 +963,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_int_array()) { - verify_error(bci, bad_type_msg, "iastore"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[I", THREAD)), + bad_type_msg, "iastore"); return; } no_control_flow = false; break; @@ -702,7 +977,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_bool_array() && !atype.is_byte_array()) { - verify_error(bci, bad_type_msg, "bastore"); + verify_error( + ErrorContext::bad_type(bci, current_frame.stack_top_ctx()), + bad_type_msg, "bastore"); return; } no_control_flow = false; break; @@ -714,7 +991,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_char_array()) { - verify_error(bci, bad_type_msg, "castore"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[C", THREAD)), + bad_type_msg, "castore"); return; } no_control_flow = false; break; @@ -726,7 +1005,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_short_array()) { - verify_error(bci, bad_type_msg, "sastore"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[S", THREAD)), + bad_type_msg, "sastore"); return; } no_control_flow = false; break; @@ -739,7 +1020,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_long_array()) { - verify_error(bci, bad_type_msg, "lastore"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[J", THREAD)), + bad_type_msg, "lastore"); return; } no_control_flow = false; break; @@ -751,7 +1034,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_float_array()) { - verify_error(bci, bad_type_msg, "fastore"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[F", THREAD)), + bad_type_msg, "fastore"); return; } no_control_flow = false; break; @@ -764,7 +1049,9 @@ atype = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!atype.is_double_array()) { - verify_error(bci, bad_type_msg, "dastore"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), ref_ctx("[D", THREAD)), + bad_type_msg, "dastore"); return; } no_control_flow = false; break; @@ -776,7 +1063,10 @@ VerificationType::reference_check(), CHECK_VERIFY(this)); // more type-checking is done at runtime if (!atype.is_reference_array()) { - verify_error(bci, bad_type_msg, "aastore"); + verify_error(ErrorContext::bad_type(bci, + current_frame.stack_top_ctx(), + TypeOrigin::implicit(VerificationType::reference_check())), + bad_type_msg, "aastore"); return; } // 4938384: relaxed constraint in JVMS 3nd edition. @@ -794,7 +1084,11 @@ current_frame.pop_stack( VerificationType::category2_check(), CHECK_VERIFY(this)); } else { - verify_error(bci, bad_type_msg, "pop2"); + /* Unreachable? Would need a category2_1st on TOS + * which does not appear possible. */ + verify_error( + ErrorContext::bad_type(bci, current_frame.stack_top_ctx()), + bad_type_msg, "pop2"); return; } no_control_flow = false; break; @@ -826,7 +1120,10 @@ type3 = current_frame.pop_stack( VerificationType::category2_check(), CHECK_VERIFY(this)); } else { - verify_error(bci, bad_type_msg, "dup_x2"); + /* Unreachable? Would need a category2_1st at stack depth 2 with + * a category1 on TOS which does not appear possible. */ + verify_error(ErrorContext::bad_type( + bci, current_frame.stack_top_ctx()), bad_type_msg, "dup_x2"); return; } current_frame.push_stack(type, CHECK_VERIFY(this)); @@ -844,7 +1141,11 @@ type2 = current_frame.pop_stack( VerificationType::category2_check(), CHECK_VERIFY(this)); } else { - verify_error(bci, bad_type_msg, "dup2"); + /* Unreachable? Would need a category2_1st on TOS which does not + * appear possible. */ + verify_error( + ErrorContext::bad_type(bci, current_frame.stack_top_ctx()), + bad_type_msg, "dup2"); return; } current_frame.push_stack(type2, CHECK_VERIFY(this)); @@ -859,11 +1160,15 @@ if (type.is_category1()) { type2 = current_frame.pop_stack( VerificationType::category1_check(), CHECK_VERIFY(this)); - } else if(type.is_category2_2nd()) { - type2 = current_frame.pop_stack - (VerificationType::category2_check(), CHECK_VERIFY(this)); + } else if (type.is_category2_2nd()) { + type2 = current_frame.pop_stack( + VerificationType::category2_check(), CHECK_VERIFY(this)); } else { - verify_error(bci, bad_type_msg, "dup2_x1"); + /* Unreachable? Would need a category2_1st on TOS which does + * not appear possible. */ + verify_error( + ErrorContext::bad_type(bci, current_frame.stack_top_ctx()), + bad_type_msg, "dup2_x1"); return; } type3 = current_frame.pop_stack( @@ -886,7 +1191,11 @@ type2 = current_frame.pop_stack( VerificationType::category2_check(), CHECK_VERIFY(this)); } else { - verify_error(bci, bad_type_msg, "dup2_x2"); + /* Unreachable? Would need a category2_1st on TOS which does + * not appear possible. */ + verify_error( + ErrorContext::bad_type(bci, current_frame.stack_top_ctx()), + bad_type_msg, "dup2_x2"); return; } type3 = current_frame.pop_stack(CHECK_VERIFY(this)); @@ -897,7 +1206,12 @@ type4 = current_frame.pop_stack( VerificationType::category2_check(), CHECK_VERIFY(this)); } else { - verify_error(bci, bad_type_msg, "dup2_x2"); + /* Unreachable? Would need a category2_1st on TOS after popping + * a long/double or two category 1's, which does not + * appear possible. */ + verify_error( + ErrorContext::bad_type(bci, current_frame.stack_top_ctx()), + bad_type_msg, "dup2_x2"); return; } current_frame.push_stack(type2, CHECK_VERIFY(this)); @@ -1177,43 +1491,50 @@ case Bytecodes::_ireturn : type = current_frame.pop_stack( VerificationType::integer_type(), CHECK_VERIFY(this)); - verify_return_value(return_type, type, bci, CHECK_VERIFY(this)); + verify_return_value(return_type, type, bci, + ¤t_frame, CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_lreturn : type2 = current_frame.pop_stack( VerificationType::long2_type(), CHECK_VERIFY(this)); type = current_frame.pop_stack( VerificationType::long_type(), CHECK_VERIFY(this)); - verify_return_value(return_type, type, bci, CHECK_VERIFY(this)); + verify_return_value(return_type, type, bci, + ¤t_frame, CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_freturn : type = current_frame.pop_stack( VerificationType::float_type(), CHECK_VERIFY(this)); - verify_return_value(return_type, type, bci, CHECK_VERIFY(this)); + verify_return_value(return_type, type, bci, + ¤t_frame, CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_dreturn : type2 = current_frame.pop_stack( VerificationType::double2_type(), CHECK_VERIFY(this)); type = current_frame.pop_stack( VerificationType::double_type(), CHECK_VERIFY(this)); - verify_return_value(return_type, type, bci, CHECK_VERIFY(this)); + verify_return_value(return_type, type, bci, + ¤t_frame, CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_areturn : type = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); - verify_return_value(return_type, type, bci, CHECK_VERIFY(this)); + verify_return_value(return_type, type, bci, + ¤t_frame, CHECK_VERIFY(this)); no_control_flow = true; break; case Bytecodes::_return : if (return_type != VerificationType::bogus_type()) { - verify_error(bci, "Method expects no return value"); + verify_error(ErrorContext::bad_code(bci), + "Method expects a return value"); return; } // Make sure "this" has been initialized if current method is an // if (_method->name() == vmSymbols::object_initializer_name() && current_frame.flag_this_uninit()) { - verify_error(bci, - "Constructor must call super() or this() before return"); + verify_error(ErrorContext::bad_code(bci), + "Constructor must call super() or this() " + "before return"); return; } no_control_flow = true; break; @@ -1240,11 +1561,13 @@ case Bytecodes::_new : { index = bcs.get_index_u2(); - verify_cp_class_type(index, cp, CHECK_VERIFY(this)); + verify_cp_class_type(bci, index, cp, CHECK_VERIFY(this)); VerificationType new_class_type = cp_index_to_type(index, cp, CHECK_VERIFY(this)); if (!new_class_type.is_object()) { - verify_error(bci, "Illegal new instruction"); + verify_error(ErrorContext::bad_type(bci, + TypeOrigin::cp(index, new_class_type)), + "Illegal new instruction"); return; } type = VerificationType::uninitialized_type(bci); @@ -1259,13 +1582,15 @@ no_control_flow = false; break; case Bytecodes::_anewarray : verify_anewarray( - bcs.get_index_u2(), cp, ¤t_frame, CHECK_VERIFY(this)); + bci, bcs.get_index_u2(), cp, ¤t_frame, CHECK_VERIFY(this)); no_control_flow = false; break; case Bytecodes::_arraylength : type = current_frame.pop_stack( VerificationType::reference_check(), CHECK_VERIFY(this)); if (!(type.is_null() || type.is_array())) { - verify_error(bci, bad_type_msg, "arraylength"); + verify_error(ErrorContext::bad_type( + bci, current_frame.stack_top_ctx()), + bad_type_msg, "arraylength"); } current_frame.push_stack( VerificationType::integer_type(), CHECK_VERIFY(this)); @@ -1273,7 +1598,7 @@ case Bytecodes::_checkcast : { index = bcs.get_index_u2(); - verify_cp_class_type(index, cp, CHECK_VERIFY(this)); + verify_cp_class_type(bci, index, cp, CHECK_VERIFY(this)); current_frame.pop_stack(object_type(), CHECK_VERIFY(this)); VerificationType klass_type = cp_index_to_type( index, cp, CHECK_VERIFY(this)); @@ -1282,7 +1607,7 @@ } case Bytecodes::_instanceof : { index = bcs.get_index_u2(); - verify_cp_class_type(index, cp, CHECK_VERIFY(this)); + verify_cp_class_type(bci, index, cp, CHECK_VERIFY(this)); current_frame.pop_stack(object_type(), CHECK_VERIFY(this)); current_frame.push_stack( VerificationType::integer_type(), CHECK_VERIFY(this)); @@ -1297,17 +1622,18 @@ { index = bcs.get_index_u2(); u2 dim = *(bcs.bcp()+3); - verify_cp_class_type(index, cp, CHECK_VERIFY(this)); + verify_cp_class_type(bci, index, cp, CHECK_VERIFY(this)); VerificationType new_array_type = cp_index_to_type(index, cp, CHECK_VERIFY(this)); if (!new_array_type.is_array()) { - verify_error(bci, - "Illegal constant pool index in multianewarray instruction"); + verify_error(ErrorContext::bad_type(bci, + TypeOrigin::cp(index, new_array_type)), + "Illegal constant pool index in multianewarray instruction"); return; } if (dim < 1 || new_array_type.dimensions() < dim) { - verify_error(bci, - "Illegal dimension in multianewarray instruction"); + verify_error(ErrorContext::bad_code(bci), + "Illegal dimension in multianewarray instruction: %d", dim); return; } for (int i = 0; i < dim; i++) { @@ -1325,7 +1651,8 @@ default: // We only need to check the valid bytecodes in class file. // And jsr and ret are not in the new class file format in JDK1.5. - verify_error(bci, "Bad instruction"); + verify_error(ErrorContext::bad_code(bci), + "Bad instruction: %02x", opcode); no_control_flow = false; return; } // end switch @@ -1341,7 +1668,8 @@ // Make sure that control flow does not fall through end of the method if (!no_control_flow) { - verify_error(code_length, "Control flow falls through code end"); + verify_error(ErrorContext::bad_code(code_length), + "Control flow falls through code end"); return; } } @@ -1360,7 +1688,7 @@ code_data[bci] = BYTECODE_OFFSET; } } else { - verify_error(bcs.bci(), "Bad instruction"); + verify_error(ErrorContext::bad_code(bcs.bci()), "Bad instruction"); return NULL; } } @@ -1403,9 +1731,11 @@ catch_type, this, CHECK_VERIFY(this)); if (!is_subclass) { // 4286534: should throw VerifyError according to recent spec change - verify_error( - "Catch type is not a subclass of Throwable in handler %d", - handler_pc); + verify_error(ErrorContext::bad_type(handler_pc, + TypeOrigin::cp(catch_type_index, catch_type), + TypeOrigin::implicit(throwable)), + "Catch type is not a subclass " + "of Throwable in exception handler %d", handler_pc); return; } } @@ -1445,19 +1775,21 @@ if (stackmap_index < stackmap_table->get_frame_count()) { u2 this_offset = stackmap_table->get_offset(stackmap_index); if (no_control_flow && this_offset > bci) { - verify_error(bci, "Expecting a stack map frame"); + verify_error(ErrorContext::missing_stackmap(bci), + "Expecting a stack map frame"); return 0; } if (this_offset == bci) { + ErrorContext ctx; // See if current stack map can be assigned to the frame in table. // current_frame is the stackmap frame got from the last instruction. // If matched, current_frame will be updated by this method. - bool match = stackmap_table->match_stackmap( + bool matches = stackmap_table->match_stackmap( current_frame, this_offset, stackmap_index, - !no_control_flow, true, false, CHECK_VERIFY_(this, 0)); - if (!match) { + !no_control_flow, true, false, &ctx, CHECK_VERIFY_(this, 0)); + if (!matches) { // report type error - verify_error(bci, "Instruction type does not match stack map"); + verify_error(ctx, "Instruction type does not match stack map"); return 0; } stackmap_index++; @@ -1467,7 +1799,7 @@ return 0; } } else if (no_control_flow) { - verify_error(bci, "Expecting a stack map frame"); + verify_error(ErrorContext::bad_code(bci), "Expecting a stack map frame"); return 0; } return stackmap_index; @@ -1499,29 +1831,31 @@ VerificationType::reference_type(vmSymbols::java_lang_Throwable()); new_frame->push_stack(throwable, CHECK_VERIFY(this)); } - bool match = stackmap_table->match_stackmap( - new_frame, handler_pc, true, false, true, CHECK_VERIFY(this)); - if (!match) { - verify_error(bci, - "Stack map does not match the one at exception handler %d", - handler_pc); + ErrorContext ctx; + bool matches = stackmap_table->match_stackmap( + new_frame, handler_pc, true, false, true, &ctx, CHECK_VERIFY(this)); + if (!matches) { + verify_error(ctx, "Stack map does not match the one at " + "exception handler %d", handler_pc); return; } } } } -void ClassVerifier::verify_cp_index(constantPoolHandle cp, int index, TRAPS) { +void ClassVerifier::verify_cp_index( + u2 bci, constantPoolHandle cp, int index, TRAPS) { int nconstants = cp->length(); if ((index <= 0) || (index >= nconstants)) { - verify_error("Illegal constant pool index %d in class %s", - index, instanceKlass::cast(cp->pool_holder())->external_name()); + verify_error(ErrorContext::bad_cp_index(bci, index), + "Illegal constant pool index %d in class %s", + index, instanceKlass::cast(cp->pool_holder())->external_name()); return; } } void ClassVerifier::verify_cp_type( - int index, constantPoolHandle cp, unsigned int types, TRAPS) { + u2 bci, int index, constantPoolHandle cp, unsigned int types, TRAPS) { // In some situations, bytecode rewriting may occur while we're verifying. // In this case, a constant pool cache exists and some indices refer to that @@ -1529,10 +1863,10 @@ // We must check was_recursively_verified() before we get here. guarantee(cp->cache() == NULL, "not rewritten yet"); - verify_cp_index(cp, index, CHECK_VERIFY(this)); + verify_cp_index(bci, cp, index, CHECK_VERIFY(this)); unsigned int tag = cp->tag_at(index).value(); if ((types & (1 << tag)) == 0) { - verify_error( + verify_error(ErrorContext::bad_cp_index(bci, index), "Illegal type at constant pool entry %d in class %s", index, instanceKlass::cast(cp->pool_holder())->external_name()); return; @@ -1540,51 +1874,46 @@ } void ClassVerifier::verify_cp_class_type( - int index, constantPoolHandle cp, TRAPS) { - verify_cp_index(cp, index, CHECK_VERIFY(this)); + u2 bci, int index, constantPoolHandle cp, TRAPS) { + verify_cp_index(bci, cp, index, CHECK_VERIFY(this)); constantTag tag = cp->tag_at(index); if (!tag.is_klass() && !tag.is_unresolved_klass()) { - verify_error("Illegal type at constant pool entry %d in class %s", - index, instanceKlass::cast(cp->pool_holder())->external_name()); + verify_error(ErrorContext::bad_cp_index(bci, index), + "Illegal type at constant pool entry %d in class %s", + index, instanceKlass::cast(cp->pool_holder())->external_name()); return; } } -void ClassVerifier::format_error_message( - const char* fmt, int offset, va_list va) { - ResourceMark rm(_thread); - stringStream message(_message, _message_buffer_len); - message.vprint(fmt, va); - if (!_method.is_null()) { - message.print(" in method %s", _method->name_and_sig_as_C_string()); - } - if (offset != -1) { - message.print(" at offset %d", offset); - } -} +void ClassVerifier::verify_error(ErrorContext ctx, const char* msg, ...) { + stringStream ss; -void ClassVerifier::verify_error(u2 offset, const char* fmt, ...) { + ctx.reset_frames(); _exception_type = vmSymbols::java_lang_VerifyError(); + _error_context = ctx; va_list va; - va_start(va, fmt); - format_error_message(fmt, offset, va); + va_start(va, msg); + ss.vprint(msg, va); va_end(va); -} - -void ClassVerifier::verify_error(const char* fmt, ...) { - _exception_type = vmSymbols::java_lang_VerifyError(); - va_list va; - va_start(va, fmt); - format_error_message(fmt, -1, va); - va_end(va); + _message = ss.as_string(); +#ifdef ASSERT + ResourceMark rm; + const char* exception_name = _exception_type->as_C_string(); + Exceptions::debug_check_abort(exception_name, NULL); +#endif // ndef ASSERT } void ClassVerifier::class_format_error(const char* msg, ...) { + stringStream ss; _exception_type = vmSymbols::java_lang_ClassFormatError(); va_list va; va_start(va, msg); - format_error_message(msg, -1, va); + ss.vprint(msg, va); va_end(va); + if (!_method.is_null()) { + ss.print(" in method %s", _method->name_and_sig_as_C_string()); + } + _message = ss.as_string(); } klassOop ClassVerifier::load_class(Symbol* name, TRAPS) { @@ -1620,7 +1949,7 @@ } } else { klassOop member_klass = target_instance->find_field(field_name, field_sig, &fd); - if(member_klass != NULL && fd.is_protected()) { + if (member_klass != NULL && fd.is_protected()) { if (!this_class->is_same_class_package(member_klass)) { return true; } @@ -1630,9 +1959,9 @@ } void ClassVerifier::verify_ldc( - int opcode, u2 index, StackMapFrame *current_frame, - constantPoolHandle cp, u2 bci, TRAPS) { - verify_cp_index(cp, index, CHECK_VERIFY(this)); + int opcode, u2 index, StackMapFrame* current_frame, + constantPoolHandle cp, u2 bci, TRAPS) { + verify_cp_index(bci, cp, index, CHECK_VERIFY(this)); constantTag tag = cp->tag_at(index); unsigned int types; if (opcode == Bytecodes::_ldc || opcode == Bytecodes::_ldc_w) { @@ -1642,12 +1971,12 @@ | (1 << JVM_CONSTANT_MethodHandle) | (1 << JVM_CONSTANT_MethodType); // Note: The class file parser already verified the legality of // MethodHandle and MethodType constants. - verify_cp_type(index, cp, types, CHECK_VERIFY(this)); + verify_cp_type(bci, index, cp, types, CHECK_VERIFY(this)); } } else { assert(opcode == Bytecodes::_ldc2_w, "must be ldc2_w"); types = (1 << JVM_CONSTANT_Double) | (1 << JVM_CONSTANT_Long); - verify_cp_type(index, cp, types, CHECK_VERIFY(this)); + verify_cp_type(bci, index, cp, types, CHECK_VERIFY(this)); } if (tag.is_string() && cp->is_pseudo_string_at(index)) { current_frame->push_stack(object_type(), CHECK_VERIFY(this)); @@ -1682,7 +2011,9 @@ VerificationType::reference_type( vmSymbols::java_lang_invoke_MethodType()), CHECK_VERIFY(this)); } else { - verify_error(bci, "Invalid index in ldc"); + /* Unreachable? verify_cp_type has already validated the cp type. */ + verify_error( + ErrorContext::bad_cp_index(bci, index), "Invalid index in ldc"); return; } } @@ -1698,7 +2029,8 @@ u2 padding_offset = 1; while ((bcp + padding_offset) < aligned_bcp) { if(*(bcp + padding_offset) != 0) { - verify_error(bci, "Nonzero padding byte in lookswitch or tableswitch"); + verify_error(ErrorContext::bad_code(bci), + "Nonzero padding byte in lookswitch or tableswitch"); return; } padding_offset++; @@ -1711,20 +2043,21 @@ jint low = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize); jint high = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize); if (low > high) { - verify_error(bci, - "low must be less than or equal to high in tableswitch"); + verify_error(ErrorContext::bad_code(bci), + "low must be less than or equal to high in tableswitch"); return; } keys = high - low + 1; if (keys < 0) { - verify_error(bci, "too many keys in tableswitch"); + verify_error(ErrorContext::bad_code(bci), "too many keys in tableswitch"); return; } delta = 1; } else { keys = (int)Bytes::get_Java_u4(aligned_bcp + jintSize); if (keys < 0) { - verify_error(bci, "number of keys in lookupswitch less than 0"); + verify_error(ErrorContext::bad_code(bci), + "number of keys in lookupswitch less than 0"); return; } delta = 2; @@ -1733,7 +2066,8 @@ jint this_key = Bytes::get_Java_u4(aligned_bcp + (2+2*i)*jintSize); jint next_key = Bytes::get_Java_u4(aligned_bcp + (2+2*i+2)*jintSize); if (this_key >= next_key) { - verify_error(bci, "Bad lookupswitch instruction"); + verify_error(ErrorContext::bad_code(bci), + "Bad lookupswitch instruction"); return; } } @@ -1768,7 +2102,8 @@ constantPoolHandle cp, TRAPS) { u2 index = bcs->get_index_u2(); - verify_cp_type(index, cp, 1 << JVM_CONSTANT_Fieldref, CHECK_VERIFY(this)); + verify_cp_type(bcs->bci(), index, cp, + 1 << JVM_CONSTANT_Fieldref, CHECK_VERIFY(this)); // Get field name and signature Symbol* field_name = cp->name_ref_at(index); @@ -1785,9 +2120,11 @@ VerificationType ref_class_type = cp_ref_index_to_type( index, cp, CHECK_VERIFY(this)); if (!ref_class_type.is_object()) { - verify_error( - "Expecting reference to class in class %s at constant pool index %d", - _klass->external_name(), index); + /* Unreachable? Class file parser verifies Fieldref contents */ + verify_error(ErrorContext::bad_type(bcs->bci(), + TypeOrigin::cp(index, ref_class_type)), + "Expecting reference to class in class %s at constant pool index %d", + _klass->external_name(), index); return; } VerificationType target_class_type = ref_class_type; @@ -1845,7 +2182,10 @@ is_assignable = target_class_type.is_assignable_from( stack_object_type, this, CHECK_VERIFY(this)); if (!is_assignable) { - verify_error(bci, "Bad type on operand stack in putfield"); + verify_error(ErrorContext::bad_type(bci, + current_frame->stack_top_ctx(), + TypeOrigin::cp(index, target_class_type)), + "Bad type on operand stack in putfield"); return; } } @@ -1869,7 +2209,10 @@ is_assignable = current_type().is_assignable_from( stack_object_type, this, CHECK_VERIFY(this)); if (!is_assignable) { - verify_error(bci, "Bad access to protected data in getfield"); + verify_error(ErrorContext::bad_type(bci, + current_frame->stack_top_ctx(), + TypeOrigin::implicit(current_type())), + "Bad access to protected data in getfield"); return; } } @@ -2071,7 +2414,7 @@ } void ClassVerifier::verify_invoke_init( - RawBytecodeStream* bcs, VerificationType ref_class_type, + RawBytecodeStream* bcs, u2 ref_class_index, VerificationType ref_class_type, StackMapFrame* current_frame, u4 code_length, bool in_try_block, bool *this_uninit, constantPoolHandle cp, StackMapTable* stackmap_table, TRAPS) { @@ -2083,7 +2426,10 @@ klassOop superk = current_class()->super(); if (ref_class_type.name() != current_class()->name() && ref_class_type.name() != superk->klass_part()->name()) { - verify_error(bci, "Bad method call"); + verify_error(ErrorContext::bad_type(bci, + TypeOrigin::implicit(ref_class_type), + TypeOrigin::implicit(current_type())), + "Bad method call"); return; } @@ -2101,10 +2447,10 @@ if (bci >= start_pc && bci < end_pc) { if (!ends_in_athrow(exhandlers.handler_pc(i))) { - verify_error( + verify_error(ErrorContext::bad_code(bci), "Bad method call from after the start of a try block"); return; - } else if (_verify_verbose) { + } else if (VerboseVerification) { ResourceMark rm; tty->print_cr( "Survived call to ends_in_athrow(): %s", @@ -2126,17 +2472,23 @@ u2 new_offset = type.bci(); address new_bcp = bcs->bcp() - bci + new_offset; if (new_offset > (code_length - 3) || (*new_bcp) != Bytecodes::_new) { - verify_error(new_offset, "Expecting new instruction"); + /* Unreachable? Stack map parsing ensures valid type and new + * instructions have a valid BCI. */ + verify_error(ErrorContext::bad_code(new_offset), + "Expecting new instruction"); return; } u2 new_class_index = Bytes::get_Java_u2(new_bcp + 1); - verify_cp_class_type(new_class_index, cp, CHECK_VERIFY(this)); + verify_cp_class_type(bci, new_class_index, cp, CHECK_VERIFY(this)); // The method must be an method of the indicated class VerificationType new_class_type = cp_index_to_type( new_class_index, cp, CHECK_VERIFY(this)); if (!new_class_type.equals(ref_class_type)) { - verify_error(bci, "Call to wrong method"); + verify_error(ErrorContext::bad_type(bci, + TypeOrigin::cp(new_class_index, new_class_type), + TypeOrigin::cp(ref_class_index, ref_class_type)), + "Call to wrong method"); return; } // According to the VM spec, if the referent class is a superclass of the @@ -2151,7 +2503,7 @@ vmSymbols::object_initializer_name(), cp->signature_ref_at(bcs->get_index_u2())); if (m == NULL) { - verify_error(bci, + verify_error(ErrorContext::bad_code(bci), "Call to missing method"); return; } @@ -2160,7 +2512,10 @@ bool assignable = current_type().is_assignable_from( objectref_type, this, CHECK_VERIFY(this)); if (!assignable) { - verify_error(bci, "Bad access to protected method"); + verify_error(ErrorContext::bad_type(bci, + TypeOrigin::cp(new_class_index, objectref_type), + TypeOrigin::implicit(current_type())), + "Bad access to protected method"); return; } } @@ -2174,7 +2529,8 @@ } current_frame->initialize_object(type, new_class_type); } else { - verify_error(bci, "Bad operand type when invoking "); + verify_error(ErrorContext::bad_type(bci, current_frame->stack_top_ctx()), + "Bad operand type when invoking "); return; } } @@ -2191,7 +2547,7 @@ : opcode == Bytecodes::_invokedynamic ? 1 << JVM_CONSTANT_InvokeDynamic : 1 << JVM_CONSTANT_Methodref); - verify_cp_type(index, cp, types, CHECK_VERIFY(this)); + verify_cp_type(bcs->bci(), index, cp, types, CHECK_VERIFY(this)); // Get method name and signature Symbol* method_name = cp->name_ref_at(index); @@ -2268,11 +2624,13 @@ // the difference between the size of the operand stack before and after the instruction // executes. if (*(bcp+3) != (nargs+1)) { - verify_error(bci, "Inconsistent args count operand in invokeinterface"); + verify_error(ErrorContext::bad_code(bci), + "Inconsistent args count operand in invokeinterface"); return; } if (*(bcp+4) != 0) { - verify_error(bci, "Fourth operand byte of invokeinterface must be zero"); + verify_error(ErrorContext::bad_code(bci), + "Fourth operand byte of invokeinterface must be zero"); return; } } @@ -2280,7 +2638,8 @@ if (opcode == Bytecodes::_invokedynamic) { address bcp = bcs->bcp(); if (*(bcp+3) != 0 || *(bcp+4) != 0) { - verify_error(bci, "Third and fourth operand bytes of invokedynamic must be zero"); + verify_error(ErrorContext::bad_code(bci), + "Third and fourth operand bytes of invokedynamic must be zero"); return; } } @@ -2289,7 +2648,8 @@ // Make sure can only be invoked by invokespecial if (opcode != Bytecodes::_invokespecial || method_name != vmSymbols::object_initializer_name()) { - verify_error(bci, "Illegal call to internal method"); + verify_error(ErrorContext::bad_code(bci), + "Illegal call to internal method"); return; } } else if (opcode == Bytecodes::_invokespecial @@ -2299,7 +2659,8 @@ bool subtype = ref_class_type.is_assignable_from( current_type(), this, CHECK_VERIFY(this)); if (!subtype) { - verify_error(bci, "Bad invokespecial instruction: " + verify_error(ErrorContext::bad_code(bci), + "Bad invokespecial instruction: " "current class isn't assignable to reference class."); return; } @@ -2312,7 +2673,7 @@ if (opcode != Bytecodes::_invokestatic && opcode != Bytecodes::_invokedynamic) { if (method_name == vmSymbols::object_initializer_name()) { // method - verify_invoke_init(bcs, ref_class_type, current_frame, + verify_invoke_init(bcs, index, ref_class_type, current_frame, code_length, in_try_block, this_uninit, cp, stackmap_table, CHECK_VERIFY(this)); } else { // other methods @@ -2343,8 +2704,10 @@ // Special case: arrays pretend to implement public Object // clone(). } else { - verify_error(bci, - "Bad access to protected data in invokevirtual"); + verify_error(ErrorContext::bad_type(bci, + current_frame->stack_top_ctx(), + TypeOrigin::implicit(current_type())), + "Bad access to protected data in invokevirtual"); return; } } @@ -2361,7 +2724,10 @@ if (sig_stream.type() != T_VOID) { if (method_name == vmSymbols::object_initializer_name()) { // method must have a void return type - verify_error(bci, "Return type must be void in method"); + /* Unreachable? Class file parser verifies that methods with '<' have + * void return */ + verify_error(ErrorContext::bad_code(bci), + "Return type must be void in method"); return; } VerificationType return_type[2]; @@ -2379,7 +2745,7 @@ NULL, NULL, NULL, NULL, "[Z", "[C", "[F", "[D", "[B", "[S", "[I", "[J", }; if (index < T_BOOLEAN || index > T_LONG) { - verify_error(bci, "Illegal newarray instruction"); + verify_error(ErrorContext::bad_code(bci), "Illegal newarray instruction"); return VerificationType::bogus_type(); } @@ -2390,8 +2756,9 @@ } void ClassVerifier::verify_anewarray( - u2 index, constantPoolHandle cp, StackMapFrame* current_frame, TRAPS) { - verify_cp_class_type(index, cp, CHECK_VERIFY(this)); + u2 bci, u2 index, constantPoolHandle cp, + StackMapFrame* current_frame, TRAPS) { + verify_cp_class_type(bci, index, cp, CHECK_VERIFY(this)); current_frame->pop_stack( VerificationType::integer_type(), CHECK_VERIFY(this)); @@ -2504,14 +2871,19 @@ } void ClassVerifier::verify_return_value( - VerificationType return_type, VerificationType type, u2 bci, TRAPS) { + VerificationType return_type, VerificationType type, u2 bci, + StackMapFrame* current_frame, TRAPS) { if (return_type == VerificationType::bogus_type()) { - verify_error(bci, "Method expects a return value"); + verify_error(ErrorContext::bad_type(bci, + current_frame->stack_top_ctx(), TypeOrigin::signature(return_type)), + "Method expects a return value"); return; } bool match = return_type.is_assignable_from(type, this, CHECK_VERIFY(this)); if (!match) { - verify_error(bci, "Bad return type"); + verify_error(ErrorContext::bad_type(bci, + current_frame->stack_top_ctx(), TypeOrigin::signature(return_type)), + "Bad return type"); return; } } diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/classfile/verifier.hpp --- a/src/share/vm/classfile/verifier.hpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/classfile/verifier.hpp Wed Nov 16 05:45:24 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2012, 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 @@ -89,18 +89,178 @@ #define CHECK_VERIFY_(verifier, result) \ CHECK_(result)); if ((verifier)->has_error()) return (result); ((void)0 +class TypeOrigin VALUE_OBJ_CLASS_SPEC { + private: + typedef enum { + CF_LOCALS, // Comes from the current frame locals + CF_STACK, // Comes from the current frame expression stack + SM_LOCALS, // Comes from stackmap locals + SM_STACK, // Comes from stackmap expression stack + CONST_POOL, // Comes from the constant pool + SIG, // Comes from method signature + IMPLICIT, // Comes implicitly from code or context + BAD_INDEX, // No type, but the index is bad + FRAME_ONLY, // No type, context just contains the frame + NONE + } Origin; + + Origin _origin; + u2 _index; // local, stack, or constant pool index + StackMapFrame* _frame; // source frame if CF or SM + VerificationType _type; // The actual type + + TypeOrigin( + Origin origin, u2 index, StackMapFrame* frame, VerificationType type) + : _origin(origin), _index(index), _frame(frame), _type(type) {} + + public: + TypeOrigin() : _origin(NONE), _index(0), _frame(NULL) {} + + static TypeOrigin null(); + static TypeOrigin local(u2 index, StackMapFrame* frame); + static TypeOrigin stack(u2 index, StackMapFrame* frame); + static TypeOrigin sm_local(u2 index, StackMapFrame* frame); + static TypeOrigin sm_stack(u2 index, StackMapFrame* frame); + static TypeOrigin cp(u2 index, VerificationType vt); + static TypeOrigin signature(VerificationType vt); + static TypeOrigin bad_index(u2 index); + static TypeOrigin implicit(VerificationType t); + static TypeOrigin frame(StackMapFrame* frame); + + void reset_frame(); + void details(outputStream* ss) const; + void print_frame(outputStream* ss) const; + const StackMapFrame* frame() const { return _frame; } + bool is_valid() const { return _origin != NONE; } + u2 index() const { return _index; } + +#ifdef ASSERT + void print_on(outputStream* str) const; +#endif +}; + +class ErrorContext VALUE_OBJ_CLASS_SPEC { + private: + typedef enum { + INVALID_BYTECODE, // There was a problem with the bytecode + WRONG_TYPE, // Type value was not as expected + FLAGS_MISMATCH, // Frame flags are not assignable + BAD_CP_INDEX, // Invalid constant pool index + BAD_LOCAL_INDEX, // Invalid local index + LOCALS_SIZE_MISMATCH, // Frames have differing local counts + STACK_SIZE_MISMATCH, // Frames have different stack sizes + STACK_OVERFLOW, // Attempt to push onto a full expression stack + STACK_UNDERFLOW, // Attempt to pop and empty expression stack + MISSING_STACKMAP, // No stackmap for this location and there should be + BAD_STACKMAP, // Format error in stackmap + NO_FAULT, // No error + UNKNOWN + } FaultType; + + int _bci; + FaultType _fault; + TypeOrigin _type; + TypeOrigin _expected; + + ErrorContext(int bci, FaultType fault) : + _bci(bci), _fault(fault) {} + ErrorContext(int bci, FaultType fault, TypeOrigin type) : + _bci(bci), _fault(fault), _type(type) {} + ErrorContext(int bci, FaultType fault, TypeOrigin type, TypeOrigin exp) : + _bci(bci), _fault(fault), _type(type), _expected(exp) {} + + public: + ErrorContext() : _bci(-1), _fault(NO_FAULT) {} + + static ErrorContext bad_code(u2 bci) { + return ErrorContext(bci, INVALID_BYTECODE); + } + static ErrorContext bad_type(u2 bci, TypeOrigin type) { + return ErrorContext(bci, WRONG_TYPE, type); + } + static ErrorContext bad_type(u2 bci, TypeOrigin type, TypeOrigin exp) { + return ErrorContext(bci, WRONG_TYPE, type, exp); + } + static ErrorContext bad_flags(u2 bci, StackMapFrame* frame) { + return ErrorContext(bci, FLAGS_MISMATCH, TypeOrigin::frame(frame)); + } + static ErrorContext bad_flags(u2 bci, StackMapFrame* cur, StackMapFrame* sm) { + return ErrorContext(bci, FLAGS_MISMATCH, + TypeOrigin::frame(cur), TypeOrigin::frame(sm)); + } + static ErrorContext bad_cp_index(u2 bci, u2 index) { + return ErrorContext(bci, BAD_CP_INDEX, TypeOrigin::bad_index(index)); + } + static ErrorContext bad_local_index(u2 bci, u2 index) { + return ErrorContext(bci, BAD_LOCAL_INDEX, TypeOrigin::bad_index(index)); + } + static ErrorContext locals_size_mismatch( + u2 bci, StackMapFrame* frame0, StackMapFrame* frame1) { + return ErrorContext(bci, LOCALS_SIZE_MISMATCH, + TypeOrigin::frame(frame0), TypeOrigin::frame(frame1)); + } + static ErrorContext stack_size_mismatch( + u2 bci, StackMapFrame* frame0, StackMapFrame* frame1) { + return ErrorContext(bci, STACK_SIZE_MISMATCH, + TypeOrigin::frame(frame0), TypeOrigin::frame(frame1)); + } + static ErrorContext stack_overflow(u2 bci, StackMapFrame* frame) { + return ErrorContext(bci, STACK_OVERFLOW, TypeOrigin::frame(frame)); + } + static ErrorContext stack_underflow(u2 bci, StackMapFrame* frame) { + return ErrorContext(bci, STACK_UNDERFLOW, TypeOrigin::frame(frame)); + } + static ErrorContext missing_stackmap(u2 bci) { + return ErrorContext(bci, MISSING_STACKMAP); + } + static ErrorContext bad_stackmap(int index, StackMapFrame* frame) { + return ErrorContext(0, BAD_STACKMAP, TypeOrigin::frame(frame)); + } + + bool is_valid() const { return _fault != NO_FAULT; } + int bci() const { return _bci; } + + void reset_frames() { + _type.reset_frame(); + _expected.reset_frame(); + } + + void details(outputStream* ss, methodOop method) const; + +#ifdef ASSERT + void print_on(outputStream* str) const { + str->print("error_context(%d, %d,", _bci, _fault); + _type.print_on(str); + str->print(","); + _expected.print_on(str); + str->print(")"); + } +#endif + + private: + void location_details(outputStream* ss, methodOop method) const; + void reason_details(outputStream* ss) const; + void frame_details(outputStream* ss) const; + void bytecode_details(outputStream* ss, methodOop method) const; + void handler_details(outputStream* ss, methodOop method) const; + void stackmap_details(outputStream* ss, methodOop method) const; +}; + // A new instance of this class is created for each class being verified class ClassVerifier : public StackObj { private: Thread* _thread; + GrowableArray* _symbols; // keep a list of symbols created + Symbol* _exception_type; char* _message; - size_t _message_buffer_len; - GrowableArray* _symbols; // keep a list of symbols created + + ErrorContext _error_context; // contains information about an error void verify_method(methodHandle method, TRAPS); char* generate_code_data(methodHandle m, u4 code_length, TRAPS); - void verify_exception_handler_table(u4 code_length, char* code_data, int& min, int& max, TRAPS); + void verify_exception_handler_table(u4 code_length, char* code_data, + int& min, int& max, TRAPS); void verify_local_variable_table(u4 code_length, char* code_data, TRAPS); VerificationType cp_ref_index_to_type( @@ -112,10 +272,10 @@ instanceKlassHandle this_class, klassOop target_class, Symbol* field_name, Symbol* field_sig, bool is_method); - void verify_cp_index(constantPoolHandle cp, int index, TRAPS); - void verify_cp_type( - int index, constantPoolHandle cp, unsigned int types, TRAPS); - void verify_cp_class_type(int index, constantPoolHandle cp, TRAPS); + void verify_cp_index(u2 bci, constantPoolHandle cp, int index, TRAPS); + void verify_cp_type(u2 bci, int index, constantPoolHandle cp, + unsigned int types, TRAPS); + void verify_cp_class_type(u2 bci, int index, constantPoolHandle cp, TRAPS); u2 verify_stackmap_table( u2 stackmap_index, u2 bci, StackMapFrame* current_frame, @@ -138,7 +298,7 @@ constantPoolHandle cp, TRAPS); void verify_invoke_init( - RawBytecodeStream* bcs, VerificationType ref_class_type, + RawBytecodeStream* bcs, u2 ref_index, VerificationType ref_class_type, StackMapFrame* current_frame, u4 code_length, bool in_try_block, bool* this_uninit, constantPoolHandle cp, StackMapTable* stackmap_table, TRAPS); @@ -159,10 +319,11 @@ constantPoolHandle cp, StackMapTable* stackmap_table, TRAPS); VerificationType get_newarray_type(u2 index, u2 bci, TRAPS); - void verify_anewarray( - u2 index, constantPoolHandle cp, StackMapFrame* current_frame, TRAPS); + void verify_anewarray(u2 bci, u2 index, constantPoolHandle cp, + StackMapFrame* current_frame, TRAPS); void verify_return_value( - VerificationType return_type, VerificationType type, u2 offset, TRAPS); + VerificationType return_type, VerificationType type, u2 offset, + StackMapFrame* current_frame, TRAPS); void verify_iload (u2 index, StackMapFrame* current_frame, TRAPS); void verify_lload (u2 index, StackMapFrame* current_frame, TRAPS); @@ -201,7 +362,7 @@ }; // constructor - ClassVerifier(instanceKlassHandle klass, char* msg, size_t msg_len, TRAPS); + ClassVerifier(instanceKlassHandle klass, TRAPS); // destructor ~ClassVerifier(); @@ -219,13 +380,17 @@ // Return status modes Symbol* result() const { return _exception_type; } bool has_error() const { return result() != NULL; } + char* exception_message() { + stringStream ss; + ss.print(_message); + _error_context.details(&ss, _method()); + return ss.as_string(); + } // Called when verify or class format errors are encountered. // May throw an exception based upon the mode. - void verify_error(u2 offset, const char* fmt, ...); - void verify_error(const char* fmt, ...); + void verify_error(ErrorContext ctx, const char* fmt, ...); void class_format_error(const char* fmt, ...); - void format_error_message(const char* fmt, int offset, va_list args); klassOop load_class(Symbol* name, TRAPS); @@ -240,10 +405,11 @@ // their reference counts need to be decrememented when the verifier object // goes out of scope. Since these symbols escape the scope in which they're // created, we can't use a TempNewSymbol. - Symbol* create_temporary_symbol(const Symbol* s, int begin, int end, TRAPS); + Symbol* create_temporary_symbol( + const Symbol* s, int begin, int end, TRAPS); Symbol* create_temporary_symbol(const char *s, int length, TRAPS); - static bool _verify_verbose; // for debugging + TypeOrigin ref_ctx(const char* str, TRAPS); }; diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/runtime/globals.hpp Wed Nov 16 05:45:24 2016 +0000 @@ -833,6 +833,9 @@ product(bool, PrintGCApplicationStoppedTime, false, \ "Print the time the application has been stopped") \ \ + diagnostic(bool, VerboseVerification, false, \ + "Display detailed verification details") \ + \ notproduct(uintx, ErrorHandlerTest, 0, \ "If > 0, provokes an error after VM initialization; the value" \ "determines which error to provoke. See test_error_handler()" \ diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/runtime/relocator.cpp --- a/src/share/vm/runtime/relocator.cpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/runtime/relocator.cpp Wed Nov 16 05:45:24 2016 +0000 @@ -465,13 +465,12 @@ void Relocator::adjust_stack_map_table(int bci, int delta) { if (method()->has_stackmap_table()) { typeArrayOop data = method()->stackmap_data(); - // The data in the array is a classfile representation of the stackmap - // table attribute, less the initial u2 tag and u4 attribute_length fields. - stack_map_table_attribute* attr = stack_map_table_attribute::at( - (address)data->byte_at_addr(0) - (sizeof(u2) + sizeof(u4))); + // The data in the array is a classfile representation of the stackmap table + stack_map_table* sm_table = + stack_map_table::at((address)data->byte_at_addr(0)); - int count = attr->number_of_entries(); - stack_map_frame* frame = attr->entries(); + int count = sm_table->number_of_entries(); + stack_map_frame* frame = sm_table->entries(); int bci_iter = -1; bool offset_adjusted = false; // only need to adjust one offset @@ -486,7 +485,7 @@ frame->set_offset_delta(new_offset_delta); } else { assert(frame->is_same_frame() || - frame->is_same_frame_1_stack_item_frame(), + frame->is_same_locals_1_stack_item_frame(), "Frame must be one of the compressed forms"); // The new delta exceeds the capacity of the 'same_frame' or // 'same_frame_1_stack_item_frame' frame types. We need to @@ -513,7 +512,7 @@ if (frame->is_same_frame()) { same_frame_extended::create_at(frame_addr, new_offset_delta); } else { - same_frame_1_stack_item_extended::create_at( + same_locals_1_stack_item_extended::create_at( frame_addr, new_offset_delta, NULL); // the verification_info_type should already be at the right spot } diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/utilities/ostream.cpp --- a/src/share/vm/utilities/ostream.cpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/utilities/ostream.cpp Wed Nov 16 05:45:24 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -237,8 +237,9 @@ return; } -void outputStream::indent() { +outputStream& outputStream::indent() { while (_position < _indentation) sp(); + return *this; } void outputStream::print_jlong(jlong value) { @@ -251,6 +252,47 @@ print(os::julong_format_specifier(), value); } +/** + * This prints out hex data in a 'windbg' or 'xxd' form, where each line is: + * : 8 * + * example: + * 0000000: 7f44 4f46 0102 0102 0000 0000 0000 0000 .DOF............ + * 0000010: 0000 0000 0000 0040 0000 0020 0000 0005 .......@... .... + * 0000020: 0000 0000 0000 0040 0000 0000 0000 015d .......@.......] + * ... + * + * indent is applied to each line. Ends with a CR. + */ +void outputStream::print_data(void* data, size_t len, bool with_ascii) { + size_t limit = (len + 16) / 16 * 16; + for (size_t i = 0; i < limit; ++i) { + if (i % 16 == 0) { + indent().print("%07x:", i); + } + if (i % 2 == 0) { + print(" "); + } + if (i < len) { + print("%02x", ((unsigned char*)data)[i]); + } else { + print(" "); + } + if ((i + 1) % 16 == 0) { + if (with_ascii) { + print(" "); + for (size_t j = 0; j < 16; ++j) { + size_t idx = i + j - 15; + if (idx < len) { + char c = ((char*)data)[idx]; + print("%c", c >= 32 && c <= 126 ? c : '.'); + } + } + } + print_cr(""); + } + } +} + stringStream::stringStream(size_t initial_size) : outputStream() { buffer_length = initial_size; buffer = NEW_RESOURCE_ARRAY(char, buffer_length); diff -r 83a11fe60ae1 -r 6088554a147c src/share/vm/utilities/ostream.hpp --- a/src/share/vm/utilities/ostream.hpp Thu Jul 05 20:54:45 2012 -0400 +++ b/src/share/vm/utilities/ostream.hpp Wed Nov 16 05:45:24 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -59,9 +59,11 @@ outputStream(int width, bool has_time_stamps); // indentation - void indent(); + outputStream& indent(); void inc() { _indentation++; }; void dec() { _indentation--; }; + void inc(int n) { _indentation += n; }; + void dec(int n) { _indentation -= n; }; int indentation() const { return _indentation; } void set_indentation(int i) { _indentation = i; } void fill_to(int col); @@ -84,6 +86,7 @@ void print_raw(const char* str, int len) { write(str, len); } void print_raw_cr(const char* str) { write(str, strlen(str)); cr(); } void print_raw_cr(const char* str, int len){ write(str, len); cr(); } + void print_data(void* data, size_t len, bool with_ascii); void put(char ch); void sp(int count = 1); void cr(); @@ -122,6 +125,19 @@ extern outputStream* tty; // tty output extern outputStream* gclog_or_tty; // stream for gc log if -Xloggc:, or tty +class streamIndentor : public StackObj { + private: + outputStream* _str; + int _amount; + + public: + streamIndentor(outputStream* str, int amt = 2) : _str(str), _amount(amt) { + _str->inc(_amount); + } + ~streamIndentor() { _str->dec(_amount); } +}; + + // advisory locking for the shared tty stream: class ttyLocker: StackObj { friend class ttyUnlocker; diff -r 83a11fe60ae1 -r 6088554a147c test/runtime/7116786/Test7116786.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/7116786/Test7116786.java Wed Nov 16 05:45:24 2016 +0000 @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2012, 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. + * + * 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. + * + */ + +/* + * @test Test7116786 + * @summary verify that VerifyError messages are as expected + * @library testcases.jar + * @run main/othervm -Xverify:all Test7116786 + */ + + +/** + * This class contains information regarding when a VerifyError is thrown + * in the verifier. Most of the data is informational-only, and can be + * used to track down where and why VerifyErrors are thrown. As such it + * is possible the information may go out-of-date. + * + * The only fields used for the purpose of testing is the 'caseName' and + * the 'message'. The 'caseName' corresponds to a classfile which exhibits + * the VerifyError, and the 'message' is a regular expression which we expect + * to match the verify error message. If the 'message' doesn't match what + * we expect, it warrents investigation to see if we are still triggering + * the VerifyError that we expect. It could simply just be that the message + * changed, which is fine. + * + * Some cases are not testable, either because the code is probably unreachable + * or the test classfile would be too onerous to create. These cases are + * marked with 'testable' == false, and the test runner will skip them. + */ +class Case { + private String caseName; // Name of the case + private String file; // Source file where VerifyError is thrown + private String location; // enclosing function or switch case + private String description; // What causes this VerifyError + private String message; // The VerifyError message used. + + private boolean testable; // Whether this case is testable or not. + + public Case(String caseName, String file, boolean testable, + String location, String description, String message) { + this.caseName = caseName; + this.file = file; + this.testable = testable; + this.location = location; + this.description = description; + this.message = message; + } + + String getCaseName() { return this.caseName; } + String getFile() { return this.file; } + String getLocation() { return this.location; } + String getDescription() { return this.description; } + String getMessage() { return this.message; } + + boolean isTestable() { return this.testable; } +} + +/** + * These are the locations in the source code where VerifyErrors are thrown + * as of today, 2012/07/18. These may change as the verification code is + * modified, which is ok. This test is trying to provide coverage for all + * VerifyErrors (just to make sure there are no crashes) and it's probably + * not necessary to update it every time the VM changes. + */ +class VerifyErrorCases { + public static final Case[] cases = { + + new Case("case00", "stackMapFrame.cpp", true, "pop_stack_ex", + "stack underflow", + "Operand stack underflow"), + + new Case("case01", "stackMapFrame.cpp", true, "pop_stack_ex", + "stack pop not assignable to expected", + "Bad type on operand stack"), + + new Case("case02", "stackMapFrame.cpp", true, "get_local", + "local index out-of-bounds", + "Local variable table overflow"), + + new Case("case03", "stackMapFrame.cpp", true, "get_local", + "local not assignable to expected", + "Bad local variable type"), + + new Case("case04", "stackMapFrame.cpp", true, "get_local_2", + "local index out-of-bounds [type2]", + "get long/double overflows locals"), + + new Case("case05", "stackMapFrame.cpp", true, "get_local_2", + "local not assignabled to expected [type2]", + "Bad local variable type"), + + /* Unreachable: Can't split long/double on stack */ + new Case("case06", "stackMapFrame.cpp", false, "get_local_2", + "local second-word not assignabled to expected", + "Bad local variable type"), + + new Case("case07", "stackMapFrame.cpp", true, "set_local", + "local index out-of-bounds", + "Local variable table overflow"), + + new Case("case08", "stackMapFrame.cpp", true, "set_local_2", + "local index out-of-bounds [type2]", + "Local variable table overflow"), + + new Case("case09", "stackMapFrame.hpp", true, "push_stack", + "stack overflow", + "Operand stack overflow"), + + new Case("case10", "stackMapFrame.hpp", true, "push_stack_2", + "stack overflow [type2]", + "Operand stack overflow"), + + new Case("case11", "stackMapFrame.hpp", true, "pop_stack", + "stack underflow", + "Operand stack underflow"), + + new Case("case12", "stackMapTable.cpp", true, "StackMapTable ctor", + "stackmap offset beyond code size", + "StackMapTable error: bad offset"), + + new Case("case13", "stackMapTable.cpp", true, "match_stackmap", + "no stackmap frame at expected location", + "Expecting a stackmap frame at branch target "), + + new Case("case14", "stackMapTable.cpp", true, "check_jump_target", + "no stackmap frame at jump location or bad jump", + "Inconsistent stackmap frames at branch target "), + + new Case("case15", "stackMapTable.cpp", true, "check_new_object", + "backward jump with uninit", + "Uninitialized object exists on backward branch "), + + /* Unreachable: wide instructions verified during bytecode analysis */ + new Case("case16", "verifier.cpp", false, "loop header", + "bad op in wide instruction", + "Bad wide instruction"), + + new Case("case17", "verifier.cpp", true, "case iaload", + "TOS not X array", + "Bad type on operand stack in iaload"), + + new Case("case18", "verifier.cpp", true, "case baload", + "TOS not X array", + "Bad type on operand stack in baload"), + + new Case("case19", "verifier.cpp", true, "case caload", + "TOS not X array", + "Bad type on operand stack in caload"), + + new Case("case20", "verifier.cpp", true, "case saload", + "TOS not X array", + "Bad type on operand stack in saload"), + + new Case("case21", "verifier.cpp", true, "case laload", + "TOS not X array", + "Bad type on operand stack in laload"), + + new Case("case22", "verifier.cpp", true, "case faload", + "TOS not X array", + "Bad type on operand stack in faload"), + + new Case("case23", "verifier.cpp", true, "case daload", + "TOS not X array", + "Bad type on operand stack in daload"), + + new Case("case24", "verifier.cpp", true, "case aaload", + "TOS not X array", + "Bad type on operand stack in aaload"), + + new Case("case25", "verifier.cpp", true, "case iastore", + "TOS not int array", + "Bad type on operand stack in iastore"), + + new Case("case26", "verifier.cpp", true, "case bastore", + "TOS not byte array", + "Bad type on operand stack in bastore"), + + new Case("case27", "verifier.cpp", true, "case castore", + "TOS not char array", + "Bad type on operand stack in castore"), + + new Case("case28", "verifier.cpp", true, "case sastore", + "TOS not short array", + "Bad type on operand stack in sastore"), + + new Case("case29", "verifier.cpp", true, "case lastore", + "TOS not long array", + "Bad type on operand stack in lastore"), + + new Case("case30", "verifier.cpp", true, "case fastore", + "TOS not float array", + "Bad type on operand stack in fastore"), + + new Case("case31", "verifier.cpp", true, "case dastore", + "TOS not double array", + "Bad type on operand stack in dastore"), + + new Case("case32", "verifier.cpp", true, "case aastore", + "TOS not object array", + "Bad type on operand stack in aastore"), + + /* Unreachable: In order to hit this case, we would need a + * category2_1st at TOS which is not possible. */ + new Case("case33", "verifier.cpp", false, "case pop2", + "TOS is category2_1st (would split)", + "Bad type on operand stack in pop2"), + + /* Unreachable: In order to hit this case, we would need a + * category2_1st at stack depth 2 with category_1 on TOS which is not + * possible. */ + new Case("case34", "verifier.cpp", false, "case dup_x2", + "TOS-1 is category2_1st (would split)", + "Bad type on operand stack in dup_x2"), + + /* Unreachable: In order to hit this case, we would need a + * category2_1st at TOS which is not possible. */ + new Case("case35", "verifier.cpp", false, "case dup2", + "TOS-1 is category2_1st (would split)", + "Bad type on operand stack in dup2"), + + /* Unreachable: In order to hit this case, we would need a + * category2_1st at TOS which is not possible. */ + new Case("case36", "verifier.cpp", false, "case dup2_x1", + "TOS-1 is category2_1st (would split)", + "Bad type on operand stack in dup2_x1"), + + /* Unreachable: In order to hit this case, we would need a + * category2_1st at TOS which is not possible. */ + new Case("case37", "verifier.cpp", false, "case dup2_x2", + "TOS-1 is category2_1st (would split)", + "Bad type on operand stack in dup2_x2"), + + /* Unreachable: In order to hit this case, we would need a + * category2_1st at stack depth 3 with either 2 category_1 or 1 + * category_2 on TOS, which is not possible. */ + new Case("case38", "verifier.cpp", false, "case dup2_x2", + "TOS-3 is category2_1st (would split)", + "Bad type on operand stack in dup2_x2"), + + new Case("case39", "verifier.cpp", true, "case return", + "return type of method is not void", + "Method expects a return value"), + + new Case("case40", "verifier.cpp", true, "case return", + "return with uninitialized this ", + "Constructor must call super() or this() before return"), + + new Case("case41", "verifier.cpp", true, "case new", + "cp index not a class type", + "Illegal new instruction"), + + new Case("case42", "verifier.cpp", true, "case arraylength", + "TOS is not an array", + "Bad type on operand stack in arraylength"), + + new Case("case43", "verifier.cpp", true, "case multianewarray", + "CP index does not refer to array type", + "Illegal constant pool index in multianewarray instruction"), + + new Case("case44", "verifier.cpp", true, "case multianewarray", + "Bad dimension (<1) or does not match CP signature", + "Illegal dimension in multianewarray instruction: "), + + new Case("case45", "verifier.cpp", true, "case default", + "Unrecognized bytecode", + "Bad instruction: "), + + new Case("case46", "verifier.cpp", true, "loop end", + "control flow falls off method", + "Control flow falls through code end"), + + new Case("case47", "verifier.cpp", true, "generate_code_data", + "illegal bytecode via RawBytecodeStream (breakpoint)", + "Bad instruction"), + + new Case("case48", "verifier.cpp", true, "generate_code_data", + "illegal bytecode via RawBytecodeStream (other illegal)", + "Bad instruction"), + + new Case("case49", "verifier.cpp", true, + "verify_exception_handler_table", + "catch_type is not throwable", + "Catch type is not a subclass of Throwable in " + + "exception handler "), + + new Case("case50", "verifier.cpp", true, "verify_stackmap_table", + "missing a stack map frame @ target location (mid table)", + "Expecting a stack map frame"), + + new Case("case51", "verifier.cpp", true, "verify_stackmap_table", + "stack map does not match?", + "Instruction type does not match stack map"), + + new Case("case52", "verifier.cpp", true, "verify_stackmap_table", + "missing a stack map frame @ target location (end of table)", + "Expecting a stack map frame"), + + new Case("case53", "verifier.cpp", true, + "verify_exception_handler_targets", + "stackmap mismatch at exception handler", + "Stack map does not match the one at exception handler "), + + new Case("case54", "verifier.cpp", true, "verify_cp_index", + "constant pool index is out-of-bounds", + "Illegal constant pool index "), + + new Case("case55", "verifier.cpp", true, "verify_cp_type", + "constant pool entry is not expected type", + "Illegal type at constant pool entry "), + + new Case("case56", "verifier.cpp", true, "verify_cp_class_type", + "constant pool entry is not an object type", + "Illegal type at constant pool entry "), + + /* Unreachable: verify_cp_type gates this case */ + new Case("case57", "verifier.cpp", false, "verify_ldc", + "invalid constant pool index in ldc", + "Invalid index in ldc"), + + new Case("case58", "verifier.cpp", true, "verify_switch", + "bad switch padding", + "Nonzero padding byte in lookswitch or tableswitch"), + + new Case("case59", "verifier.cpp", true, "verify_switch", + "tableswitch low is greater than high", + "low must be less than or equal to high in tableswitch"), + + /* Unreachable on 64-bit? Only way to get here is to overflow + * the 'keys' variable which can't happen on 64-bit since we're dealing + * with 32-bit values. Perhaps reachable on 32-bit but the + * triggering class would be quite large */ + new Case("case60", "verifier.cpp", false, "verify_switch", + "high - low + 1 < 0 (overflow?)", + "too many keys in tableswitch"), + + /* Would have to create a 16G classfile to trip this. Possible but + * not reasonable to do in a test. */ + new Case("case61", "verifier.cpp", false, "verify_switch", + "lookupswitch keys < 0", + "number of keys in lookupswitch less than 0"), + + new Case("case62", "verifier.cpp", true, "verify_switch", + "lookupswitch keys out-of-order", + "Bad lookupswitch instruction"), + + /* Unreachable: Class file parser verifies Fieldref contents */ + new Case("case63", "verifier.cpp", false, "verify_field_instructions", + "referenced class is not an CP object", + "Expecting reference to class in class "), + + new Case("case64", "verifier.cpp", true, "verify_field_instructions", + "TOS not assignable to field type in putfield", + "Bad type on operand stack in putfield"), + + new Case("case65", "verifier.cpp", true, "verify_field_instructions", + "TOS not assignable to class when accessing protected field", + "Bad access to protected data in getfield"), + + new Case("case66", "verifier.cpp", true, "verify_invoke_init", + "Uninit_this is not of the current type or it's supertype", + "Bad method call"), + + /* Unreachable: Stack map parsing ensures valid type and new + * instructions have a valid BCI. */ + new Case("case67", "verifier.cpp", false, "verify_invoke_init", + "Uninit type with bad new instruction index", + "Expecting new instruction"), + + new Case("case68", "verifier.cpp", true, "verify_invoke_init", + "calling other class's method", + "Call to wrong method"), + + new Case("case69", "verifier.cpp", true, "verify_invoke_init", + "Calling protected and type unassignable from current", + "Bad access to protected method"), + + new Case("case70", "verifier.cpp", true, "verify_invoke_init", + "TOS is not an uninitialized (or Uninit_this) type", + "Bad operand type when invoking "), + + new Case("case71", "verifier.cpp", true, "verify_invoke_instructions", + "Arg count in instruction doesn't match signature", + "Inconsistent args count operand in invokeinterface"), + + new Case("case72", "verifier.cpp", true, "verify_invoke_instructions", + "Non-zero pad in invokeinterface", + "Fourth operand byte of invokeinterface must be zero"), + + new Case("case73", "verifier.cpp", true, "verify_invoke_instructions", + "Non-zero pad in invokedynamic", + "Third and fourth operand bytes of " + + "invokedynamic must be zero"), + + new Case("case74", "verifier.cpp", true, "verify_invoke_instructions", + "Non-invokespecial trying to invoke a '<' method", + "Illegal call to internal method"), + + new Case("case75", "verifier.cpp", true, "verify_invoke_instructions", + "invokespecial and current unassignable from referenced type", + "Bad invokespecial instruction: current class isn't " + + "assignable to reference class."), + + new Case("case76", "verifier.cpp", true, "verify_invoke_instructions", + "TOS not assignable to current when calling protected method", + "Bad access to protected data in invokevirtual"), + + /* Unreachable: class file parser enforces void signature */ + new Case("case77", "verifier.cpp", false, "verify_invoke_instructions", + " method is not void return", + "Return type must be void in method"), + + new Case("case78", "verifier.cpp", true, "get_newarray_type", + "newarray type invalid", + "Illegal newarray instruction"), + + new Case("case79", "verifier.cpp", true, "verify_return_value", + "void return from method which has a return value", + "Method expects a return value"), + + new Case("case80", "verifier.cpp", true, "verify_return_value", + "TOS type does not match signature", + "Bad return type"), + + new Case("case81", "verifier.cpp", true, "verify_stackmap_table", + "stack map does not match (flags)", + "Instruction type does not match stack map") + }; +} + +public class Test7116786 { + public static void main(String argv[]) throws Exception { + for (Case c : VerifyErrorCases.cases) { + System.out.println("******** " + c.getCaseName() + " ********"); + if (c.isTestable()) { + try { + ClassLoader cl = Test7116786.class.getClassLoader(); + Class cls = Class.forName(c.getCaseName(), true, cl); + throw new RuntimeException( + "++ FAIL: No verify error encountered"); + } catch (VerifyError ve) { + String message = c.getMessage(); + String veMessage = ve.getMessage(); + System.out.print(veMessage); + if (!veMessage.startsWith(message)) { + // We're not seeing the message we expect. Could be + // that we've gotten the wrong VerifyError case, or + // maybe the message changed. + System.out.println("++ FAIL? " + + "Message does not match what was expected: " + + message); + continue; + } + if (!veMessage.contains("Exception Details:") && + !veMessage.contains("Reason:")) { + System.out.println("++ FAIL: No details found"); + throw new RuntimeException("FAIL: No details found"); + } + System.out.println("++ PASS"); + } + } else { + System.out.println("++ SKIPPED"); + } + } + } +} diff -r 83a11fe60ae1 -r 6088554a147c test/runtime/7116786/testcases.jar Binary file test/runtime/7116786/testcases.jar has changed