United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4931314 : java.io.StreamCorruptedException thrown due to java.lang.ClassNotFoundException

Details
Type:
Bug
Submit Date:
2003-10-02
Status:
Closed
Updated Date:
2003-12-01
Project Name:
JDK
Resolved Date:
2003-10-17
Component:
core-libs
OS:
generic
Sub-Component:
java.io:serialization
CPU:
generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
1.4.1_03
Fixed Versions:
1.4.1_07 (07)

Related Reports
Backport:
Backport:

Sub Tasks

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


                                     
2004-06-14
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);
                                     
2004-06-11
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
                                     
2003-10-02



Hardware and Software, Engineered to Work Together