diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java index ab9b497de99..ba975111264 100644 --- a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java +++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java @@ -30,8 +30,7 @@ import java.util.Arrays; import java.util.Collection; import org.apache.commons.io.FileUtils; -import org.apache.logging.log4j.util.Constants; -import org.apache.logging.log4j.util.FilteredObjectInputStream; +import org.apache.logging.log4j.test.junit.SerialUtil; /** * Utiities for serialization tests. @@ -114,15 +113,12 @@ public static Object deserializeStream(final String witness) throws Exception { } private static ObjectInputStream newObjectInputStream(final InputStream in) throws IOException { - if (Constants.JAVA_MAJOR_VERSION == 8) { - // FilteredObjectInputStream's default allow-list covers `org.apache.logging.log4j.` but - // not the `org.apache.log4j.` 1.2-compatibility namespace, so we have to enumerate the - // 1.2 classes that the tests in this module deserialize on Java 8. - final Collection allowedLog4j12Classes = - Arrays.asList("org.apache.log4j.Level", "org.apache.log4j.LevelTest$CustomLevel"); - return new FilteredObjectInputStream(in, allowedLog4j12Classes); - } - return new ObjectInputStream(in); + // The default allow-list covers `org.apache.logging.log4j.` but + // not the `org.apache.log4j.` 1.2-compatibility namespace, so we have to enumerate the + // 1.2 classes that the tests in this module deserialize. + final Collection allowedLog4j12Classes = + Arrays.asList("org.apache.log4j.Level", "org.apache.log4j.LevelTest$CustomLevel"); + return SerialUtil.getObjectInputStream(in, allowedLog4j12Classes); } /** diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/SerializableMatchers.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/SerializableMatchers.java index a5af542e862..2aff5fefd30 100644 --- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/SerializableMatchers.java +++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/SerializableMatchers.java @@ -39,7 +39,7 @@ public static Matcher serializesRoundTrip(final Matc /** * Same as {@link #serializesRoundTrip(Matcher)} but extends the default deserialization - * allow-list on Java 8 (see {@link SerialUtil#deserialize(byte[], Collection)}). + * allowlist (see {@link SerialUtil#deserialize(byte[], Collection)}). */ public static Matcher serializesRoundTrip( final Matcher matcher, final Collection allowedExtraClasses) { @@ -64,8 +64,8 @@ public static Matcher serializesRoundTrip() { } /** - * Same as {@link #serializesRoundTrip()} but extends the default deserialization allow-list on - * Java 8 (see {@link SerialUtil#deserialize(byte[], Collection)}). + * Same as {@link #serializesRoundTrip()} but extends the default deserialization allow-list + * (see {@link SerialUtil#deserialize(byte[], Collection)}). */ public static Matcher serializesRoundTrip(final Collection allowedExtraClasses) { return serializesRoundTrip(any(Serializable.class), allowedExtraClasses); diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SerialUtil.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SerialUtil.java index 34600f0c831..89e783928d3 100644 --- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SerialUtil.java +++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SerialUtil.java @@ -24,6 +24,7 @@ import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import org.apache.logging.log4j.test.internal.annotation.SuppressFBWarnings; @@ -35,6 +36,34 @@ */ public class SerialUtil { + // On Java 9+ streams are filtered with `DefaultObjectInputFilter`, which must be accessed reflectively. + private static final Method createFilter; + private static final Method newDefaultObjectInputFilter; + private static final Method setObjectInputFilter; + + static { + Method createFilterMethod = null; + Method newInstanceMethod = null; + Method setFilterMethod = null; + if (Constants.JAVA_MAJOR_VERSION != 8) { + try { + final Class filterClass = Class.forName("java.io.ObjectInputFilter"); + createFilterMethod = + Class.forName("java.io.ObjectInputFilter$Config").getMethod("createFilter", String.class); + newInstanceMethod = Class.forName("org.apache.logging.log4j.util.internal.DefaultObjectInputFilter") + .getMethod("newInstance", filterClass); + setFilterMethod = ObjectInputStream.class.getMethod("setObjectInputFilter", filterClass); + } catch (final ReflectiveOperationException e) { + createFilterMethod = null; + newInstanceMethod = null; + // setFilterMethod is already null + } + } + createFilter = createFilterMethod; + newDefaultObjectInputFilter = newInstanceMethod; + setObjectInputFilter = setFilterMethod; + } + private SerialUtil() {} /** @@ -76,12 +105,10 @@ public static T deserialize(final byte[] data) { } /** - * Deserialize an object from the specified byte array using a {@link FilteredObjectInputStream} - * extended with the supplied allow-list (Java 8 only — Java 9+ uses the JVM's serialization - * filter, so the allow-list is ignored). + * Deserialize an object from the specified byte array using a stream that applies Log4j's + * deserialization allow-list, extended with the supplied extra classes. * @param data byte array representing the serialized object - * @param allowedExtraClasses fully-qualified class names to add to {@link - * FilteredObjectInputStream}'s default allow-list on Java 8 + * @param allowedExtraClasses fully-qualified class names to add to the default allow-list * @return the deserialized object */ @SuppressWarnings("unchecked") @@ -106,8 +133,8 @@ public static ObjectInputStream getObjectInputStream(final byte[] data) throws I } /** - * Creates an {@link ObjectInputStream} adapted to the current Java version, extended with the - * supplied allow-list on Java 8. + * Creates an {@link ObjectInputStream} adapted to the current Java version, applying Log4j's + * deserialization allow-list extended with the supplied extra classes. */ @SuppressFBWarnings("OBJECT_DESERIALIZATION") public static ObjectInputStream getObjectInputStream( @@ -127,14 +154,24 @@ public static ObjectInputStream getObjectInputStream(final InputStream stream) t } /** - * Creates an {@link ObjectInputStream} adapted to the current Java version, extended with the - * supplied allow-list on Java 8. + * Creates an {@link ObjectInputStream} adapted to the current Java version, applying Log4j's + * deserialization allowlist extended with the supplied extra classes. */ @SuppressFBWarnings("OBJECT_DESERIALIZATION") public static ObjectInputStream getObjectInputStream( final InputStream stream, final Collection allowedExtraClasses) throws IOException { - return Constants.JAVA_MAJOR_VERSION == 8 - ? new FilteredObjectInputStream(stream, allowedExtraClasses) - : new ObjectInputStream(stream); + if (Constants.JAVA_MAJOR_VERSION == 8 || newDefaultObjectInputFilter == null) { + return new FilteredObjectInputStream(stream, allowedExtraClasses); + } + final ObjectInputStream ois = new ObjectInputStream(stream); + try { + final Object extraClassesFilter = allowedExtraClasses.isEmpty() + ? null + : createFilter.invoke(null, String.join(";", allowedExtraClasses)); + setObjectInputFilter.invoke(ois, newDefaultObjectInputFilter.invoke(null, extraClassesFilter)); + } catch (final ReflectiveOperationException e) { + throw new IllegalStateException("Unable to install the deserialization filter", e); + } + return ois; } } diff --git a/src/changelog/.2.x.x/4270_enforce_test_deserialization_filter_java9.xml b/src/changelog/.2.x.x/4270_enforce_test_deserialization_filter_java9.xml new file mode 100644 index 00000000000..d17cbaa71e8 --- /dev/null +++ b/src/changelog/.2.x.x/4270_enforce_test_deserialization_filter_java9.xml @@ -0,0 +1,12 @@ + + + + + The `SerialUtil` test helper now applies the deserialization allowlist also on Java 9 and later. + +