Mercurial > hg > openjdk > jdk6 > hotspot
changeset 4085:c9b5ffda6970
8050485: super() in a try block in a ctor causes VerifyError
Summary: Parse catch clause paths to ensure they end in throws
Reviewed-by: dlong, acorn, kamg, ctornqvi, lfoltan
author | hseigel |
---|---|
date | Mon, 19 Jan 2015 06:43:45 +0000 |
parents | 8af48ff040c9 |
children | 0dd28ac12d7d |
files | src/share/vm/classfile/verifier.cpp src/share/vm/classfile/verifier.hpp |
diffstat | 2 files changed, 207 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/src/share/vm/classfile/verifier.cpp Mon Jan 19 05:28:21 2015 +0000 +++ b/src/share/vm/classfile/verifier.cpp Mon Jan 19 06:43:45 2015 +0000 @@ -1497,7 +1497,7 @@ new_frame->push_stack(throwable, CHECK_VERIFY(this)); } bool match = stackmap_table->match_stackmap( - new_frame, handler_pc, true, false, true, CHECK_VERIFY(this)); + 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", @@ -1877,6 +1877,185 @@ } } +// Look at the method's handlers. If the bci is in the handler's try block +// then check if the handler_pc is already on the stack. If not, push it. +void ClassVerifier::push_handlers(typeArrayHandle exhandlers, + GrowableArray<u4>* handler_stack, + u4 bci) { + int exlength = exhandlers->length(); + for(int x = 0; x < exlength; x++) { + u2 start_pc = exhandlers->int_at(x++); + u2 end_pc = exhandlers->int_at(x++); + u2 handler_pc = exhandlers->int_at(x++); + + if (bci >= start_pc && bci < end_pc) { + handler_stack->append_if_missing(handler_pc); + } + } +} + +// Return TRUE if all code paths starting with start_bc_offset end in +// bytecode athrow or loop. +bool ClassVerifier::ends_in_athrow(u4 start_bc_offset, TRAPS) { + ResourceMark rm; + // Create bytecode stream. + RawBytecodeStream bcs(method()); + u4 code_length = method()->code_size(); + bcs.set_start(start_bc_offset); + u4 target; + // Create stack for storing bytecode start offsets for if* and *switch. + GrowableArray<u4>* bci_stack = new GrowableArray<u4>(30); + // Create stack for handlers for try blocks containing this handler. + GrowableArray<u4>* handler_stack = new GrowableArray<u4>(30); + // Create list of visited branch opcodes (goto* and if*). + GrowableArray<u4>* visited_branches = new GrowableArray<u4>(30); + typeArrayHandle exhandlers(THREAD, _method->exception_table()); + + while (true) { + if (bcs.is_last_bytecode()) { + // if no more starting offsets to parse or if at the end of the + // method then return false. + if ((bci_stack->is_empty()) || ((u4)bcs.end_bci() == code_length)) + return false; + // Pop a bytecode starting offset and scan from there. + bcs.set_start(bci_stack->pop()); + } + Bytecodes::Code opcode = bcs.raw_next(); + u4 bci = bcs.bci(); + + // If the bytecode is in a TRY block, push its handlers so they + // will get parsed. + push_handlers(exhandlers, handler_stack, bci); + + switch (opcode) { + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + target = bcs.dest(); + if (visited_branches->contains(bci)) { + if (bci_stack->is_empty()) return true; + // Pop a bytecode starting offset and scan from there. + bcs.set_start(bci_stack->pop()); + } else { + if (target > bci) { // forward branch + if (target >= code_length) return false; + // Push the branch target onto the stack. + bci_stack->push(target); + // then, scan bytecodes starting with next. + bcs.set_start(bcs.next_bci()); + } else { // backward branch + // Push bytecode offset following backward branch onto the stack. + bci_stack->push(bcs.next_bci()); + // Check bytecodes starting with branch target. + bcs.set_start(target); + } + // Record target so we don't branch here again. + visited_branches->append(bci); + } + break; + + case Bytecodes::_goto: + case Bytecodes::_goto_w: + target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w()); + if (visited_branches->contains(bci)) { + if (bci_stack->is_empty()) return true; + // Been here before, pop new starting offset from stack. + bcs.set_start(bci_stack->pop()); + } else { + if (target >= code_length) return false; + // Continue scanning from the target onward. + bcs.set_start(target); + // Record target so we don't branch here again. + visited_branches->append(bci); + } + break; + + // Check that all switch alternatives end in 'athrow' bytecodes. Since it + // is difficult to determine where each switch alternative ends, parse + // each switch alternative until either hit a 'return', 'athrow', or reach + // the end of the method's bytecodes. This is gross but should be okay + // because: + // 1. tableswitch and lookupswitch byte codes in handlers for ctor explicit + // constructor invocations should be rare. + // 2. if each switch alternative ends in an athrow then the parsing should be + // short. If there is no athrow then it is bogus code, anyway. + case Bytecodes::_lookupswitch: + case Bytecodes::_tableswitch: + { + address aligned_bcp = (address) round_to((intptr_t)(bcs.bcp() + 1), jintSize); + u4 default_offset = Bytes::get_Java_u4(aligned_bcp) + bci; + int keys, delta; + if (opcode == Bytecodes::_tableswitch) { + jint low = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize); + jint high = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize); + // This is invalid, but let the regular bytecode verifier + // report this because the user will get a better error message. + if (low > high) return true; + keys = high - low + 1; + delta = 1; + } else { + keys = (int)Bytes::get_Java_u4(aligned_bcp + jintSize); + delta = 2; + } + // Invalid, let the regular bytecode verifier deal with it. + if (keys < 0) return true; + + // Push the offset of the next bytecode onto the stack. + bci_stack->push(bcs.next_bci()); + + // Push the switch alternatives onto the stack. + for (int i = 0; i < keys; i++) { + u4 target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); + if (target > code_length) return false; + bci_stack->push(target); + } + + // Start bytecode parsing for the switch at the default alternative. + if (default_offset > code_length) return false; + bcs.set_start(default_offset); + break; + } + + case Bytecodes::_return: + return false; + + case Bytecodes::_athrow: + { + if (bci_stack->is_empty()) { + if (handler_stack->is_empty()) { + return true; + } else { + // Parse the catch handlers for try blocks containing athrow. + bcs.set_start(handler_stack->pop()); + } + } else { + // Pop a bytecode offset and starting scanning from there. + bcs.set_start(bci_stack->pop()); + } + } + break; + + default: + ; + } // end switch + } // end while loop + + return false; +} + void ClassVerifier::verify_invoke_init( RawBytecodeStream* bcs, VerificationType ref_class_type, StackMapFrame* current_frame, u4 code_length, bool *this_uninit, @@ -1893,25 +2072,30 @@ return; } - // Make sure that this call is not done from within a TRY block because - // that can result in returning an incomplete object. Simply checking - // (bci >= start_pc) also ensures that this call is not done after a TRY - // block. That is also illegal because this call must be the first Java - // statement in the constructor. + // Check if this call is done from inside of a TRY block. If so, make + // sure that all catch clause paths end in a throw. Otherwise, this + // can result in returning an incomplete object. typeArrayHandle exhandlers (THREAD, _method->exception_table()); - for(int i = 0; i < exhandlers->length(); i += 4) { + for(int i = 0; i < exhandlers->length(); i++) { /* - * Skip reading end_pc, handler_pc and catch_type_index. + * Skip reading catch_type_index. * Looks like this if you need all of them: * u2 start_pc = exhandlers->int_at(i++); * u2 end_pc = exhandlers->int_at(i++); * u2 handler_pc = exhandlers->int_at(i++); * int catch_type_index = exhandlers->int_at(i++); */ - u2 start_pc = exhandlers->int_at(i); - if (bci >= start_pc) { - verify_error(bci, "Bad <init> method call from after the start of a try block"); - return; + u2 start_pc = exhandlers->int_at(i++); + u2 end_pc = exhandlers->int_at(i++); + u2 handler_pc = exhandlers->int_at(i++); + + if (bci >= start_pc && bci < end_pc) { + bool athrow_check = ends_in_athrow(handler_pc, CHECK_VERIFY(this)); + if (!athrow_check) { + verify_error( + "Bad <init> method call from after the start of a try block"); + return; + } } }
--- a/src/share/vm/classfile/verifier.hpp Mon Jan 19 05:28:21 2015 +0000 +++ b/src/share/vm/classfile/verifier.hpp Mon Jan 19 06:43:45 2015 +0000 @@ -30,6 +30,7 @@ #include "oops/klass.hpp" #include "oops/methodOop.hpp" #include "runtime/handles.hpp" +#include "utilities/growableArray.hpp" #include "utilities/exceptions.hpp" // The verifier class @@ -141,6 +142,16 @@ StackMapFrame* current_frame, u4 code_length, bool* this_uninit, constantPoolHandle cp, TRAPS); + // Used by ends_in_athrow() to push all handlers that contain bci onto + // the handler_stack, if the handler is not already on the stack. + void push_handlers(typeArrayHandle exhandlers, + GrowableArray<u4>* handler_stack, + u4 bci); + + // Returns true if all paths starting with start_bc_offset end in athrow + // bytecode or loop. + bool ends_in_athrow(u4 start_bc_offset, TRAPS); + void verify_invoke_instructions( RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame, bool* this_uninit, VerificationType return_type,