8234836: Improve serialization handling

Reviewed-by: skoivu, rhalade, chegar
This commit is contained in:
Roger Riggs 2020-04-16 10:57:51 -04:00
parent e6364a020e
commit 62da722d66
2 changed files with 67 additions and 0 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,23 @@ 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 =
privilegedGetProperty("jdk.serialSetFilterAfterRead");
private static boolean privilegedGetProperty(String theProp) {
if (System.getSecurityManager() == null) {
return Boolean.getBoolean(theProp);
} else {
return AccessController.doPrivileged(
new GetBooleanAction(theProp));
}
}
}
/*
@ -1315,6 +1333,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

@ -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.