8212328: Exceptional throw cases
Reviewed-by: smarks, rriggs, igerasim, ahgross, skoivu
--- a/src/java.base/share/classes/java/lang/Throwable.java Wed Mar 27 00:24:13 2019 -0700
+++ b/src/java.base/share/classes/java/lang/Throwable.java Thu Mar 28 14:18:57 2019 -0700
@@ -25,8 +25,8 @@
package java.lang;
-import java.io.*;
-import java.util.*;
+import java.io.*;
+import java.util.*;
/**
* The {@code Throwable} class is the superclass of all errors and
@@ -904,24 +904,36 @@
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject(); // read in all fields
- if (suppressedExceptions != null) {
- List<Throwable> suppressed = null;
- if (suppressedExceptions.isEmpty()) {
- // Use the sentinel for a zero-length list
- suppressed = SUPPRESSED_SENTINEL;
- } else { // Copy Throwables to new list
- suppressed = new ArrayList<>(1);
- for (Throwable t : suppressedExceptions) {
+
+ // Set suppressed exceptions and stack trace elements fields
+ // to marker values until the contents from the serial stream
+ // are validated.
+ List<Throwable> candidateSuppressedExceptions = suppressedExceptions;
+ suppressedExceptions = SUPPRESSED_SENTINEL;
+
+ StackTraceElement[] candidateStackTrace = stackTrace;
+ stackTrace = UNASSIGNED_STACK.clone();
+
+ if (candidateSuppressedExceptions != null) {
+ int suppressedSize = validateSuppressedExceptionsList(candidateSuppressedExceptions);
+ if (suppressedSize > 0) { // Copy valid Throwables to new list
+ var suppList = new ArrayList<Throwable>(Math.min(100, suppressedSize));
+
+ for (Throwable t : candidateSuppressedExceptions) {
// Enforce constraints on suppressed exceptions in
// case of corrupt or malicious stream.
Objects.requireNonNull(t, NULL_CAUSE_MESSAGE);
if (t == this)
throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE);
- suppressed.add(t);
+ suppList.add(t);
}
+ // If there are any invalid suppressed exceptions,
+ // implicitly use the sentinel value assigned earlier.
+ suppressedExceptions = suppList;
}
- suppressedExceptions = suppressed;
- } // else a null suppressedExceptions field remains null
+ } else {
+ suppressedExceptions = null;
+ }
/*
* For zero-length stack traces, use a clone of
@@ -932,24 +944,41 @@
* the stackTrace needs to be constructed from the information
* in backtrace.
*/
- if (stackTrace != null) {
- if (stackTrace.length == 0) {
- stackTrace = UNASSIGNED_STACK.clone();
- } else if (stackTrace.length == 1 &&
+ if (candidateStackTrace != null) {
+ // Work from a clone of the candidateStackTrace to ensure
+ // consistency of checks.
+ candidateStackTrace = candidateStackTrace.clone();
+ if (candidateStackTrace.length >= 1) {
+ if (candidateStackTrace.length == 1 &&
// Check for the marker of an immutable stack trace
- SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(stackTrace[0])) {
- stackTrace = null;
- } else { // Verify stack trace elements are non-null.
- for(StackTraceElement ste : stackTrace) {
- Objects.requireNonNull(ste, "null StackTraceElement in serial stream.");
+ SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(candidateStackTrace[0])) {
+ stackTrace = null;
+ } else { // Verify stack trace elements are non-null.
+ for (StackTraceElement ste : candidateStackTrace) {
+ Objects.requireNonNull(ste, "null StackTraceElement in serial stream.");
+ }
+ stackTrace = candidateStackTrace;
}
}
+ }
+ // A null stackTrace field in the serial form can result from
+ // an exception serialized without that field in older JDK
+ // releases; treat such exceptions as having empty stack
+ // traces by leaving stackTrace assigned to a clone of
+ // UNASSIGNED_STACK.
+ }
+
+ private int validateSuppressedExceptionsList(List<Throwable> deserSuppressedExceptions)
+ throws IOException {
+ if (!Object.class.getModule().
+ equals(deserSuppressedExceptions.getClass().getModule())) {
+ throw new StreamCorruptedException("List implementation not in base module.");
} else {
- // A null stackTrace field in the serial form can result
- // from an exception serialized without that field in
- // older JDK releases; treat such exceptions as having
- // empty stack traces.
- stackTrace = UNASSIGNED_STACK.clone();
+ int size = deserSuppressedExceptions.size();
+ if (size < 0) {
+ throw new StreamCorruptedException("Negative list size reported.");
+ }
+ return size;
}
}