JDK-8059590 : ArrayIndexOutOfBoundsException occurs when Container with overridden getComponents() is deserialized
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 8u20
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2014-10-02
  • Updated: 2015-06-04
  • Resolved: 2014-10-20
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.
8u40Fixed 9 b38Fixed
Related Reports
Relates :  
If a subclass of java.awt.Container overrides getComponents(), and the object with overridden getComponents() is serialized and then deserialized, ArrayIndexOutOfBoundsException occurs.

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at java.awt.Container.readObject(Container.java:3733)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:483)
        at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1017)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1896)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
        at a.main(a.java:17)

This problem does not occur in jdk6u81. This seems to be a regression.
Regression was introduced by JDK-6784816 which is very old to analyze.

Root cause: Container.writeObject directly accesses component.size() to write "ncomponents" field but it uses getComponentsSync() method when writing "component" field. This method eventually calls public method getComponents(). Since getComponents() is overridden to return an empty array, the number of "expected" components in "ncomponents" and the length of "component" array differs which causes ArrayIndexOutOfBoundsException when readObject tries to copy the read "component" array into internal component list.

Proposed fix: Use component.toArray(EMPTY_ARRAY) to serialize "component" field. This approach preserves the internal state of Container object: if a component is added into Container, it will be in the Container after the object is deserialized. If serialization code uses getComponents() or getComponentsSync() to write both "ncomponents" and "component" fields, then components added via add() method will be lost after deserialization.