This is an automated email from the ASF dual-hosted git repository.

ppkarwasz pushed a commit to branch feat/2.x/serial-util-java9-filter
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 513e55a18123f63e468dcfa2522039b129e66b20
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Wed Aug 26 23:58:58 2026 +0200

    Apply the deserialization filter in test helpers on Java 9+
    
    The `SerialUtil` test helper returned a plain, unfiltered
    `ObjectInputStream` on Java 9 and later, so the deserialization
    allowlist was only ever exercised by tests running on a Java 8
    toolchain. It now installs `DefaultObjectInputFilter` (reflectively,
    since the class must still compile and run on Java 8), combined with an
    `ObjectInputFilter.Config.createFilter` delegate built from the
    caller-supplied extra allowed classes, reproducing the
    `FilteredObjectInputStream` semantics on modern JDKs. When
    `DefaultObjectInputFilter` is absent (it only exists in the packaged
    multi-release JAR), the helper falls back to
    `FilteredObjectInputStream` on any Java version.
    
    `SerializationTestHelper` in `log4j-1.2-api` now delegates to
    `SerialUtil`, so its `org.apache.log4j.*` extras are honored on all
    Java versions as well.
    
    Part of the hardening series from #4168.
    
    Assisted-By: Claude Fable 5 <[email protected]>
---
 .../apache/log4j/util/SerializationTestHelper.java | 18 +++----
 .../logging/log4j/test/SerializableMatchers.java   |  6 +--
 .../logging/log4j/test/junit/SerialUtil.java       | 61 +++++++++++++++++-----
 .../enforce_test_deserialization_filter_java9.xml  | 11 ++++
 4 files changed, 70 insertions(+), 26 deletions(-)

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 ab9b497de9..ba97511126 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.io.ObjectOutputStream;
 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 final class SerializationTestHelper {
     }
 
     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<String> 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<String> 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 a5af542e86..2aff5fefd3 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 final class SerializableMatchers {
 
     /**
      * 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 <T extends Serializable> Matcher<T> serializesRoundTrip(
             final Matcher<T> matcher, final Collection<String> 
allowedExtraClasses) {
@@ -64,8 +64,8 @@ public final class SerializableMatchers {
     }
 
     /**
-     * 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<? super Serializable> serializesRoundTrip(final 
Collection<String> 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 34600f0c83..89e783928d 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.ObjectInputStream;
 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 @@ import 
org.apache.logging.log4j.util.FilteredObjectInputStream;
  */
 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 class SerialUtil {
     }
 
     /**
-     * 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 class SerialUtil {
     }
 
     /**
-     * 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 class SerialUtil {
     }
 
     /**
-     * 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<String> 
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/enforce_test_deserialization_filter_java9.xml 
b/src/changelog/.2.x.x/enforce_test_deserialization_filter_java9.xml
new file mode 100644
index 0000000000..6a4ca0505f
--- /dev/null
+++ b/src/changelog/.2.x.x/enforce_test_deserialization_filter_java9.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="
+           https://logging.apache.org/xml/ns
+           https://logging.apache.org/xml/ns/log4j-changelog-0.xsd";
+       type="changed">
+  <description format="asciidoc">
+    The `SerialUtil` test helper now applies the deserialization allowlist 
also on Java 9 and later.
+  </description>
+</entry>

Reply via email to