diff --git a/src/share/classes/java/io/ObjectInputStream.java b/src/share/classes/java/io/ObjectInputStream.java index ebdd09af12dee76b05eb9ca69c6108524297343e..d95b96253ef002b5a520e1f0b22845ce4f8ff1ec 100644 --- a/src/share/classes/java/io/ObjectInputStream.java +++ b/src/share/classes/java/io/ObjectInputStream.java @@ -43,9 +43,9 @@ import java.util.concurrent.ConcurrentMap; import static java.io.ObjectStreamClass.processQueue; +import sun.misc.SharedSecrets; import sun.misc.ObjectInputFilter; import sun.misc.ObjectStreamClassValidator; -import sun.misc.SharedSecrets; import sun.reflect.misc.ReflectUtil; import sun.misc.JavaOISAccess; import sun.util.logging.PlatformLogger; @@ -254,6 +254,12 @@ public class ObjectInputStream public ObjectInputFilter getObjectInputFilter(ObjectInputStream stream) { return stream.getInternalObjectInputFilter(); } + + public void checkArray(ObjectInputStream stream, Class arrayType, int arrayLength) + throws InvalidClassException + { + stream.checkArray(arrayType, arrayLength); + } }); } @@ -1256,6 +1262,33 @@ public class ObjectInputStream } } + /** + * Checks the given array type and length to ensure that creation of such + * an array is permitted by this ObjectInputStream. The arrayType argument + * must represent an actual array type. + * + * This private method is called via SharedSecrets. + * + * @param arrayType the array type + * @param arrayLength the array length + * @throws NullPointerException if arrayType is null + * @throws IllegalArgumentException if arrayType isn't actually an array type + * @throws NegativeArraySizeException if arrayLength is negative + * @throws InvalidClassException if the filter rejects creation + */ + private void checkArray(Class arrayType, int arrayLength) throws InvalidClassException { + Objects.requireNonNull(arrayType); + if (! arrayType.isArray()) { + throw new IllegalArgumentException("not an array type"); + } + + if (arrayLength < 0) { + throw new NegativeArraySizeException(); + } + + filterCheck(arrayType, arrayLength); + } + /** * Provide access to the persistent fields read from the input stream. */ diff --git a/src/share/classes/java/util/ArrayDeque.java b/src/share/classes/java/util/ArrayDeque.java index 9e77f6dba5fbb9686f511690ebb4da73ce696eee..256379bd9fb76ef5e0cb76a75c2eef16a6730f8b 100644 --- a/src/share/classes/java/util/ArrayDeque.java +++ b/src/share/classes/java/util/ArrayDeque.java @@ -36,6 +36,7 @@ package java.util; import java.io.Serializable; import java.util.function.Consumer; +import sun.misc.SharedSecrets; /** * Resizable-array implementation of the {@link Deque} interface. Array @@ -118,12 +119,7 @@ public class ArrayDeque extends AbstractCollection // ****** Array allocation and resizing utilities ****** - /** - * Allocates empty array to hold the given number of elements. - * - * @param numElements the number of elements to hold - */ - private void allocateElements(int numElements) { + private static int calculateSize(int numElements) { int initialCapacity = MIN_INITIAL_CAPACITY; // Find the best power of two to hold elements. // Tests "<=" because arrays aren't kept full. @@ -139,7 +135,16 @@ public class ArrayDeque extends AbstractCollection if (initialCapacity < 0) // Too many elements, must back off initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements } - elements = new Object[initialCapacity]; + return initialCapacity; + } + + /** + * Allocates empty array to hold the given number of elements. + * + * @param numElements the number of elements to hold + */ + private void allocateElements(int numElements) { + elements = new Object[calculateSize(numElements)]; } /** @@ -879,6 +884,8 @@ public class ArrayDeque extends AbstractCollection // Read in size and allocate array int size = s.readInt(); + int capacity = calculateSize(size); + SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity); allocateElements(size); head = 0; tail = size; diff --git a/src/share/classes/java/util/ArrayList.java b/src/share/classes/java/util/ArrayList.java index 925cc180eb9714a383b995e8ffb397a420226174..4c4a0e2b37a1c71da80a6c8c490846adbacc913d 100644 --- a/src/share/classes/java/util/ArrayList.java +++ b/src/share/classes/java/util/ArrayList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, 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 @@ -28,6 +28,7 @@ package java.util; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; +import sun.misc.SharedSecrets; /** * Resizable-array implementation of the List interface. Implements @@ -219,12 +220,15 @@ public class ArrayList extends AbstractList } } - private void ensureCapacityInternal(int minCapacity) { + private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { - minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); + return Math.max(DEFAULT_CAPACITY, minCapacity); + } + return minCapacity; } - ensureExplicitCapacity(minCapacity); + private void ensureCapacityInternal(int minCapacity) { + ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) { @@ -783,6 +787,8 @@ public class ArrayList extends AbstractList if (size > 0) { // be like clone(), allocate array based upon size not capacity + int capacity = calculateCapacity(elementData, size); + SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity); ensureCapacityInternal(size); Object[] a = elementData; diff --git a/src/share/classes/java/util/HashMap.java b/src/share/classes/java/util/HashMap.java index eeb11f03c1c46037c1d8fd67ad4730964c1a16b2..15f898a91f64fd1db6b07c50b347ce535783c379 100644 --- a/src/share/classes/java/util/HashMap.java +++ b/src/share/classes/java/util/HashMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, 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 @@ -34,6 +34,7 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; +import sun.misc.SharedSecrets; /** * Hash table based implementation of the Map interface. This @@ -1392,6 +1393,10 @@ public class HashMap extends AbstractMap float ft = (float)cap * lf; threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ? (int)ft : Integer.MAX_VALUE); + + // Check Map.Entry[].class since it's the nearest public type to + // what we're actually creating. + SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap); @SuppressWarnings({"rawtypes","unchecked"}) Node[] tab = (Node[])new Node[cap]; table = tab; diff --git a/src/share/classes/java/util/HashSet.java b/src/share/classes/java/util/HashSet.java index f9b09ee4c83458cef27dd0a83da82650ef812e25..54a526c8f17a1688709bde18465a9471e12f89d5 100644 --- a/src/share/classes/java/util/HashSet.java +++ b/src/share/classes/java/util/HashSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, 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 @@ -26,6 +26,7 @@ package java.util; import java.io.InvalidObjectException; +import sun.misc.SharedSecrets; /** * This class implements the Set interface, backed by a hash table @@ -316,12 +317,19 @@ public class HashSet throw new InvalidObjectException("Illegal size: " + size); } - // Set the capacity according to the size and load factor ensuring that // the HashMap is at least 25% full but clamping to maximum capacity. capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f), HashMap.MAXIMUM_CAPACITY); + // Constructing the backing map will lazily create an array when the first element is + // added, so check it before construction. Call HashMap.tableSizeFor to compute the + // actual allocation size. Check Map.Entry[].class since it's the nearest public type to + // what is actually created. + + SharedSecrets.getJavaOISAccess() + .checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity)); + // Create backing HashMap map = (((HashSet)this) instanceof LinkedHashSet ? new LinkedHashMap(capacity, loadFactor) : diff --git a/src/share/classes/java/util/Hashtable.java b/src/share/classes/java/util/Hashtable.java index c7e92515d197446315ad49cdc6b80d5b877a9185..b22bab8da1c1ea5205921d7118f77f464d946bbb 100644 --- a/src/share/classes/java/util/Hashtable.java +++ b/src/share/classes/java/util/Hashtable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2017, 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 @@ -30,6 +30,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.BiFunction; +import sun.misc.SharedSecrets; /** * This class implements a hash table, which maps keys to values. Any @@ -1192,6 +1193,10 @@ public class Hashtable if (length > elements && (length & 1) == 0) length--; length = Math.min(length, origlength); + + // Check Map.Entry[].class since it's the nearest public type to + // what we're actually creating. + SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, length); table = new Entry[length]; threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1); count = 0; diff --git a/src/share/classes/java/util/IdentityHashMap.java b/src/share/classes/java/util/IdentityHashMap.java index 9dc0c26c13f7ed56032129bbef91691899f7b8c1..89db44817f284bd2a10b6bff32ff7e6684ffe1be 100644 --- a/src/share/classes/java/util/IdentityHashMap.java +++ b/src/share/classes/java/util/IdentityHashMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, 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 @@ -29,6 +29,7 @@ import java.lang.reflect.Array; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; +import sun.misc.SharedSecrets; /** * This class implements the Map interface with a hash table, using @@ -1304,7 +1305,9 @@ public class IdentityHashMap if (size < 0) throw new java.io.StreamCorruptedException ("Illegal mappings count: " + size); - init(capacity(size)); + int cap = capacity(size); + SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, cap); + init(cap); // Read the keys and values, and put the mappings in the table for (int i=0; i extends AbstractQueue // Read in (and discard) array length s.readInt(); + SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size); queue = new Object[size]; // Read in all elements. diff --git a/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java index 162ad3b51d14eb9044272af3337db7031205e8e5..1f310ef48850e996f66582ec49915e222fee866f 100644 --- a/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java +++ b/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java @@ -50,6 +50,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; +import sun.misc.SharedSecrets; /** * A thread-safe variant of {@link java.util.ArrayList} in which all mutative @@ -989,6 +990,7 @@ public class CopyOnWriteArrayList // Read in array length and allocate array int len = s.readInt(); + SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, len); Object[] elements = new Object[len]; // Read in all elements in the proper order. diff --git a/src/share/classes/sun/misc/JavaOISAccess.java b/src/share/classes/sun/misc/JavaOISAccess.java index 8be96eb7c904d1c3e4c3ed8dd37c1a1a8f6ff211..c51f2684d49a1f26b39df441a7983533afd81cf0 100644 --- a/src/share/classes/sun/misc/JavaOISAccess.java +++ b/src/share/classes/sun/misc/JavaOISAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -25,9 +25,12 @@ package sun.misc; +import java.io.InvalidClassException; import java.io.ObjectInputStream; public interface JavaOISAccess { void setObjectInputFilter(ObjectInputStream stream, ObjectInputFilter filter); ObjectInputFilter getObjectInputFilter(ObjectInputStream stream); + void checkArray(ObjectInputStream stream, Class arrayType, int arrayLength) + throws InvalidClassException; } diff --git a/test/java/io/Serializable/serialFilter/SerialFilterTest.java b/test/java/io/Serializable/serialFilter/SerialFilterTest.java index 479b849217ea70bba1bde61a4cc58cd2c2edf1e2..0f6d50ffe33c3811386739952dd10e23908ade30 100644 --- a/test/java/io/Serializable/serialFilter/SerialFilterTest.java +++ b/test/java/io/Serializable/serialFilter/SerialFilterTest.java @@ -35,9 +35,11 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Hashtable; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.LongAdder; import sun.misc.ObjectInputFilter; @@ -154,6 +156,11 @@ public class SerialFilterTest implements Serializable { interfaces, (p, m, args) -> p); Runnable runnable = (Runnable & Serializable) SerialFilterTest::noop; + + List> classList = new ArrayList<>(); + classList.add(HashSet.class); + classList.addAll(Collections.nCopies(21, Map.Entry[].class)); + Object[][] objects = { { null, 0, -1, 0, 0, 0, Arrays.asList()}, // no callback, no values @@ -173,8 +180,7 @@ public class SerialFilterTest implements Serializable { objArray.getClass(), SerialFilterTest.class, java.lang.invoke.SerializedLambda.class)}, - { deepHashSet(10), 48, -1, 50, 11, 619, - Arrays.asList(HashSet.class)}, + { deepHashSet(10), 69, 4, 50, 11, 619, classList }, { proxy.getClass(), 3, -1, 2, 2, 112, Arrays.asList(Runnable.class, java.lang.reflect.Proxy.class,