src/share/vm/classfile/verifier.cpp
Index Unified diffs Context diffs Sdiffs Wdiffs Patch New Old Previous File Next File
*** old/src/share/vm/classfile/verifier.cpp	Thu Jul 24 11:15:43 2014
--- new/src/share/vm/classfile/verifier.cpp	Thu Jul 24 11:15:41 2014

*** 2217,2226 **** --- 2217,2345 ---- } default: ShouldNotReachHere(); } } + // 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, constantPoolHandle cp, TRAPS) { u2 bci = bcs->bci();
*** 2243,2264 **** --- 2362,2391 ---- verify_error(ErrorContext::bad_code(bci), "Bad <init> method call from inside of a branch"); 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)) { + 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 <init> 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()); + } } } current_frame->initialize_object(type, current_type()); *this_uninit = true;

src/share/vm/classfile/verifier.cpp
Index Unified diffs Context diffs Sdiffs Wdiffs Patch New Old Previous File Next File