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	Fri Aug  8 08:40:13 2014
--- new/src/share/vm/classfile/verifier.cpp	Fri Aug  8 08:40:10 2014

*** 2215,2224 **** --- 2215,2407 ---- } default: ShouldNotReachHere(); } } + // 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(ExceptionTable* exhandlers, + GrowableArray<u4>* handler_stack, + u4 bci) { + int exlength = exhandlers->length(); + for(int x = 0; x < exlength; x++) { + if (bci >= exhandlers->start_pc(x) && bci < exhandlers->end_pc(x)) { + handler_stack->append_if_missing(exhandlers->handler_pc(x)); + } + } + } + + // Return TRUE if all code paths starting with start_bc_offset end in athrow. + bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) { + // Create bytecode stream. + RawBytecodeStream bcs(method()); + u4 code_length = method()->code_size(); + bcs.set_interval(start_bc_offset, code_length); + u4 target; + // Create stack for storing bytecode intervals for if*, goto*, and *switch. + GrowableArray<u4>* bci_stack = new GrowableArray<u4>(50); + // Create stack for handlers for try blocks containing this handler. + GrowableArray<u4>* handler_stack = new GrowableArray<u4>(50); + // Create list of visited branch opcodes (goto* and if*). + GrowableArray<u4>* visited_branches = new GrowableArray<u4>(50); + ExceptionTable exhandlers(_method()); + + while (true) { + if (bcs.is_last_bytecode()) { + // if no more intervals 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 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(); + + // 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; + 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); + } else { + 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, code_length); + // 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); + } + // 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; + 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); + } else { + if (target >= code_length) return false; + // Continue scanning from the target onward. + bcs.set_interval(target, code_length); + // 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 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()) { + if (handler_stack->is_empty()) { + return true; + } else { + // Parse the catch handlers for try blocks containing athrow. + bcs.set_interval(handler_stack->pop(), code_length); + } + } 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, constantPoolHandle cp, TRAPS) { u2 bci = bcs->bci();
*** 2234,2255 **** --- 2417,2446 ---- TypeOrigin::implicit(current_type())), "Bad <init> method call"); 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))) { 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