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