changeset 9955:9d38e2747017

8234836: Improve serialization handling Reviewed-by: skoivu, rhalade, chegar
author rriggs
date Thu, 16 Apr 2020 10:57:51 -0400
parents 28ed736f2e85
children 0dfd910e9753
files src/share/classes/java/io/ObjectInputStream.java test/java/io/Serializable/serialFilter/SerialFilterTest.java
diffstat 2 files changed, 68 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/io/ObjectInputStream.java	Tue Feb 11 16:29:00 2020 -0800
+++ b/src/share/classes/java/io/ObjectInputStream.java	Thu Apr 16 10:57:51 2020 -0400
@@ -49,6 +49,7 @@
 import sun.misc.SharedSecrets;
 import sun.reflect.misc.ReflectUtil;
 import sun.util.logging.PlatformLogger;
+import sun.security.action.GetBooleanAction;
 
 /**
  * An ObjectInputStream deserializes primitive data and objects previously
@@ -242,6 +243,23 @@
         /** 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));
+            }
+        }
     }
 
     static {
@@ -1248,6 +1266,10 @@
                 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;
     }
 
--- a/test/java/io/Serializable/serialFilter/SerialFilterTest.java	Tue Feb 11 16:29:00 2020 -0800
+++ b/test/java/io/Serializable/serialFilter/SerialFilterTest.java	Thu Apr 16 10:57:51 2020 -0400
@@ -50,8 +50,10 @@
 import sun.misc.ObjectInputFilter;
 
 /* @test
+ * @bug 8234836
  * @build SerialFilterTest
  * @run testng/othervm  SerialFilterTest
+ * @run testng/othervm  -Djdk.serialSetFilterAfterRead=true SerialFilterTest
  *
  * @summary Test ObjectInputFilters
  */
@@ -77,6 +79,10 @@
      */
     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.
@@ -271,6 +277,46 @@
     }
 
     /**
+     * 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
+                ObjectInputFilter filter = new ObjectInputFilter() {
+                    @Override
+                    public Status checkInput(FilterInfo filterInfo) {
+                        return null;
+                    }
+                };
+                ObjectInputFilter.Config.setObjectInputFilter(ois, filter);
+                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.
      * @throws IOException if an error occurs