--- old/src/share/vm/classfile/verifier.cpp 2014-07-24 11:15:43.761527000 -0400 +++ new/src/share/vm/classfile/verifier.cpp 2014-07-24 11:15:41.051719000 -0400 @@ -2219,6 +2219,125 @@ } } +// Make sure that all switch alternatives end in 'athrow' bytecodes. +// Since it could be 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. +bool ClassVerifier::switch_athrows(Bytecodes::Code opcode, RawBytecodeStream* bcs) { + assert(opcode == Bytecodes::_lookupswitch || opcode == Bytecodes::_tableswitch, + "Bad opcode"); + int bci = bcs->bci(); + address bcp = bcs->bcp(); + address aligned_bcp = (address) round_to((intptr_t)(bcp + 1), jintSize); + u4 default_offset = Bytes::get_Java_u4(aligned_bcp); + u4 code_length = method()->code_size(); + 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; + } + + // This is invalid, but let the regular bytecode verifier + // report this because the user will get a better error message. + if (keys < 0) return true; + + if (default_offset + bci > code_length) return false; + if (!ends_in_athrow(default_offset + bci, code_length, false)) return false; + + 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; + if (!ends_in_athrow(target, code_length, false)) return false; + } + + return true; // All switch alternatives ended in a throw. +} + +bool ClassVerifier::ends_in_athrow(u4 start_bc_offset, u4 end_bc_offset, + bool reach_end_of_bc) { + // Create bytecode stream. + RawBytecodeStream bcs(method()); + bcs.set_interval(start_bc_offset, end_bc_offset); + u4 code_length = method()->code_size(); + u4 target; + + while (!bcs.is_last_bytecode()) { + Bytecodes::Code opcode = bcs.raw_next(); + u2 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; + // scan bytecodes up to the target. + if (!ends_in_athrow(bcs.next_bci(), target, true)) return false; + bcs.set_next_bci(target); + } else { // backward branch + // Check the bytecodes between the branch target and the current offset + if (!ends_in_athrow(target, bci, true)) return false; + } + 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; + return ends_in_athrow(target, code_length, false); + } else { // backward branch + // Check bytecodes between the branch target and the current offset. + if (!ends_in_athrow(target, bci, true)) return false; + } + break; + + case Bytecodes::_lookupswitch : + case Bytecodes::_tableswitch: + if (!switch_athrows(opcode, &bcs)) return false; + break; + + case Bytecodes::_return : + return false; + + case Bytecodes::_athrow : + return true; + + default: + ; + } // end switch + } // end while loop + + return reach_end_of_bc; +} + 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 +2364,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, false)) { + 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()); + } } }