RFR: 8264859: Implement Context-Specific Deserialization Filters [v3]

Daniel Fuchs dfuchs at openjdk.java.net
Thu May 20 19:14:31 UTC 2021


On Thu, 20 May 2021 16:10:11 GMT, Roger Riggs <rriggs at openjdk.org> wrote:

>> JEP 415: Context-specific Deserialization Filters extends the deserialization filtering mechanisms with more flexible and customizable protections against malicious deserialization.  See JEP 415: https://openjdk.java.net/jeps/415.
>> The `java.io.ObjectInputFilter` and `java.io.ObjectInputStream` classes are extended with additional
>> configuration mechanisms and filter utilities.
>> 
>> javadoc for `ObjectInputFilter`, `ObjectInputFilter.Config`, and `ObjectInputStream`:
>>     http://cr.openjdk.java.net/~rriggs/filter-factory/java.base/java/io/ObjectInputFilter.html
>
> Roger Riggs has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Simplify factory interface to BinaryOperator<ObjectInputFilter> and cleanup the example

src/java.base/share/classes/java/io/ObjectInputFilter.java line 410:

> 408:      * The class must have a public zero-argument constructor, implement the
> 409:      * {@link BinaryOperator} interface,
> 410:      * and provide its implementation.

I wonder if some more explanation is needed to explain how the filter factory class is located. I mean - I expect it will be looked up using the System ClassLoader - doesn't this need to be specified? Or if it's already specified elsewhere then maybe a link should be provided?

Maybe the spec should also say that the class should be public. And I guess that if it is in another module then it should be in an exported package - or possibly in a package opened to reflection? Or does this only work if the class is in the unnamed module (deployed on the classpath)?

src/java.base/share/classes/java/io/ObjectInputFilter.java line 431:

> 429:          * Used as a system property and a java.security.Security property.
> 430:          */
> 431:         private final static String SERIAL_FILTER_PROPNAME = "jdk.serialFilter";

should be `private static final` (in canonical order).

src/java.base/share/classes/java/io/ObjectInputFilter.java line 589:

> 587:         /**
> 588:          * Returns the JVM-wide deserialization filter factory.
> 589:          * If the filter factory has been {@link #setSerialFilterFactory(BinaryOperator) set} it is returned,

Should be `{@linkplain ...}`

src/java.base/share/classes/java/io/ObjectInputFilter.java line 592:

> 590:          * otherwise, a builtin deserialization filter factory is returned.
> 591:          * The filter factory provides a filter for every ObjectInputStream when invoked from
> 592:          * {@link ObjectInputStream#ObjectInputStream(InputStream) ObjectInputStream constructors}

Should be {@linkplain ...}

src/java.base/share/classes/java/io/ObjectInputFilter.java line 601:

> 599:          * {@link ObjectInputStream#ObjectInputStream(InputStream) ObjectInputStream constructors}.
> 600:          * When invoked {@link ObjectInputStream#setObjectInputFilter(ObjectInputFilter)
> 601:          * to set the stream-specific filter} the requested filter replaces the static JVM-wide filter,

These three should be {@linkplain ...} too.

src/java.base/share/classes/java/io/ObjectInputFilter.java line 603:

> 601:          * to set the stream-specific filter} the requested filter replaces the static JVM-wide filter,
> 602:          * unless it has already been set. The stream-specific filter can only be set once,
> 603:          * if it has already been set, an {@link IllegalStateException} is thrown.

I am assuming the `IllegalStateException` exception is thrown by `ObjecInputStream::setObjectInputFilter` - maybe that should be clarified here.

src/java.base/share/classes/java/io/ObjectInputFilter.java line 609:

> 607:          *
> 608:          * @return the JVM-wide deserialization filter factory; non-null
> 609:          * @since TBD

Need to replace TBD before pushing ;-)

src/java.base/share/classes/java/io/ObjectInputFilter.java line 632:

> 630:          * The factory determines the filter to be used for {@code ObjectInputStream} streams based
> 631:          * on its inputs, and any other filters, context, or state that is available.
> 632:          * The factory may throw runtime exceptions to signal incorrect use or invalid parameters.

What happens if the factory throws an exception?

src/java.base/share/classes/java/io/ObjectInputFilter.java line 639:

> 637:          * @throws SecurityException if there is security manager and the
> 638:          *       {@code SerializablePermission("serialFilter")} is not granted
> 639:          * @since TBD

... TBD ...

src/java.base/share/classes/java/io/ObjectInputFilter.java line 756:

> 754:          * <pre><code>
> 755:          *     ObjectInputFilter f = allowFilter(cl -> cl.getClassLoader() == ClassLoader.getPlatformClassLoader()
> 756:          *                                          || cl.getClassLoader() == null);

The example here seems to be missing the `otherStatus` parameter.

src/java.base/share/classes/java/io/ObjectInputFilter.java line 785:

> 783:          * <pre><code>
> 784:          *     ObjectInputFilter f = rejectFilter(cl -> cl.getClassLoader() == ClassLoader.ClassLoader.getSystemClassLoader());
> 785:          * </code></pre>

Same here, the example is missing the `otherStatus` parameter.

src/java.base/share/classes/java/io/ObjectInputFilter.java line 830:

> 828:          *
> 829:          */
> 830:         final static class Global implements ObjectInputFilter {

IIRC the canonical order is `static final` not `final static`.

src/java.base/share/classes/java/io/ObjectInputFilter.java line 1079:

> 1077:                 }
> 1078:                 // There are no classes to check and none of the limits have been exceeded.
> 1079:                 return Status.ALLOWED;

Should this be addressed outside of this JEP and pushed separately?

src/java.base/share/classes/java/io/ObjectInputFilter.java line 1127:

> 1125:             public ObjectInputFilter.Status checkInput(FilterInfo info) {
> 1126:                 return (info.serialClass() != null &&
> 1127:                         predicate.test(info.serialClass())) ? ifTrueStatus : ifFalseStatus;

Maybe the result of info.serialClass() should be stored in a local var to avoid calling the method twice.

src/java.base/share/classes/java/io/ObjectInputFilter.java line 1139:

> 1137:          * and not classes.
> 1138:          */
> 1139:         private static class AllowMaxLimitsFilter implements ObjectInputFilter {

This class is maybe misnamed. If limitCheck == REJECTED it will not allow max limits. Or am I missing something?

src/java.base/share/classes/java/io/ObjectInputFilter.java line 1238:

> 1236:          * {@link #getSerialFilter static serial filter} when invoked from
> 1237:          * {@link ObjectInputStream#ObjectInputStream(InputStream) ObjectInputStream constructors}.
> 1238:          * When invoked from {@link ObjectInputStream#setObjectInputFilter(ObjectInputFilter)

These three links should be {@linkplain ...}

src/java.base/share/classes/java/io/ObjectInputFilter.java line 1270:

> 1268:              *       is not {@code null} and is not the JVM-wide filter
> 1269:              * @throws SecurityException if there is security manager and the
> 1270:              *       {@code SerializablePermission("serialFilter")} is not granted

Where is this thrown? I don't see it in the implementation of `apply` below.

-------------

PR: https://git.openjdk.java.net/jdk/pull/3996


More information about the core-libs-dev mailing list