changeset 13595:f7b22e107b51

8212328: Exceptional throw cases Reviewed-by: smarks, rriggs, igerasim, ahgross, skoivu, andrew Contributed-by: David Alvarez <alvdavi@amazon.com>
author andrew
date Thu, 04 Jul 2019 17:55:44 +0100
parents 35818757a9c6
children 26993f6987f7
files src/share/classes/java/lang/Throwable.java
diffstat 1 files changed, 58 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/Throwable.java	Mon Jul 01 20:58:14 2019 +0100
+++ b/src/share/classes/java/lang/Throwable.java	Thu Jul 04 17:55:44 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, 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
@@ -24,8 +24,9 @@
  */
 
 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
@@ -912,25 +913,37 @@
     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
+                List<Throwable> suppList  = new ArrayList<Throwable>(Math.min(100, suppressedSize));
+
+                for (Throwable t : candidateSuppressedExceptions) {
                     // Enforce constraints on suppressed exceptions in
                     // case of corrupt or malicious stream.
                     if (t == null)
                         throw new NullPointerException(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
@@ -941,25 +954,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) {
-                    if (ste == null)
-                        throw new NullPointerException("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) {
+                        if (ste == null)
+                            throw new NullPointerException("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.getClassLoader() != deserSuppressedExceptions.getClass().getClassLoader()) {
+            throw new StreamCorruptedException("List implementation not on the bootclasspath.");
         } 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;
         }
     }