902 * valid values for the field. |
902 * valid values for the field. |
903 */ |
903 */ |
904 private void readObject(ObjectInputStream s) |
904 private void readObject(ObjectInputStream s) |
905 throws IOException, ClassNotFoundException { |
905 throws IOException, ClassNotFoundException { |
906 s.defaultReadObject(); // read in all fields |
906 s.defaultReadObject(); // read in all fields |
907 if (suppressedExceptions != null) { |
907 |
908 List<Throwable> suppressed = null; |
908 // Set suppressed exceptions and stack trace elements fields |
909 if (suppressedExceptions.isEmpty()) { |
909 // to marker values until the contents from the serial stream |
910 // Use the sentinel for a zero-length list |
910 // are validated. |
911 suppressed = SUPPRESSED_SENTINEL; |
911 List<Throwable> candidateSuppressedExceptions = suppressedExceptions; |
912 } else { // Copy Throwables to new list |
912 suppressedExceptions = SUPPRESSED_SENTINEL; |
913 suppressed = new ArrayList<>(1); |
913 |
914 for (Throwable t : suppressedExceptions) { |
914 StackTraceElement[] candidateStackTrace = stackTrace; |
|
915 stackTrace = UNASSIGNED_STACK.clone(); |
|
916 |
|
917 if (candidateSuppressedExceptions != null) { |
|
918 int suppressedSize = validateSuppressedExceptionsList(candidateSuppressedExceptions); |
|
919 if (suppressedSize > 0) { // Copy valid Throwables to new list |
|
920 var suppList = new ArrayList<Throwable>(Math.min(100, suppressedSize)); |
|
921 |
|
922 for (Throwable t : candidateSuppressedExceptions) { |
915 // Enforce constraints on suppressed exceptions in |
923 // Enforce constraints on suppressed exceptions in |
916 // case of corrupt or malicious stream. |
924 // case of corrupt or malicious stream. |
917 Objects.requireNonNull(t, NULL_CAUSE_MESSAGE); |
925 Objects.requireNonNull(t, NULL_CAUSE_MESSAGE); |
918 if (t == this) |
926 if (t == this) |
919 throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE); |
927 throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE); |
920 suppressed.add(t); |
928 suppList.add(t); |
921 } |
929 } |
|
930 // If there are any invalid suppressed exceptions, |
|
931 // implicitly use the sentinel value assigned earlier. |
|
932 suppressedExceptions = suppList; |
922 } |
933 } |
923 suppressedExceptions = suppressed; |
934 } else { |
924 } // else a null suppressedExceptions field remains null |
935 suppressedExceptions = null; |
|
936 } |
925 |
937 |
926 /* |
938 /* |
927 * For zero-length stack traces, use a clone of |
939 * For zero-length stack traces, use a clone of |
928 * UNASSIGNED_STACK rather than UNASSIGNED_STACK itself to |
940 * UNASSIGNED_STACK rather than UNASSIGNED_STACK itself to |
929 * allow identity comparison against UNASSIGNED_STACK in |
941 * allow identity comparison against UNASSIGNED_STACK in |
930 * getOurStackTrace. The identity of UNASSIGNED_STACK in |
942 * getOurStackTrace. The identity of UNASSIGNED_STACK in |
931 * stackTrace indicates to the getOurStackTrace method that |
943 * stackTrace indicates to the getOurStackTrace method that |
932 * the stackTrace needs to be constructed from the information |
944 * the stackTrace needs to be constructed from the information |
933 * in backtrace. |
945 * in backtrace. |
934 */ |
946 */ |
935 if (stackTrace != null) { |
947 if (candidateStackTrace != null) { |
936 if (stackTrace.length == 0) { |
948 // Work from a clone of the candidateStackTrace to ensure |
937 stackTrace = UNASSIGNED_STACK.clone(); |
949 // consistency of checks. |
938 } else if (stackTrace.length == 1 && |
950 candidateStackTrace = candidateStackTrace.clone(); |
|
951 if (candidateStackTrace.length >= 1) { |
|
952 if (candidateStackTrace.length == 1 && |
939 // Check for the marker of an immutable stack trace |
953 // Check for the marker of an immutable stack trace |
940 SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(stackTrace[0])) { |
954 SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(candidateStackTrace[0])) { |
941 stackTrace = null; |
955 stackTrace = null; |
942 } else { // Verify stack trace elements are non-null. |
956 } else { // Verify stack trace elements are non-null. |
943 for(StackTraceElement ste : stackTrace) { |
957 for (StackTraceElement ste : candidateStackTrace) { |
944 Objects.requireNonNull(ste, "null StackTraceElement in serial stream."); |
958 Objects.requireNonNull(ste, "null StackTraceElement in serial stream."); |
|
959 } |
|
960 stackTrace = candidateStackTrace; |
945 } |
961 } |
946 } |
962 } |
|
963 } |
|
964 // A null stackTrace field in the serial form can result from |
|
965 // an exception serialized without that field in older JDK |
|
966 // releases; treat such exceptions as having empty stack |
|
967 // traces by leaving stackTrace assigned to a clone of |
|
968 // UNASSIGNED_STACK. |
|
969 } |
|
970 |
|
971 private int validateSuppressedExceptionsList(List<Throwable> deserSuppressedExceptions) |
|
972 throws IOException { |
|
973 if (!Object.class.getModule(). |
|
974 equals(deserSuppressedExceptions.getClass().getModule())) { |
|
975 throw new StreamCorruptedException("List implementation not in base module."); |
947 } else { |
976 } else { |
948 // A null stackTrace field in the serial form can result |
977 int size = deserSuppressedExceptions.size(); |
949 // from an exception serialized without that field in |
978 if (size < 0) { |
950 // older JDK releases; treat such exceptions as having |
979 throw new StreamCorruptedException("Negative list size reported."); |
951 // empty stack traces. |
980 } |
952 stackTrace = UNASSIGNED_STACK.clone(); |
981 return size; |
953 } |
982 } |
954 } |
983 } |
955 |
984 |
956 /** |
985 /** |
957 * Write a {@code Throwable} object to a stream. |
986 * Write a {@code Throwable} object to a stream. |