8267118: OutOfMemoryError cannot be caught as a Throwable

Co-authored-by: Ioi Lam <iklam@openjdk.org>
Reviewed-by: coleenp
This commit is contained in:
David Holmes 2021-06-02 10:42:17 +00:00
parent de6472c441
commit 71425ddfb4
3 changed files with 125 additions and 1 deletions

View File

@ -1890,6 +1890,12 @@ void ClassVerifier::verify_exception_handler_table(u4 code_length, char* code_da
catch_type_index, cp, CHECK_VERIFY(this));
VerificationType throwable =
VerificationType::reference_type(vmSymbols::java_lang_Throwable());
// If the catch type is Throwable pre-resolve it now as the assignable check won't
// do that, and we need to avoid a runtime resolution in case we are trying to
// catch OutOfMemoryError.
if (cp->klass_name_at(catch_type_index) == vmSymbols::java_lang_Throwable()) {
cp->klass_at(catch_type_index, CHECK);
}
bool is_subclass = throwable.is_assignable_from(
catch_type, this, false, CHECK_VERIFY(this));
if (!is_subclass) {

View File

@ -226,6 +226,11 @@ void Method::print_external_name(outputStream *os, Klass* klass, Symbol* method_
}
int Method::fast_exception_handler_bci_for(const methodHandle& mh, Klass* ex_klass, int throw_bci, TRAPS) {
if (log_is_enabled(Debug, exceptions)) {
ResourceMark rm(THREAD);
log_debug(exceptions)("Looking for catch handler for exception of type \"%s\" in method \"%s\"",
ex_klass == NULL ? "NULL" : ex_klass->external_name(), mh->name()->as_C_string());
}
// exception table holds quadruple entries of the form (beg_bci, end_bci, handler_bci, klass_index)
// access exception table
ExceptionTable table(mh());
@ -238,27 +243,67 @@ int Method::fast_exception_handler_bci_for(const methodHandle& mh, Klass* ex_kla
int beg_bci = table.start_pc(i);
int end_bci = table.end_pc(i);
assert(beg_bci <= end_bci, "inconsistent exception table");
log_debug(exceptions)(" - checking exception table entry for BCI %d to %d",
beg_bci, end_bci);
if (beg_bci <= throw_bci && throw_bci < end_bci) {
// exception handler bci range covers throw_bci => investigate further
log_debug(exceptions)(" - entry covers throw point BCI %d", throw_bci);
int handler_bci = table.handler_pc(i);
int klass_index = table.catch_type_index(i);
if (klass_index == 0) {
if (log_is_enabled(Info, exceptions)) {
ResourceMark rm(THREAD);
log_info(exceptions)("Found catch-all handler for exception of type \"%s\" in method \"%s\" at BCI: %d",
ex_klass == NULL ? "NULL" : ex_klass->external_name(), mh->name()->as_C_string(), handler_bci);
}
return handler_bci;
} else if (ex_klass == NULL) {
// Is this even possible?
if (log_is_enabled(Info, exceptions)) {
ResourceMark rm(THREAD);
log_info(exceptions)("NULL exception class is implicitly caught by handler in method \"%s\" at BCI: %d",
mh()->name()->as_C_string(), handler_bci);
}
return handler_bci;
} else {
if (log_is_enabled(Debug, exceptions)) {
ResourceMark rm(THREAD);
log_debug(exceptions)(" - resolving catch type \"%s\"",
pool->klass_name_at(klass_index)->as_C_string());
}
// we know the exception class => get the constraint class
// this may require loading of the constraint class; if verification
// fails or some other exception occurs, return handler_bci
Klass* k = pool->klass_at(klass_index, CHECK_(handler_bci));
Klass* k = pool->klass_at(klass_index, THREAD);
if (HAS_PENDING_EXCEPTION) {
if (log_is_enabled(Debug, exceptions)) {
ResourceMark rm(THREAD);
log_debug(exceptions)(" - exception \"%s\" occurred resolving catch type",
PENDING_EXCEPTION->klass()->external_name());
}
return handler_bci;
}
assert(k != NULL, "klass not loaded");
if (ex_klass->is_subtype_of(k)) {
if (log_is_enabled(Info, exceptions)) {
ResourceMark rm(THREAD);
log_info(exceptions)("Found matching handler for exception of type \"%s\" in method \"%s\" at BCI: %d",
ex_klass == NULL ? "NULL" : ex_klass->external_name(), mh->name()->as_C_string(), handler_bci);
}
return handler_bci;
}
}
}
}
if (log_is_enabled(Debug, exceptions)) {
ResourceMark rm(THREAD);
log_debug(exceptions)("No catch handler found for exception of type \"%s\" in method \"%s\"",
ex_klass->external_name(), mh->name()->as_C_string());
}
return -1;
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8267118
* @summary Test catching Throwable doesn't trigger OOME
* @library /test/lib
* @run driver TestCatchThrowableOOM
*/
import java.util.HashMap;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
public class TestCatchThrowableOOM {
private static String[] expected = new String[] {
"Test starting ...",
"Test complete",
"Exception <a 'java/lang/OutOfMemoryError'", // from logging
};
public static void main(String[] args) throws Throwable {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xmx64m",
"-Xlog:exceptions=trace",
"TestCatchThrowableOOM$OOM");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
for (String msg : expected) {
output.shouldContain(msg);
}
}
static class OOM {
private static HashMap<Object, Object> store = new HashMap<>();
public static void main(String[] args) {
System.out.println(expected[0]);
try {
// Keep adding entries until we throw OOME
while (true) {
Object o = new Byte[100 * 1024];
store.put(o, o);
}
} catch (Throwable oome) {
store = null;
System.gc(); // Just for good measure
System.out.println(expected[1]);
}
}
}
}