-
CSR
-
Resolution: Approved
-
P3
-
None
-
source, binary, behavioral
-
low
-
-
Java API
-
SE
Summary
The ObjectInputStream.GetField.get(String name, Object val)
method is returning null instead of throwing an exception when the class of the object is not found. The caller is not able to correctly handle the case where the class is not found. The signature of GetField.get(name, val)
should have a throws ClassNotFoundException
and a ClassNotFoundException
exception should be thrown.
Adding ClassNotFoundException to the signature is considered because calling the method can only occur within the readObject
method of a serializable class and the readObject signature includes ClassNotFoundException in its list of throws clauses.
See the compatibility section for details.
The problem is the same for all versions of the JDK. The solution in JDK-8275652 for older versions is to throw IOException
instead of ClassNotFoundException
and it has some compatibility risk.
The issue has only been reported for java.util.Vector and a more specific workaround is proposed (JDK-8277093).
Problem
When using ObjectInputStream.readFields, it may be the case that while deserializing an object, a class being deserialized is not found.
There is currently a bug such that though the ClassNotFoundException
is recorded, it is not reported and null is returned instead. The specification is silent on this particular case, and returning null (except when the field value is actually null) is not supported by the specification. The caller is likely to misinterpret the null as a field value.
ObjectInputStream.readFields()
is used to provide access to field values read from the stream. The returned ObjectInputStream.GetField
instance holds the values read for later retrieval. For a missing class, the ClassNotFoundException is correctly recorded for its field. However, calling ObjectInputStream.GetField.get(String name, Object val)
method is unable to throw the CNFE because the method signature does not include the needed throws clause.
This bug has been present since JDK 1.4 and has caused an issue in java.util.Vector in JDK 7 and more recent versions.
This change only applies to uses of ObjectInputStream.readFields
and does not apply to cases where defaultReadObject
is used by a class implementing its own readObject
method.
Solution
A ClassNotFoundException
should be thrown from GetField.get
to prevent the caller from continuing to process the field value as if it were valid. The signature of GetField.get
is modified to add a throws for ClassNotFoundException
.
The ClassNotFoundException
recorded for the field is thrown allowing the caller to correctly identify the cause.
Note that GetField.get
is only callable from the readObject
method of the class being deserialized; it is rare that any caller actually catches either ClassNotFoundException
or IOException
; both are typically handled further up the call stack.
The ObjectInputStream.readFields
and GetField.get
method can only be called from inside the readObject
method by the implementation of ObjectInputStream.readObject
. The ObjectInputStream
implementation handles the specific case of IOException
wrapping a ClassNotFoundException
correctly to identify the missing class and throw the ClassNotFoundException
.
Specification
In java.io.ObjectInputStream.GetField:
/**
* Get the value of the named Object field from the persistent field.
*
* @param name the name of the field
* @param val the default value to use if {@code name} does not
* have a value
* @return the value of the named {@code Object} field
+ * @throws ClassNotFoundException Class of a serialized object cannot be found.
* @throws IOException if there are I/O errors while reading from the
* underlying {@code InputStream}
* @throws IllegalArgumentException if type of {@code name} is
* not serializable or if the field type is incorrect
*/
- public abstract Object get(String name, Object val) throws IOException;
+ public abstract Object get(String name, Object val) throws IOException, ClassNotFoundException;
To revert to the old behavior a system property jdk.serialGetFieldCnfeReturnsNull
is added to the implementation (not a JavaSE property). Setting the value to true
reverts to the old behavior (returning null); leaving it unset or to any other value results in the throwing of ClassNotFoundException
.
- csr of
-
JDK-8276665 ObjectInputStream.GetField.get(name, object) should throw ClassNotFoundException
-
- Closed
-
- relates to
-
JDK-8275652 ObjectInputStream.GetField.get returns null instead of handling ClassNotFoundException
-
- Closed
-