EnumSet.class serialization broken - twice
Stuart Marks
stuart.marks at oracle.com
Thu Jun 27 21:57:40 UTC 2019
Arrrrrggggh. Yet another serialization bug.
But yes, this is a bug. Thanks for finding and diagnosing it.
Like Martin, I've often forgotten that classes themselves can be included in a
serial stream, as well as instances of those classes. In fact I seem to recall
arguing that because EnumSet uses the serialization proxy pattern, instances of
it should never appear in a legitimate serial stream. I think that's true.
However, I sent on to say that because of this, there is no issue with
serialization compatibility, and thus EnumSet didn't need a serialVersionUID.
That's incorrect.
I'm uncomfortable with relaxing the serialization spec and mechanism to allow a
class in the serial stream to have a different svuid from the one loaded in the
running JVM. Offhand I don't know what problems it could cause, but it seems
like a fundamental change that would lead to problems at some point.
Also, this is a problem with one class (so far...) and I don't think we should
change the whole serialization mechanism to support it.
I'm thus leaning toward your first suggestion of adding a serialVersionUID
declaration to EnumSet that matches the value from JDK 8. This would go into the
current repo (JDK 14) and likely be backported to JDK 13.
It seems like a no-brainer to backport this to JDK 11 as well; this would
provide broad compatibility across JDK 8 LTS, JDK 11 LTS, and current JDK
releases. However, changing the svuid is a specification change. More
investigation is necessary to figure out what would be involved in doing this.
Meanwhile, it would seem sensible to file a bug and start on a fix for the
current release(s). Would you be able to do that?
Again, thanks for finding this.
s'marks
On 6/18/19 7:32 AM, Peter Levart wrote:
>
>
> On 6/18/19 4:00 PM, Martin Buchholz wrote:
>> Java Historian says:
>> I was a reviewer for Effective Java 3rd Edition and EnumSet is the canonical
>> example of the Serialization Proxy pattern,
>> so I tried to make sure the pattern was implemented as perfectly as possible.
>> 8192935: Fix EnumSet's SerializationProxy javadoc
>> All of us who try to make java serialization work right have a mental model of
>> the many things that might go wrong.
>> Serialization of Class objects has never been part of my own mental model -
>> I've only ever considered instances.
>
> Perhaps the necessity for Class objects representing Serializable classes to
> agree in sertialVersionUID is a bug in Java serialization implementation?
> There's no such requirement for Class objects representing non-Serializable
> classes and I don't see why this requirement is there for Serializable classes.
> Could this requirement simply be relaxed with no ill consequences?
>
> Regards, Peter
>
>>
>>
>> On Tue, Jun 18, 2019 at 5:32 AM Peter Levart <peter.levart at gmail.com
>> <mailto:peter.levart at gmail.com>> wrote:
>>
>> Hi,
>>
>> I recently stumbled on an exception thrown when deserializing stream
>> produced on JDK 8 and read with JDK 11. I narrowed the problem
>> down to
>> serialization/deserialization of a public EnumSet.class object. There
>> were several changes made to EnumSet in the Mercurial history of jdk
>> repo, but I think the following two broke the serialization:
>>
>> http://hg.openjdk.java.net/jdk/jdk/rev/d0e8542ef650
>> http://hg.openjdk.java.net/jdk/jdk/rev/a7e13065a7a0
>>
>> It is interesting to note that before those two changes were made,
>> there
>> was a chance to fix the problem reported by newly added serial lint
>> warnings. Unfortunately they were just silenced:
>>
>> http://hg.openjdk.java.net/jdk/jdk/rev/501d8479f798
>>
>> + at SuppressWarnings("serial") // No serialVersionUID due to usage of
>> + // serial proxy pattern
>>
>> It is true that serialization of instances of Serializable classes is
>> not broken by changes to them when they implement serial proxy
>> pattern
>> (i.e. writeReplace() method) even if they don't itself declare a
>> private
>> static final long serialVersionUID field, but this is not true of
>> Class
>> objects representing those Serializable classes. It is even more
>> controversial that serialization of Class objects representing
>> non-Serializable classes is never broken (which is understandable as
>> they don't have a habit of declaring serialVersionUID fields).
>>
>> Both of the above braking changes were made post JDK 8 release, so
>> deserialization of JDK 8 (and older) streams is affected in all
>> JDK 9 +
>> releases or vice versa.
>>
>> So, what shall be done. I suggest adding serialVersionUID field to
>> EnumSet vith a value that corresponds to JDK 8 serialization
>> format and
>> later backport this change to JDK 11.
>>
>> What do you think?
>>
>>
>> Regards, Peter
>>
>>
>> PS: ImmutableCollections nested classes also implement serial proxy
>> pattern and don't declare serialVersionUID fields, but they are not
>> public, so it is less chance that Class objects representing them
>> could
>> be used in serial streams, although it is not impossible. For example:
>>
>> objectOutputStream.writeObject(Set.of().getClass());
>>
>
More information about the core-libs-dev
mailing list