author | rriggs |
Fri, 11 Oct 2019 13:11:56 -0400 | |
changeset 58565 | baa5969ecf34 |
parent 47423 | 4fc2a4a29f3d |
permissions | -rw-r--r-- |
41230 | 1 |
/* |
58565
baa5969ecf34
8231427: Warning cleanup in tests of java.io.Serializable
rriggs
parents:
47423
diff
changeset
|
2 |
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. |
41230 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
7 |
* published by the Free Software Foundation. |
|
8 |
* |
|
9 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
13 |
* accompanied this code). |
|
14 |
* |
|
15 |
* You should have received a copy of the GNU General Public License version |
|
16 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
17 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 |
* |
|
19 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 |
* or visit www.oracle.com if you need additional information or have any |
|
21 |
* questions. |
|
22 |
*/ |
|
23 |
||
24 |
import java.io.ByteArrayInputStream; |
|
25 |
import java.io.ByteArrayOutputStream; |
|
26 |
import java.io.EOFException; |
|
27 |
import java.io.IOException; |
|
28 |
import java.io.InvalidClassException; |
|
44756 | 29 |
import java.io.ObjectInputFilter; |
41230 | 30 |
import java.io.ObjectInputStream; |
31 |
import java.io.ObjectOutputStream; |
|
32 |
import java.io.Serializable; |
|
33 |
import java.lang.invoke.SerializedLambda; |
|
34 |
import java.lang.reflect.Constructor; |
|
35 |
import java.lang.reflect.InvocationTargetException; |
|
36 |
import java.lang.reflect.Proxy; |
|
44756 | 37 |
import java.util.ArrayList; |
41230 | 38 |
import java.util.Arrays; |
47423 | 39 |
import java.util.Collections; |
41230 | 40 |
import java.util.HashSet; |
41 |
import java.util.Hashtable; |
|
44756 | 42 |
import java.util.List; |
47423 | 43 |
import java.util.Map; |
41230 | 44 |
import java.util.concurrent.atomic.LongAdder; |
45 |
||
41888
dc533ef5583a
8169055: [TESTBUG] java/io/Serializable/serialFilter/ tests have undeclared dependency on java.compiler module
rriggs
parents:
41230
diff
changeset
|
46 |
import javax.net.ssl.SSLEngineResult; |
41230 | 47 |
|
48 |
import org.testng.Assert; |
|
49 |
import org.testng.annotations.Test; |
|
50 |
import org.testng.annotations.DataProvider; |
|
51 |
||
52 |
/* @test |
|
53 |
* @build SerialFilterTest |
|
54 |
* @run testng/othervm SerialFilterTest |
|
55 |
* |
|
56 |
* @summary Test ObjectInputFilters |
|
57 |
*/ |
|
58 |
@Test |
|
59 |
public class SerialFilterTest implements Serializable { |
|
60 |
||
61 |
private static final long serialVersionUID = -6999613679881262446L; |
|
62 |
||
63 |
/** |
|
64 |
* Enable three arg lambda. |
|
65 |
* @param <T> The pattern |
|
66 |
* @param <U> The test object |
|
67 |
* @param <V> Boolean for if the filter should allow or reject |
|
68 |
*/ |
|
69 |
interface TriConsumer< T, U, V> { |
|
70 |
void accept(T t, U u, V v); |
|
71 |
} |
|
72 |
||
73 |
/** |
|
74 |
* Misc object to use that should always be accepted. |
|
75 |
*/ |
|
76 |
private static final Object otherObject = Integer.valueOf(0); |
|
77 |
||
78 |
/** |
|
79 |
* DataProvider for the individual patterns to test. |
|
80 |
* Expand the patterns into cases for each of the Std and Compatibility APIs. |
|
81 |
* @return an array of arrays of the parameters including factories |
|
82 |
*/ |
|
83 |
@DataProvider(name="Patterns") |
|
84 |
static Object[][] patterns() { |
|
85 |
Object[][] patterns = new Object[][]{ |
|
86 |
{"java.util.Hashtable"}, |
|
87 |
{"java.util.Hash*"}, |
|
41888
dc533ef5583a
8169055: [TESTBUG] java/io/Serializable/serialFilter/ tests have undeclared dependency on java.compiler module
rriggs
parents:
41230
diff
changeset
|
88 |
{"javax.net.ssl.*"}, |
dc533ef5583a
8169055: [TESTBUG] java/io/Serializable/serialFilter/ tests have undeclared dependency on java.compiler module
rriggs
parents:
41230
diff
changeset
|
89 |
{"javax.net.**"}, |
41230 | 90 |
{"*"}, |
91 |
{"maxarray=47"}, |
|
92 |
{"maxdepth=5"}, |
|
93 |
{"maxrefs=10"}, |
|
94 |
{"maxbytes=100"}, |
|
95 |
{"maxbytes=72"}, |
|
96 |
{"maxbytes=+1024"}, |
|
97 |
{"java.base/java.util.Hashtable"}, |
|
98 |
}; |
|
99 |
return patterns; |
|
100 |
} |
|
101 |
||
102 |
@DataProvider(name="InvalidPatterns") |
|
103 |
static Object[][] invalidPatterns() { |
|
104 |
return new Object [][] { |
|
105 |
{".*"}, |
|
106 |
{".**"}, |
|
107 |
{"!"}, |
|
108 |
{"/java.util.Hashtable"}, |
|
109 |
{"java.base/"}, |
|
110 |
{"/"}, |
|
111 |
}; |
|
112 |
} |
|
113 |
||
114 |
@DataProvider(name="Limits") |
|
115 |
static Object[][] limits() { |
|
116 |
// The numbers are arbitrary > 1 |
|
42446
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
117 |
return new Object[][] { |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
118 |
{"maxrefs", 1}, // 0 is tested as n-1 |
41230 | 119 |
{"maxrefs", 10}, |
120 |
{"maxdepth", 5}, |
|
121 |
{"maxbytes", 100}, |
|
122 |
{"maxarray", 16}, |
|
42446
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
123 |
{"maxbytes", Long.MAX_VALUE}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
124 |
}; |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
125 |
} |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
126 |
|
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
127 |
@DataProvider(name="InvalidLimits") |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
128 |
static Object[][] invalidLimits() { |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
129 |
return new Object[][] { |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
130 |
{"maxrefs=-1"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
131 |
{"maxdepth=-1"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
132 |
{"maxbytes=-1"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
133 |
{"maxarray=-1"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
134 |
{"xyz=0"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
135 |
{"xyz=-1"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
136 |
{"maxrefs=0xabc"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
137 |
{"maxrefs=abc"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
138 |
{"maxrefs="}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
139 |
{"maxrefs=+"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
140 |
{"maxbytes=-1"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
141 |
{"maxbytes=9223372036854775808"}, |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
142 |
{"maxbytes=-9223372036854775807"}, |
41230 | 143 |
}; |
144 |
} |
|
145 |
||
146 |
/** |
|
147 |
* DataProvider of individual objects. Used to check the information |
|
148 |
* available to the filter. |
|
149 |
* @return Arrays of parameters with objects |
|
150 |
*/ |
|
151 |
@DataProvider(name="Objects") |
|
152 |
static Object[][] objects() { |
|
153 |
byte[] byteArray = new byte[0]; |
|
154 |
Object[] objArray = new Object[7]; |
|
155 |
objArray[objArray.length - 1] = objArray; |
|
156 |
||
157 |
Class<?> serClass = null; |
|
158 |
String className = "java.util.concurrent.atomic.LongAdder$SerializationProxy"; |
|
159 |
try { |
|
160 |
serClass = Class.forName(className); |
|
161 |
} catch (Exception e) { |
|
162 |
Assert.fail("missing class: " + className, e); |
|
163 |
} |
|
164 |
||
165 |
Class<?>[] interfaces = {Runnable.class}; |
|
166 |
Runnable proxy = (Runnable) Proxy.newProxyInstance(null, |
|
167 |
interfaces, (p, m, args) -> p); |
|
168 |
||
169 |
Runnable runnable = (Runnable & Serializable) SerialFilterTest::noop; |
|
47423 | 170 |
|
171 |
List<Class<?>> classList = new ArrayList<>(); |
|
172 |
classList.add(HashSet.class); |
|
173 |
classList.addAll(Collections.nCopies(21, Map.Entry[].class)); |
|
174 |
||
41230 | 175 |
Object[][] objects = { |
176 |
{ null, 0, -1, 0, 0, 0, |
|
44756 | 177 |
Arrays.asList()}, // no callback, no values |
178 |
{ objArray, 3, 7, 9, 2, 55, |
|
179 |
Arrays.asList(objArray.getClass(), objArray.getClass())}, |
|
180 |
{ Object[].class, 1, -1, 1, 1, 38, |
|
181 |
Arrays.asList(Object[].class)}, |
|
182 |
{ new SerialFilterTest(), 1, -1, 1, 1, 35, |
|
183 |
Arrays.asList(SerialFilterTest.class)}, |
|
184 |
{ new LongAdder(), 2, -1, 2, 1, 93, |
|
185 |
Arrays.asList(serClass, LongAdder.class)}, |
|
186 |
{ new byte[14], 2, 14, 2, 1, 27, |
|
187 |
Arrays.asList(byteArray.getClass(), byteArray.getClass())}, |
|
188 |
{ runnable, 13, 0, 13, 2, 514, |
|
189 |
Arrays.asList(java.lang.invoke.SerializedLambda.class, |
|
190 |
objArray.getClass(), |
|
191 |
objArray.getClass(), |
|
41230 | 192 |
SerialFilterTest.class, |
44756 | 193 |
java.lang.invoke.SerializedLambda.class)}, |
47423 | 194 |
{ deepHashSet(10), 69, 4, 50, 11, 619, classList }, |
44756 | 195 |
{ proxy.getClass(), 3, -1, 2, 2, 112, |
196 |
Arrays.asList(Runnable.class, |
|
197 |
java.lang.reflect.Proxy.class, |
|
198 |
java.lang.reflect.Proxy.class)}, |
|
199 |
{ new F(), 6, -1, 6, 6, 202, |
|
200 |
Arrays.asList(F.class, E.class, D.class, |
|
201 |
C.class, B.class, A.class)}, |
|
202 |
||
41230 | 203 |
}; |
204 |
return objects; |
|
205 |
} |
|
206 |
||
207 |
@DataProvider(name="Arrays") |
|
208 |
static Object[][] arrays() { |
|
209 |
return new Object[][]{ |
|
210 |
{new Object[16], 16}, |
|
211 |
{new boolean[16], 16}, |
|
212 |
{new byte[16], 16}, |
|
213 |
{new char[16], 16}, |
|
214 |
{new int[16], 16}, |
|
215 |
{new long[16], 16}, |
|
216 |
{new short[16], 16}, |
|
217 |
{new float[16], 16}, |
|
218 |
{new double[16], 16}, |
|
219 |
}; |
|
220 |
} |
|
221 |
||
222 |
||
223 |
/** |
|
224 |
* Test each object and verify the classes identified by the filter, |
|
225 |
* the count of calls to the filter, the max array size, max refs, max depth, |
|
226 |
* max bytes. |
|
227 |
* This test ignores/is not dependent on the global filter settings. |
|
228 |
* |
|
229 |
* @param object a Serializable object |
|
230 |
* @param count the expected count of calls to the filter |
|
231 |
* @param maxArray the maximum array size |
|
232 |
* @param maxRefs the maximum references |
|
233 |
* @param maxDepth the maximum depth |
|
234 |
* @param maxBytes the maximum stream size |
|
235 |
* @param classes the expected (unique) classes |
|
236 |
* @throws IOException |
|
237 |
*/ |
|
238 |
@Test(dataProvider="Objects") |
|
239 |
public static void t1(Object object, |
|
240 |
long count, long maxArray, long maxRefs, long maxDepth, long maxBytes, |
|
44756 | 241 |
List<Class<?>> classes) throws IOException { |
41230 | 242 |
byte[] bytes = writeObjects(object); |
243 |
Validator validator = new Validator(); |
|
244 |
validate(bytes, validator); |
|
245 |
System.out.printf("v: %s%n", validator); |
|
44756 | 246 |
|
41230 | 247 |
Assert.assertEquals(validator.count, count, "callback count wrong"); |
248 |
Assert.assertEquals(validator.classes, classes, "classes mismatch"); |
|
249 |
Assert.assertEquals(validator.maxArray, maxArray, "maxArray mismatch"); |
|
250 |
Assert.assertEquals(validator.maxRefs, maxRefs, "maxRefs wrong"); |
|
251 |
Assert.assertEquals(validator.maxDepth, maxDepth, "depth wrong"); |
|
252 |
Assert.assertEquals(validator.maxBytes, maxBytes, "maxBytes wrong"); |
|
253 |
} |
|
254 |
||
255 |
/** |
|
256 |
* Test each pattern with an appropriate object. |
|
257 |
* A filter is created from the pattern and used to serialize and |
|
258 |
* deserialize a generated object with both the positive and negative case. |
|
259 |
* This test ignores/is not dependent on the global filter settings. |
|
260 |
* |
|
261 |
* @param pattern a pattern |
|
262 |
*/ |
|
263 |
@Test(dataProvider="Patterns") |
|
264 |
static void testPatterns(String pattern) { |
|
265 |
evalPattern(pattern, (p, o, neg) -> testPatterns(p, o, neg)); |
|
266 |
} |
|
267 |
||
268 |
/** |
|
269 |
* Test that the filter on a OIS can be set only on a fresh OIS, |
|
270 |
* before deserializing any objects. |
|
271 |
* This test is agnostic the global filter being set or not. |
|
272 |
*/ |
|
273 |
@Test |
|
274 |
static void nonResettableFilter() { |
|
275 |
Validator validator1 = new Validator(); |
|
276 |
Validator validator2 = new Validator(); |
|
277 |
||
278 |
try { |
|
279 |
byte[] bytes = writeObjects("text1"); // an object |
|
280 |
||
281 |
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); |
|
282 |
ObjectInputStream ois = new ObjectInputStream(bais)) { |
|
283 |
// Check the initial filter is the global filter; may be null |
|
284 |
ObjectInputFilter global = ObjectInputFilter.Config.getSerialFilter(); |
|
285 |
ObjectInputFilter initial = ois.getObjectInputFilter(); |
|
286 |
Assert.assertEquals(global, initial, "initial filter should be the global filter"); |
|
287 |
||
288 |
// Check if it can be set to null |
|
289 |
ois.setObjectInputFilter(null); |
|
290 |
ObjectInputFilter filter = ois.getObjectInputFilter(); |
|
291 |
Assert.assertNull(filter, "set to null should be null"); |
|
292 |
||
293 |
ois.setObjectInputFilter(validator1); |
|
294 |
Object o = ois.readObject(); |
|
295 |
try { |
|
296 |
ois.setObjectInputFilter(validator2); |
|
297 |
Assert.fail("Should not be able to set filter twice"); |
|
298 |
} catch (IllegalStateException ise) { |
|
299 |
// success, the exception was expected |
|
300 |
} |
|
301 |
} catch (EOFException eof) { |
|
302 |
Assert.fail("Should not reach end-of-file", eof); |
|
303 |
} catch (ClassNotFoundException cnf) { |
|
304 |
Assert.fail("Deserializing", cnf); |
|
305 |
} |
|
306 |
} catch (IOException ex) { |
|
307 |
Assert.fail("Unexpected IOException", ex); |
|
308 |
} |
|
309 |
} |
|
310 |
||
311 |
/** |
|
312 |
* Test that if an Objects readReadResolve method returns an array |
|
313 |
* that the callback to the filter includes the proper array length. |
|
314 |
* @throws IOException if an error occurs |
|
315 |
*/ |
|
316 |
@Test(dataProvider="Arrays") |
|
317 |
static void testReadResolveToArray(Object array, int length) throws IOException { |
|
318 |
ReadResolveToArray object = new ReadResolveToArray(array, length); |
|
319 |
byte[] bytes = writeObjects(object); |
|
320 |
Object o = validate(bytes, object); // the object is its own filter |
|
321 |
Assert.assertEquals(o.getClass(), array.getClass(), "Filter not called with the array"); |
|
322 |
} |
|
323 |
||
324 |
||
325 |
/** |
|
326 |
* Test repeated limits use the last value. |
|
327 |
* Construct a filter with the limit and the limit repeated -1. |
|
328 |
* Invoke the filter with the limit to make sure it is rejected. |
|
329 |
* Invoke the filter with the limit -1 to make sure it is accepted. |
|
330 |
* @param name the name of the limit to test |
|
331 |
* @param value a test value |
|
332 |
*/ |
|
333 |
@Test(dataProvider="Limits") |
|
42446
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
334 |
static void testLimits(String name, long value) { |
41230 | 335 |
Class<?> arrayClass = new int[0].getClass(); |
336 |
String pattern = String.format("%s=%d;%s=%d", name, value, name, value - 1); |
|
337 |
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); |
|
338 |
Assert.assertEquals( |
|
339 |
filter.checkInput(new FilterValues(arrayClass, value, value, value, value)), |
|
340 |
ObjectInputFilter.Status.REJECTED, |
|
341 |
"last limit value not used: " + filter); |
|
342 |
Assert.assertEquals( |
|
343 |
filter.checkInput(new FilterValues(arrayClass, value-1, value-1, value-1, value-1)), |
|
344 |
ObjectInputFilter.Status.UNDECIDED, |
|
345 |
"last limit value not used: " + filter); |
|
346 |
} |
|
347 |
||
348 |
/** |
|
42446
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
349 |
* Test invalid limits. |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
350 |
* Construct a filter with the limit, it should throw IllegalArgumentException. |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
351 |
* @param pattern a pattern to test |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
352 |
*/ |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
353 |
@Test(dataProvider="InvalidLimits", expectedExceptions=java.lang.IllegalArgumentException.class) |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
354 |
static void testInvalidLimits(String pattern) { |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
355 |
try { |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
356 |
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
357 |
} catch (IllegalArgumentException iae) { |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
358 |
System.out.printf(" success exception: %s%n", iae); |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
359 |
throw iae; |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
360 |
} |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
361 |
} |
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
362 |
|
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
363 |
/** |
41230 | 364 |
* Test that returning null from a filter causes deserialization to fail. |
365 |
*/ |
|
366 |
@Test(expectedExceptions=InvalidClassException.class) |
|
367 |
static void testNullStatus() throws IOException { |
|
368 |
byte[] bytes = writeObjects(0); // an Integer |
|
369 |
try { |
|
370 |
Object o = validate(bytes, new ObjectInputFilter() { |
|
371 |
public ObjectInputFilter.Status checkInput(ObjectInputFilter.FilterInfo f) { |
|
372 |
return null; |
|
373 |
} |
|
374 |
}); |
|
375 |
} catch (InvalidClassException ice) { |
|
42446
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
376 |
System.out.printf(" success exception: %s%n", ice); |
41230 | 377 |
throw ice; |
378 |
} |
|
379 |
} |
|
380 |
||
381 |
/** |
|
382 |
* Verify that malformed patterns throw IAE. |
|
383 |
* @param pattern pattern from the data source |
|
384 |
*/ |
|
385 |
@Test(dataProvider="InvalidPatterns", expectedExceptions=IllegalArgumentException.class) |
|
386 |
static void testInvalidPatterns(String pattern) { |
|
387 |
try { |
|
388 |
ObjectInputFilter.Config.createFilter(pattern); |
|
389 |
} catch (IllegalArgumentException iae) { |
|
390 |
System.out.printf(" success exception: %s%n", iae); |
|
391 |
throw iae; |
|
392 |
} |
|
393 |
} |
|
394 |
||
395 |
/** |
|
396 |
* Test that Config.create returns null if the argument does not contain any patterns or limits. |
|
397 |
*/ |
|
398 |
@Test() |
|
399 |
static void testEmptyPattern() { |
|
400 |
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(""); |
|
401 |
Assert.assertNull(filter, "empty pattern did not return null"); |
|
402 |
||
403 |
filter = ObjectInputFilter.Config.createFilter(";;;;"); |
|
404 |
Assert.assertNull(filter, "pattern with only delimiters did not return null"); |
|
405 |
} |
|
406 |
||
407 |
/** |
|
408 |
* Read objects from the serialized stream, validated with the filter. |
|
409 |
* |
|
410 |
* @param bytes a byte array to read objects from |
|
411 |
* @param filter the ObjectInputFilter |
|
412 |
* @return the object deserialized if any |
|
413 |
* @throws IOException can be thrown |
|
414 |
*/ |
|
415 |
static Object validate(byte[] bytes, |
|
416 |
ObjectInputFilter filter) throws IOException { |
|
417 |
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); |
|
418 |
ObjectInputStream ois = new ObjectInputStream(bais)) { |
|
419 |
ois.setObjectInputFilter(filter); |
|
420 |
||
421 |
Object o = ois.readObject(); |
|
422 |
return o; |
|
423 |
} catch (EOFException eof) { |
|
424 |
// normal completion |
|
425 |
} catch (ClassNotFoundException cnf) { |
|
426 |
Assert.fail("Deserializing", cnf); |
|
427 |
} |
|
428 |
return null; |
|
429 |
} |
|
430 |
||
431 |
/** |
|
432 |
* Write objects and return a byte array with the bytes. |
|
433 |
* |
|
434 |
* @param objects zero or more objects to serialize |
|
435 |
* @return the byte array of the serialized objects |
|
436 |
* @throws IOException if an exception occurs |
|
437 |
*/ |
|
438 |
static byte[] writeObjects(Object... objects) throws IOException { |
|
439 |
byte[] bytes; |
|
440 |
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
441 |
ObjectOutputStream oos = new ObjectOutputStream(baos)) { |
|
442 |
for (Object o : objects) { |
|
443 |
oos.writeObject(o); |
|
444 |
} |
|
445 |
bytes = baos.toByteArray(); |
|
446 |
} |
|
447 |
return bytes; |
|
448 |
} |
|
449 |
||
450 |
/** |
|
451 |
* A filter that accumulates information about the checkInput callbacks |
|
452 |
* that can be checked after readObject completes. |
|
453 |
*/ |
|
454 |
static class Validator implements ObjectInputFilter { |
|
455 |
long count; // Count of calls to checkInput |
|
44756 | 456 |
List<Class<?>> classes = new ArrayList<>(); |
41230 | 457 |
long maxArray = -1; |
458 |
long maxRefs; |
|
459 |
long maxDepth; |
|
460 |
long maxBytes; |
|
461 |
||
462 |
Validator() { |
|
463 |
} |
|
464 |
||
465 |
@Override |
|
466 |
public ObjectInputFilter.Status checkInput(FilterInfo filter) { |
|
44756 | 467 |
Class<?> serialClass = filter.serialClass(); |
468 |
System.out.printf(" checkInput: class: %s, arrayLen: %d, refs: %d, depth: %d, bytes; %d%n", |
|
469 |
serialClass, filter.arrayLength(), filter.references(), |
|
470 |
filter.depth(), filter.streamBytes()); |
|
41230 | 471 |
count++; |
44756 | 472 |
if (serialClass != null) { |
473 |
if (serialClass.getName().contains("$$Lambda$")) { |
|
41230 | 474 |
// TBD: proper identification of serialized Lambdas? |
475 |
// Fold the serialized Lambda into the SerializedLambda type |
|
476 |
classes.add(SerializedLambda.class); |
|
44756 | 477 |
} else if (Proxy.isProxyClass(serialClass)) { |
41230 | 478 |
classes.add(Proxy.class); |
479 |
} else { |
|
44756 | 480 |
classes.add(serialClass); |
41230 | 481 |
} |
482 |
||
483 |
} |
|
484 |
this.maxArray = Math.max(this.maxArray, filter.arrayLength()); |
|
485 |
this.maxRefs = Math.max(this.maxRefs, filter.references()); |
|
486 |
this.maxDepth = Math.max(this.maxDepth, filter.depth()); |
|
487 |
this.maxBytes = Math.max(this.maxBytes, filter.streamBytes()); |
|
488 |
return ObjectInputFilter.Status.UNDECIDED; |
|
489 |
} |
|
490 |
||
491 |
public String toString(){ |
|
492 |
return "count: " + count |
|
493 |
+ ", classes: " + classes.toString() |
|
494 |
+ ", maxArray: " + maxArray |
|
495 |
+ ", maxRefs: " + maxRefs |
|
496 |
+ ", maxDepth: " + maxDepth |
|
497 |
+ ", maxBytes: " + maxBytes; |
|
498 |
} |
|
499 |
} |
|
500 |
||
501 |
||
502 |
/** |
|
503 |
* Create a filter from a pattern and API factory, then serialize and |
|
504 |
* deserialize an object and check allowed or reject. |
|
505 |
* |
|
506 |
* @param pattern the pattern |
|
507 |
* @param object the test object |
|
508 |
* @param allowed the expected result from ObjectInputStream (exception or not) |
|
509 |
*/ |
|
510 |
static void testPatterns(String pattern, Object object, boolean allowed) { |
|
511 |
try { |
|
512 |
byte[] bytes = SerialFilterTest.writeObjects(object); |
|
513 |
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); |
|
514 |
validate(bytes, filter); |
|
515 |
Assert.assertTrue(allowed, "filter should have thrown an exception"); |
|
516 |
} catch (IllegalArgumentException iae) { |
|
517 |
Assert.fail("bad format pattern", iae); |
|
518 |
} catch (InvalidClassException ice) { |
|
519 |
Assert.assertFalse(allowed, "filter should not have thrown an exception: " + ice); |
|
520 |
} catch (IOException ioe) { |
|
521 |
Assert.fail("Unexpected IOException", ioe); |
|
522 |
} |
|
523 |
} |
|
524 |
||
525 |
/** |
|
526 |
* For a filter pattern, generate and apply a test object to the action. |
|
527 |
* @param pattern a pattern |
|
528 |
* @param action an action to perform on positive and negative cases |
|
529 |
*/ |
|
530 |
static void evalPattern(String pattern, TriConsumer<String, Object, Boolean> action) { |
|
531 |
Object o = genTestObject(pattern, true); |
|
532 |
Assert.assertNotNull(o, "success generation failed"); |
|
533 |
action.accept(pattern, o, true); |
|
534 |
||
535 |
// Test the negative pattern |
|
536 |
o = genTestObject(pattern, false); |
|
537 |
Assert.assertNotNull(o, "fail generation failed"); |
|
538 |
String negPattern = pattern.contains("=") ? pattern : "!" + pattern; |
|
539 |
action.accept(negPattern, o, false); |
|
540 |
} |
|
541 |
||
542 |
/** |
|
543 |
* Generate a test object based on the pattern. |
|
544 |
* Handles each of the forms of the pattern, wildcards, |
|
545 |
* class name, various limit forms. |
|
546 |
* @param pattern a pattern |
|
547 |
* @param allowed a boolean indicating to generate the allowed or disallowed case |
|
548 |
* @return an object or {@code null} to indicate no suitable object could be generated |
|
549 |
*/ |
|
550 |
static Object genTestObject(String pattern, boolean allowed) { |
|
551 |
if (pattern.contains("=")) { |
|
552 |
return genTestLimit(pattern, allowed); |
|
553 |
} else if (pattern.endsWith("*")) { |
|
554 |
return genTestObjectWildcard(pattern, allowed); |
|
555 |
} else { |
|
556 |
// class |
|
557 |
// isolate module name, if any |
|
558 |
int poffset = 0; |
|
559 |
int soffset = pattern.indexOf('/', poffset); |
|
560 |
String module = null; |
|
561 |
if (soffset >= 0) { |
|
562 |
poffset = soffset + 1; |
|
563 |
module = pattern.substring(0, soffset); |
|
564 |
} |
|
565 |
try { |
|
566 |
Class<?> clazz = Class.forName(pattern.substring(poffset)); |
|
567 |
Constructor<?> cons = clazz.getConstructor(); |
|
568 |
return cons.newInstance(); |
|
569 |
} catch (ClassNotFoundException ex) { |
|
570 |
Assert.fail("no such class available: " + pattern); |
|
571 |
} catch (InvocationTargetException |
|
572 |
| NoSuchMethodException |
|
573 |
| InstantiationException |
|
574 |
| IllegalAccessException ex1) { |
|
575 |
Assert.fail("newInstance: " + ex1); |
|
576 |
} |
|
577 |
} |
|
578 |
return null; |
|
579 |
} |
|
580 |
||
581 |
/** |
|
582 |
* Generate an object to be used with the various wildcard pattern forms. |
|
583 |
* Explicitly supports only specific package wildcards with specific objects. |
|
584 |
* @param pattern a wildcard pattern ending in "*" |
|
585 |
* @param allowed a boolean indicating to generate the allowed or disallowed case |
|
586 |
* @return an object within or outside the wildcard |
|
587 |
*/ |
|
588 |
static Object genTestObjectWildcard(String pattern, boolean allowed) { |
|
589 |
if (pattern.endsWith(".**")) { |
|
590 |
// package hierarchy wildcard |
|
41888
dc533ef5583a
8169055: [TESTBUG] java/io/Serializable/serialFilter/ tests have undeclared dependency on java.compiler module
rriggs
parents:
41230
diff
changeset
|
591 |
if (pattern.startsWith("javax.net.")) { |
dc533ef5583a
8169055: [TESTBUG] java/io/Serializable/serialFilter/ tests have undeclared dependency on java.compiler module
rriggs
parents:
41230
diff
changeset
|
592 |
return SSLEngineResult.Status.BUFFER_OVERFLOW; |
41230 | 593 |
} |
594 |
if (pattern.startsWith("java.")) { |
|
595 |
return 4; |
|
596 |
} |
|
597 |
if (pattern.startsWith("javax.")) { |
|
41888
dc533ef5583a
8169055: [TESTBUG] java/io/Serializable/serialFilter/ tests have undeclared dependency on java.compiler module
rriggs
parents:
41230
diff
changeset
|
598 |
return SSLEngineResult.Status.BUFFER_UNDERFLOW; |
41230 | 599 |
} |
600 |
return otherObject; |
|
601 |
} else if (pattern.endsWith(".*")) { |
|
602 |
// package wildcard |
|
41888
dc533ef5583a
8169055: [TESTBUG] java/io/Serializable/serialFilter/ tests have undeclared dependency on java.compiler module
rriggs
parents:
41230
diff
changeset
|
603 |
if (pattern.startsWith("javax.net.ssl")) { |
dc533ef5583a
8169055: [TESTBUG] java/io/Serializable/serialFilter/ tests have undeclared dependency on java.compiler module
rriggs
parents:
41230
diff
changeset
|
604 |
return SSLEngineResult.Status.BUFFER_UNDERFLOW; |
41230 | 605 |
} |
606 |
} else { |
|
607 |
// class wildcard |
|
608 |
if (pattern.equals("*")) { |
|
609 |
return otherObject; // any object will do |
|
610 |
} |
|
611 |
if (pattern.startsWith("java.util.Hash")) { |
|
612 |
return new Hashtable<String, String>(); |
|
613 |
} |
|
614 |
} |
|
615 |
Assert.fail("Object could not be generated for pattern: " |
|
616 |
+ pattern |
|
617 |
+ ", allowed: " + allowed); |
|
618 |
return null; |
|
619 |
} |
|
620 |
||
621 |
/** |
|
622 |
* Generate a limit test object for the pattern. |
|
623 |
* For positive cases, the object exactly hits the limit. |
|
624 |
* For negative cases, the object is 1 greater than the limit |
|
625 |
* @param pattern the pattern, containing "=" and a maxXXX keyword |
|
626 |
* @param allowed a boolean indicating to generate the allowed or disallowed case |
|
627 |
* @return a sitable object |
|
628 |
*/ |
|
629 |
static Object genTestLimit(String pattern, boolean allowed) { |
|
630 |
int ndx = pattern.indexOf('='); |
|
631 |
Assert.assertNotEquals(ndx, -1, "missing value in limit"); |
|
632 |
long value = Long.parseUnsignedLong(pattern.substring(ndx+1)); |
|
633 |
if (pattern.startsWith("maxdepth=")) { |
|
634 |
// Return an object with the requested depth (or 1 greater) |
|
635 |
long depth = allowed ? value : value + 1; |
|
636 |
Object[] array = new Object[1]; |
|
637 |
for (int i = 1; i < depth; i++) { |
|
638 |
Object[] n = new Object[1]; |
|
639 |
n[0] = array; |
|
640 |
array = n; |
|
641 |
} |
|
642 |
return array; |
|
643 |
} else if (pattern.startsWith("maxbytes=")) { |
|
644 |
// Return a byte array that when written to OOS creates |
|
645 |
// a stream of exactly the size requested. |
|
646 |
return genMaxBytesObject(allowed, value); |
|
647 |
} else if (pattern.startsWith("maxrefs=")) { |
|
44756 | 648 |
// 4 references to classes in addition to the array contents |
649 |
Object[] array = new Object[allowed ? (int)value - 4 : (int)value - 3]; |
|
41230 | 650 |
for (int i = 0; i < array.length; i++) { |
651 |
array[i] = otherObject; |
|
652 |
} |
|
653 |
return array; |
|
654 |
} else if (pattern.startsWith("maxarray=")) { |
|
42446
397681315009
8170291: Unpredictable results of j.i.ObjectInputFilter::createFilter
rriggs
parents:
41888
diff
changeset
|
655 |
return allowed ? new int[(int)value] : new int[(int)value+1]; |
41230 | 656 |
} |
657 |
Assert.fail("Object could not be generated for pattern: " |
|
658 |
+ pattern |
|
659 |
+ ", allowed: " + allowed); |
|
660 |
return null; |
|
661 |
} |
|
662 |
||
663 |
/** |
|
664 |
* Generate an an object that will be serialized to some number of bytes. |
|
665 |
* Or 1 greater if allowed is false. |
|
666 |
* It returns a two element Object array holding a byte array sized |
|
667 |
* to achieve the desired total size. |
|
668 |
* @param allowed true if the stream should be allowed at that size, |
|
669 |
* false if the stream should be larger |
|
670 |
* @param maxBytes the number of bytes desired in the stream; |
|
671 |
* should not be less than 72 (due to protocol overhead). |
|
672 |
* @return a object that will be serialized to the length requested |
|
673 |
*/ |
|
674 |
private static Object genMaxBytesObject(boolean allowed, long maxBytes) { |
|
675 |
Object[] holder = new Object[2]; |
|
676 |
long desiredSize = allowed ? maxBytes : maxBytes + 1; |
|
677 |
long actualSize = desiredSize; |
|
678 |
long byteSize = desiredSize - 72; // estimate needed array size |
|
679 |
do { |
|
680 |
byteSize += (desiredSize - actualSize); |
|
681 |
byte[] a = new byte[(int)byteSize]; |
|
682 |
holder[0] = a; |
|
683 |
holder[1] = a; |
|
684 |
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
|
685 |
ObjectOutputStream os = new ObjectOutputStream(baos)) { |
|
686 |
os.writeObject(holder); |
|
687 |
os.flush(); |
|
688 |
actualSize = baos.size(); |
|
689 |
} catch (IOException ie) { |
|
690 |
Assert.fail("exception generating stream", ie); |
|
691 |
} |
|
692 |
} while (actualSize != desiredSize); |
|
693 |
return holder; |
|
694 |
} |
|
695 |
||
696 |
/** |
|
697 |
* Returns a HashSet of a requested depth. |
|
698 |
* @param depth the depth |
|
699 |
* @return a HashSet of HashSets... |
|
700 |
*/ |
|
701 |
static HashSet<Object> deepHashSet(int depth) { |
|
702 |
HashSet<Object> hashSet = new HashSet<>(); |
|
703 |
HashSet<Object> s1 = hashSet; |
|
704 |
HashSet<Object> s2 = new HashSet<>(); |
|
705 |
for (int i = 0; i < depth; i++ ) { |
|
706 |
HashSet<Object> t1 = new HashSet<>(); |
|
707 |
HashSet<Object> t2 = new HashSet<>(); |
|
708 |
// make t1 not equal to t2 |
|
709 |
t1.add("by Jimminy"); |
|
710 |
s1.add(t1); |
|
711 |
s1.add(t2); |
|
712 |
s2.add(t1); |
|
713 |
s2.add(t2); |
|
714 |
s1 = t1; |
|
715 |
s2 = t2; |
|
716 |
} |
|
717 |
return hashSet; |
|
718 |
} |
|
719 |
||
720 |
/** |
|
721 |
* Simple method to use with Serialized Lambda. |
|
722 |
*/ |
|
723 |
private static void noop() {} |
|
724 |
||
725 |
||
726 |
/** |
|
727 |
* Class that returns an array from readResolve and also implements |
|
728 |
* the ObjectInputFilter to check that it has the expected length. |
|
729 |
*/ |
|
730 |
static class ReadResolveToArray implements Serializable, ObjectInputFilter { |
|
731 |
private static final long serialVersionUID = 123456789L; |
|
732 |
||
58565
baa5969ecf34
8231427: Warning cleanup in tests of java.io.Serializable
rriggs
parents:
47423
diff
changeset
|
733 |
@SuppressWarnings("serial") /* Incorrect declarations are being tested */ |
41230 | 734 |
private final Object array; |
735 |
private final int length; |
|
736 |
||
737 |
ReadResolveToArray(Object array, int length) { |
|
738 |
this.array = array; |
|
739 |
this.length = length; |
|
740 |
} |
|
741 |
||
742 |
Object readResolve() { |
|
743 |
return array; |
|
744 |
} |
|
745 |
||
746 |
@Override |
|
747 |
public ObjectInputFilter.Status checkInput(FilterInfo filter) { |
|
748 |
if (ReadResolveToArray.class.isAssignableFrom(filter.serialClass())) { |
|
749 |
return ObjectInputFilter.Status.ALLOWED; |
|
750 |
} |
|
751 |
if (filter.serialClass() != array.getClass() || |
|
752 |
(filter.arrayLength() >= 0 && filter.arrayLength() != length)) { |
|
753 |
return ObjectInputFilter.Status.REJECTED; |
|
754 |
} |
|
755 |
return ObjectInputFilter.Status.UNDECIDED; |
|
756 |
} |
|
757 |
||
758 |
} |
|
759 |
||
760 |
/** |
|
761 |
* Hold a snapshot of values to be passed to an ObjectInputFilter. |
|
762 |
*/ |
|
763 |
static class FilterValues implements ObjectInputFilter.FilterInfo { |
|
764 |
private final Class<?> clazz; |
|
765 |
private final long arrayLength; |
|
766 |
private final long depth; |
|
767 |
private final long references; |
|
768 |
private final long streamBytes; |
|
769 |
||
770 |
public FilterValues(Class<?> clazz, long arrayLength, long depth, long references, long streamBytes) { |
|
771 |
this.clazz = clazz; |
|
772 |
this.arrayLength = arrayLength; |
|
773 |
this.depth = depth; |
|
774 |
this.references = references; |
|
775 |
this.streamBytes = streamBytes; |
|
776 |
} |
|
777 |
||
778 |
@Override |
|
779 |
public Class<?> serialClass() { |
|
780 |
return clazz; |
|
781 |
} |
|
782 |
||
783 |
public long arrayLength() { |
|
784 |
return arrayLength; |
|
785 |
} |
|
786 |
||
787 |
public long depth() { |
|
788 |
return depth; |
|
789 |
} |
|
790 |
||
791 |
public long references() { |
|
792 |
return references; |
|
793 |
} |
|
794 |
||
795 |
public long streamBytes() { |
|
796 |
return streamBytes; |
|
797 |
} |
|
798 |
} |
|
44756 | 799 |
|
800 |
// Deeper superclass hierarchy |
|
801 |
static class A implements Serializable { |
|
802 |
private static final long serialVersionUID = 1L; |
|
803 |
}; |
|
804 |
static class B extends A { |
|
805 |
private static final long serialVersionUID = 2L; |
|
806 |
} |
|
807 |
static class C extends B { |
|
808 |
private static final long serialVersionUID = 3L; |
|
809 |
} |
|
810 |
static class D extends C { |
|
811 |
private static final long serialVersionUID = 4L; |
|
812 |
} |
|
813 |
static class E extends D { |
|
814 |
private static final long serialVersionUID = 5L; |
|
815 |
} |
|
816 |
static class F extends E { |
|
817 |
private static final long serialVersionUID = 6L; |
|
818 |
} |
|
819 |
||
41230 | 820 |
} |