--- old/src/share/vm/classfile/verifier.cpp 2014-07-29 09:17:21.891741000 -0400 +++ new/src/share/vm/classfile/verifier.cpp 2014-07-29 09:17:19.167517000 -0400 @@ -2219,6 +2219,148 @@ } } +// Return TRUE if all code paths in the bytecode interval end in athrow. +bool ClassVerifier::ends_in_athrow(u4 start_bc_offset, u4 end_bc_offset) { + // Create bytecode stream. + RawBytecodeStream bcs(method()); + bcs.set_interval(start_bc_offset, end_bc_offset); + u4 code_length = method()->code_size(); + u4 target; + // Create stack for storing bytecode intervals for if*, goto*, and *switch. + GrowableArray* bci_stack = new GrowableArray(50); + + while (true) { + if (bcs.is_last_bytecode()) { + if (bci_stack->is_empty()) { + return false; + } else { + // Pop a bytecode interval and scan that interval. + u4 end_offset = pop_bci_end_offset(bci_stack); + u4 start_offset = pop_bci_start_offset(bci_stack); + assert(end_offset >= start_offset, "Messed up bytecode offsets"); + bcs.set_interval(start_offset, end_offset); + } + } + Bytecodes::Code opcode = bcs.raw_next(); + u4 bci = bcs.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 (target > bci) { // forward branch + if (target >= code_length) return false; + // Push the branch target interval onto the stack. + push_bci_offsets(bci_stack, target, bcs.end_bci()); + // then, scan bytecodes up to the target. + bcs.set_interval(bcs.next_bci(), target); + + } else { // backward branch + // Push the interval following the backward branch onto the stack. + push_bci_offsets(bci_stack, bcs.next_bci(), bcs.end_bci()); + // Check bytecodes between the branch target and the current offset. + bcs.set_interval(target, bci); + } + break; + + case Bytecodes::_goto: + case Bytecodes::_goto_w: + target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w()); + if (target > bci) { // forward branch + if (target >= code_length) return false; + bcs.set_interval(target, code_length); + + } else { // backward branch + // Push the interval following the backward branch onto the stack. + push_bci_offsets(bci_stack, bcs.next_bci(), bcs.end_bci()); + // Check bytecodes between the branch target and the current offset. + bcs.set_interval(target, 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 current state onto the stack. + push_bci_offsets(bci_stack, bcs.next_bci(), bcs.end_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; + push_bci_offsets(bci_stack, target, code_length); + } + + // Start bytecode parsing for the switch at the default alternative. + if (default_offset > code_length) return false; + bcs.set_interval(default_offset, code_length); + break; + } + + case Bytecodes::_return: + return false; + + case Bytecodes::_athrow: + if (bci_stack->is_empty()) { + return true; + } else { + // Pop a bytecode interval and scan that interval. + u4 end_offset = pop_bci_end_offset(bci_stack); + u4 start_offset = pop_bci_start_offset(bci_stack); + assert(end_offset >= start_offset, "Mixed up bytecode offsets"); + bcs.set_interval(start_offset, end_offset); + break; + } + + default: + ; + } // end switch + } // end while loop + + return false; +} + void ClassVerifier::verify_invoke_init( RawBytecodeStream* bcs, u2 ref_class_index, VerificationType ref_class_type, StackMapFrame* current_frame, u4 code_length, bool *this_uninit, @@ -2245,18 +2387,26 @@ 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. ExceptionTable exhandlers(_method()); int exlength = exhandlers.length(); for(int i = 0; i < exlength; i++) { - if (bci >= exhandlers.start_pc(i)) { - verify_error(ErrorContext::bad_code(bci), - "Bad method call from after the start of a try block"); - return; + u2 start_pc = exhandlers.start_pc(i); + u2 end_pc = exhandlers.end_pc(i); + + if (bci >= start_pc && bci < end_pc) { + if (!ends_in_athrow(exhandlers.handler_pc(i), code_length)) { + verify_error(ErrorContext::bad_code(bci), + "Bad method call from after the start of a try block"); + return; + } else if (VerboseVerification) { + ResourceMark rm; + tty->print_cr( + "Survived call to ends_in_athrow(): %s", + current_class()->name()->as_C_string()); + } } }