# HG changeset patch # User plevart # Date 1563014625 0 # Node ID 7fd4446c02ee3868a5597f4175b7a014280c6f14 # Parent 7e04ccc598e0e70af1b643931b23b6be88be124a 8227368: EnumSet.class serialization broken in JDK 9+ Reviewed-by: smarks diff -r 7e04ccc598e0 -r 7fd4446c02ee src/java.base/share/classes/java/util/EnumSet.java --- a/src/java.base/share/classes/java/util/EnumSet.java Fri Jul 12 21:38:46 2019 +0200 +++ b/src/java.base/share/classes/java/util/EnumSet.java Sat Jul 13 10:43:45 2019 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,11 +76,12 @@ * @since 1.5 * @see EnumMap */ -@SuppressWarnings("serial") // No serialVersionUID due to usage of - // serial proxy pattern public abstract class EnumSet> extends AbstractSet implements Cloneable, java.io.Serializable { + // declare EnumSet.class serialization compatibility with JDK 8 + private static final long serialVersionUID = 1009687484059888093L; + /** * The class of all the elements of this set. */ diff -r 7e04ccc598e0 -r 7fd4446c02ee test/jdk/java/util/EnumSet/BogusEnumSet.java --- a/test/jdk/java/util/EnumSet/BogusEnumSet.java Fri Jul 12 21:38:46 2019 +0200 +++ b/test/jdk/java/util/EnumSet/BogusEnumSet.java Sat Jul 13 10:43:45 2019 +0000 @@ -32,11 +32,11 @@ public class BogusEnumSet { public static void main(String[] args) throws Throwable { - // This test depends on the current serialVersionUID of EnumSet, - // which may change if the EnumSet class is modified. - // The current value is -2409567991088730183L = 0xde8f7eadb5012fb9L - // If the value changes, it will have to be patched into the - // serialized byte stream below at the location noted. + // This test tries to deserialize a bogus stream produced with + // hypothetical EnumSet without a writeReplace() method - i.e. + // not using serialization proxy pattern. It tests that such + // stream is not accepted as valid stream - the EnumSet class + // declares a readObject() method which throws exception. byte[] serializedForm = { (byte)0xac, (byte)0xed, 0x0, 0x5, 0x73, 0x72, 0x0, 0x18, 0x6a, 0x61, 0x76, 0x61, 0x2e, 0x75, 0x74, 0x69, @@ -45,10 +45,9 @@ 0x7e, (byte)0xb0, (byte)0xd0, 0x7e, 0x2, 0x0, 0x1, 0x4a, 0x0, 0x8, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x78, 0x72, 0x0, 0x11, 0x6a, 0x61, 0x76, 0x61, 0x2e, 0x75, 0x74, 0x69, - 0x6c, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x74, - // EnumSet's serialVersionUID is the following eight bytes (big-endian) - (byte)0xde, (byte)0x8f, 0x7e, (byte)0xad, (byte)0xb5, (byte)0x01, 0x2f, (byte)0xb9, - 0x2, 0x0, 0x2, 0x4c, 0x0, 0xb, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x6c, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x74, 0xe, + 0x3, 0x21, 0x6a, (byte)0xcd, (byte)0x8c, 0x29, (byte)0xdd, 0x2, + 0x0, 0x2, 0x4c, 0x0, 0xb, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x74, 0x0, 0x11, 0x4c, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x3b, 0x5b, 0x0, 0x8, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, diff -r 7e04ccc598e0 -r 7fd4446c02ee test/jdk/java/util/EnumSet/EnumSetClassSerialization.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/util/EnumSet/EnumSetClassSerialization.java Sat Jul 13 10:43:45 2019 +0000 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8227368 + * @summary Test deserialization of a stream containing EnumSet.class object + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EnumSet; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class EnumSetClassSerialization { + + public static void main(String[] args) throws Exception { + // EnumSet.class object serialized with JDK 8 + int[] bytes = { + 0xac, 0xed, 0x00, 0x05, 0x76, 0x72, 0x00, 0x11, 0x6a, 0x61, 0x76, 0x61, 0x2e, 0x75, 0x74, 0x69, + 0x6c, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x74, 0x0e, 0x03, 0x21, 0x6a, 0xcd, 0x8c, 0x29, + 0xdd, 0x02, 0x00, 0x02, 0x4c, 0x00, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x74, 0x00, 0x11, 0x4c, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x3b, 0x5b, 0x00, 0x08, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, + 0x65, 0x74, 0x00, 0x11, 0x5b, 0x4c, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, + 0x45, 0x6e, 0x75, 0x6d, 0x3b, 0x78, 0x70 + }; + + InputStream in = new InputStream() { + int i = 0; + + @Override + public int read() { + return i < bytes.length ? bytes[i++] & 0xFF : -1; + } + }; + ObjectInputStream ois = new ObjectInputStream(in); + + Object res = ois.readObject(); + + if (res != EnumSet.class) { + throw new AssertionError( + "Expected: " + EnumSet.class + ", got: " + res); + } + } + + /** + * This class can be used to print out lines that constitute + * the 'bytes' variable initializer in the test. + */ + public static class Serializer { + public static void main(String[] args) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(EnumSet.class); + oos.close(); + byte[] bytes = baos.toByteArray(); + int bpl = 16; + System.out.print( + IntStream + .range(0, (bytes.length + bpl - 1) / bpl) + .mapToObj(i -> IntStream + .range( + i * bpl, + Math.min(i * bpl + bpl, bytes.length) + ) + .mapToObj(ii -> { + String s = Integer.toHexString(bytes[ii] & 0xFF); + return s.length() == 1 ? "0x0" + s : "0x" + s; + }) + .collect(Collectors.joining(", ")) + ) + .collect(Collectors.joining(",\n ", "int[] bytes = {\n ", "\n};")) + ); + } + } +}