Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8276742

ObjectInputStream.GetField.get(name, object) should throw ClassNotFoundException

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P3 P3
    • 18
    • core-libs
    • None
    • source, binary, behavioral
    • low
    • Hide
      The source compatibility risk is low; the addition of a throws CNFE should not cause a source compatibility or a compilation warning because the `GetField` object and its methods are called within the context of the `readObject` method and it includes `throws ClassNotFoundException`.

      The binary and behavioral compatibility risk is minimal since CNFE is one of the expected exceptions in any `readObject` method.

      A system property `jdk.serialGetFieldCnfeReturnsNull` is added to allow an application to revert to the old behavior.
      Show
      The source compatibility risk is low; the addition of a throws CNFE should not cause a source compatibility or a compilation warning because the `GetField` object and its methods are called within the context of the `readObject` method and it includes `throws ClassNotFoundException`. The binary and behavioral compatibility risk is minimal since CNFE is one of the expected exceptions in any `readObject` method. A system property `jdk.serialGetFieldCnfeReturnsNull` is added to allow an application to revert to the old behavior.
    • 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.

            rriggs Roger Riggs
            rriggs Roger Riggs
            Stuart Marks
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: