diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java index e7eb22494283075b75ad6dbe2ffbe66fde22b19d..3386b1a08df3aa35ddce2bad43ca0bce8ec34d97 100644 --- a/src/java.base/share/classes/java/io/ObjectInputStream.java +++ b/src/java.base/share/classes/java/io/ObjectInputStream.java @@ -47,6 +47,7 @@ import static java.io.ObjectStreamClass.processQueue; import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.Unsafe; import sun.reflect.misc.ReflectUtil; +import sun.security.action.GetBooleanAction; /** * An ObjectInputStream deserializes primitive data and objects previously @@ -259,6 +260,23 @@ public class ObjectInputStream /** queue for WeakReferences to audited subclasses */ static final ReferenceQueue> 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)); + } + } } /* @@ -1280,6 +1298,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; } diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.java b/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.java index 362df997675ad3dcebb932bc0c3892054ab54791..279dfe7a44fe21d09544fac2a9a88bbab4952d82 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.java @@ -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.