8234836: Improve serialization handling

Reviewed-by: chegar, rhalade, skoivu
This commit is contained in:
Roger Riggs 2020-04-16 10:33:44 -04:00
parent 19c29923a5
commit 34fc33074a
2 changed files with 63 additions and 2 deletions

View File

@ -49,6 +49,7 @@ import static java.io.ObjectStreamClass.processQueue;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import sun.reflect.misc.ReflectUtil;
import sun.security.action.GetBooleanAction;
/**
* An ObjectInputStream deserializes primitive data and objects previously
@ -294,6 +295,14 @@ public class ObjectInputStream
/** queue for WeakReferences to audited subclasses */
static final ReferenceQueue<Class<?>> subclassAuditsQueue =
new ReferenceQueue<>();
/**
* Property to permit setting a filter after objects
* have been read.
* See {@link #setObjectInputFilter(ObjectInputFilter)}
*/
static final boolean SET_FILTER_AFTER_READ = GetBooleanAction
.privilegedGetProperty("jdk.serialSetFilterAfterRead");
}
/*
@ -1260,6 +1269,8 @@ public class ObjectInputStream
* {@link ObjectInputFilter.Config#getSerialFilter() ObjectInputFilter.Config.getSerialFilter}
* when the {@code ObjectInputStream} is constructed and can be set
* to a custom filter only once.
* The filter must be set before reading any objects from the stream;
* for example, by calling {@link #readObject} or {@link #readUnshared}.
*
* @implSpec
* The filter, when not {@code null}, is invoked during {@link #readObject readObject}
@ -1302,7 +1313,8 @@ public class ObjectInputStream
* @throws SecurityException if there is security manager and the
* {@code SerializablePermission("serialFilter")} is not granted
* @throws IllegalStateException if the {@linkplain #getObjectInputFilter() current filter}
* is not {@code null} and is not the system-wide filter
* is not {@code null} and is not the system-wide filter, or
* if an object has been read
* @since 9
*/
public final void setObjectInputFilter(ObjectInputFilter filter) {
@ -1315,6 +1327,10 @@ public class ObjectInputStream
serialFilter != ObjectInputFilter.Config.getSerialFilter()) {
throw new IllegalStateException("filter can not be set more than once");
}
if (totalObjectRefs > 0 && !Caches.SET_FILTER_AFTER_READ) {
throw new IllegalStateException(
"filter can not be set after an object has been read");
}
this.serialFilter = filter;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2020, 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
@ -50,8 +50,10 @@ import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
/* @test
* @bug 8234836
* @build SerialFilterTest
* @run testng/othervm SerialFilterTest
* @run testng/othervm -Djdk.serialSetFilterAfterRead=true SerialFilterTest
*
* @summary Test ObjectInputFilters
*/
@ -75,6 +77,10 @@ public class SerialFilterTest implements Serializable {
*/
private static final Object otherObject = Integer.valueOf(0);
// Cache value of jdk.serialSetFilterAfterRead property.
static final boolean SET_FILTER_AFTER_READ =
Boolean.getBoolean("jdk.serialSetFilterAfterRead");
/**
* DataProvider for the individual patterns to test.
* Expand the patterns into cases for each of the Std and Compatibility APIs.
@ -308,6 +314,45 @@ public class SerialFilterTest implements Serializable {
}
}
/**
* After reading some objects from the stream, setting a filter is disallowed.
* If the filter was allowed to be set, it would have unpredictable behavior.
* Objects already read would not be checked again, including class descriptors.
*
* Note: To mitigate possible incompatibility a system property can be set
* to revert to the old behavior but it re-enables the incorrect use.
*/
@Test
static void testNonSettableAfterReadObject() throws IOException, ClassNotFoundException {
String expected1 = "text1";
String expected2 = "text2";
byte[] bytes = writeObjects(expected1, expected2);
for (boolean toggle: new boolean[] {true, false}) {
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais)) {
Object actual1 = toggle ? ois.readObject() : ois.readUnshared();
Assert.assertEquals(actual1, expected1, "unexpected string");
// Attempt to set filter
ois.setObjectInputFilter(new ObjectInputFilter() {
@Override
public Status checkInput(FilterInfo filterInfo) {
return null;
}
});
if (!SET_FILTER_AFTER_READ)
Assert.fail("Should not be able to set filter after readObject has been called");
} catch (IllegalStateException ise) {
// success, the exception was expected
if (SET_FILTER_AFTER_READ)
Assert.fail("With jdk.serialSetFilterAfterRead property set = true; " +
"should be able to set the filter after a read");
} catch (EOFException eof) {
Assert.fail("Should not reach end-of-file", eof);
}
}
}
/**
* Test that if an Objects readReadResolve method returns an array
* that the callback to the filter includes the proper array length.