JDK-4931314 : java.io.StreamCorruptedException thrown due to java.lang.ClassNotFoundException
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io:serialization
  • Affected Version: 1.4.1_03
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2003-10-02
  • Updated: 2017-05-16
  • Resolved: 2003-10-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other Other
1.4.1_07 07Fixed 1.4.2_04Fixed
Description
Many of us have came across lot of crs that has StreamCorruptedExceptions from the following code in java.io.ObjectInputStream:

         private ObjectStreamClass readNonProxyDesc(boolean unshared)
         throws IOException
         {
         if (bin.readByte() != TC_CLASSDESC) {
             throw new StreamCorruptedException();
         }

         ObjectStreamClass desc = new ObjectStreamClass();
         int descHandle = handles.assign(unshared ? unsharedMarker : desc);
         passHandle = NULL_HANDLE;

         ObjectStreamClass readDesc = null;
         try {
             readDesc = readClassDescriptor();
         } catch (ClassNotFoundException ex) {
             // REMIND: do something less drastic here?
             throw new StreamCorruptedException();
         }

         Class cl = null;
         ClassNotFoundException resolveEx = null;
         bin.setBlockDataMode(true);
         try {
             if ((cl = resolveClass(readDesc)) == null) {
             throw new ClassNotFoundException("null class");
             }
         } catch (ClassNotFoundException ex) {
             resolveEx = ex;
         }
         skipCustomData();

         desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));

         handles.finish(descHandle);
         passHandle = descHandle;
         return desc;
         }

I highligted the place where it is throwing StreamCorruptedException.  It is throwing StreamCorruptedException when it gets ClassNotFoundException while reading ClassDescriptor.  Many of us actually gone through crs that has StreamCorruptedExceptions from this code and we were little confused intially that why we would experience this exception.  Later on after looking at the code we realized that it is result of ClassCastException.  

There is also a reminder that says 'do something less drastic here'.  This wasn't like this in 1.3.1_x.  In 1.3.1_x, it is using inputClassDescriptor() method and that method throws ClassNotFoundException.  From 1.4.1 onwards they changed the code and calling this readNonProxyDesc() method and it doesn't have ClassNotFoundException in the throws class and hence they are throwing StreamCorruptedExceptions.

crs -> customer complaints received by BEA

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.4.1_07 1.4.2_04 generic tiger-beta FIXED IN: 1.4.1_07 1.4.2_04 tiger-beta INTEGRATED IN: 1.4.1_07 1.4.2_04 tiger-b28 tiger-beta VERIFIED IN: 1.4.1_07
14-06-2004

SUGGESTED FIX ###@###.### 2003-10-17 1.4.1_06, 1.4.2_03 will implement following fix. 1.5.0 will likely have similar fix. *** /usr/local/ctetools/jakarta-tomcat-3.2.2/webapps/ctetools/CodeStore/847/webrev/src/share/classes/java/io/ObjectInputStream.java- Thu Oct 16 14:52:43 2003 --- ObjectInputStream.java Fri Oct 10 11:55:48 2003 *** 1500,1511 **** ObjectStreamClass readDesc = null; try { readDesc = readClassDescriptor(); } catch (ClassNotFoundException ex) { ! // REMIND: do something less drastic here? ! throw new StreamCorruptedException(); } Class cl = null; ClassNotFoundException resolveEx = null; bin.setBlockDataMode(true); --- 1500,1511 ---- ObjectStreamClass readDesc = null; try { readDesc = readClassDescriptor(); } catch (ClassNotFoundException ex) { ! throw (IOException) new InvalidClassException( ! "failed to read class descriptor").initCause(ex); } Class cl = null; ClassNotFoundException resolveEx = null; bin.setBlockDataMode(true);
11-06-2004

EVALUATION The change in behavior between 1.3 and 1.4 was a consequence of the fix for bugs 4313167 ("ClassNotFoundException in skipped objects causes serialization to fail") and 4312433 ("reading back reference to obj with unresolved class should throw exception"). The fix for these bugs involved reworking the way that ObjectInputStream handles ClassNotFoundExceptions to be more robust--starting in JDK 1.4, ObjectInputStream is able to tolerate class resolution failures and continue to parse the stream, allowing it to deserialize objects appearing later in the stream (which is necessary in cases where the class resolution failure is non-fatal--for instance, if the missing class corresponds to a "skipped" object that is not referenced by the deserialized object graph). ObjectInputStream is able to tolerate class resolution failures because the serialization stream is self-describing--even if a class isn't present, its class descriptor in the stream describes the length and layout of the data for instances of that class. If, however, an error occurs while reading in the class descriptor itself, then the stream becomes unparsable, because the data layout information is lost. For this reason, a ClassNotFoundException encountered while reading in a class descriptor is more serious than an "ordinary" ClassNotFoundException, since it leaves the stream in an unknown, unparsable state. Because of this, such a ClassNotFoundException cannot simply be propagated to the caller--other ObjectInputStream code further up the call stack would unsuccessfully attempt to recover from the ClassNotFoundException, in most cases ultimately resulting in a StreamCorruptedException. This highlights the need to distinguish ClassNotFoundExceptions thrown while reading in class descriptors, which should be considered fatal stream errors, from "ordinary" ClassNotFoundExceptions, which the stream can tolerate. That said, the bug report is justified in pointing out that throwing StreamCorruptedException in this case leads to confusion; furthermore, the cause of the thrown exception is not set to the original ClassNotFoundException, thus further obscuring the original problem. A better solution would be to instead throw an InvalidClassException whose cause is set to be the original ClassNotFoundException. This has the following benefits: - InvalidClassException more clearly indicates that the problem is related to class data deserialization - InvalidClassException is a subclass of ObjectStreamException, as is StreamCorruptedException, so any code catching IOExceptions or ObjectStreamExceptions thrown by ObjectInputStream in this situation would not be affected by the change - InvalidClassException is (obviously) not a ClassNotFoundException, and hence avoids the problems with propagating the ClassNotFoundException unchanged, as alluded to above ###@###.### 2003-10-02
02-10-2003