diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/DefaultObjectInputFilter.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/DefaultObjectInputFilter.java index 1c456541688..df62f71e5f7 100644 --- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/DefaultObjectInputFilter.java +++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/DefaultObjectInputFilter.java @@ -44,7 +44,7 @@ public static DefaultObjectInputFilter newInstance(final ObjectInputFilter filte @Override public Status checkInput(final FilterInfo filterInfo) { - Status status = null; + Status status; if (delegate != null) { status = delegate.checkInput(filterInfo); if (status != Status.UNDECIDED) { @@ -59,9 +59,10 @@ public Status checkInput(final FilterInfo filterInfo) { return status; } } - if (filterInfo.serialClass() != null) { - final String name = filterInfo.serialClass().getName(); - if (isAllowedByDefault(name) || isRequiredPackage(name)) { + final Class serialClass = filterInfo.serialClass(); + if (serialClass != null) { + final String name = SerializationUtil.stripArray(serialClass); + if (isAllowedByDefault(name)) { return Status.ALLOWED; } } else { diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java index bd4ab1ad804..ddade3dd8a8 100644 --- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java +++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java @@ -24,4 +24,8 @@ public final class SerializationUtil { public static final List REQUIRED_JAVA_CLASSES = List.of(); public static final List REQUIRED_JAVA_PACKAGES = List.of(); + + public static String stripArray(final Class clazz) { + return null; + } } 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 943fb39b0f1..e568e415130 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 @@ -19,7 +19,10 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.io.ObjectInputStream; +import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.Serializable; import org.apache.logging.log4j.util.Constants; @@ -38,10 +41,21 @@ private SerialUtil() {} * @return the serialized object */ public static byte[] serialize(final Serializable obj) { + return serialize(new Serializable[] {obj}); + } + + /** + * Serializes the specified object and returns the result as a byte array. + * @param objs an array of objects to serialize + * @return the serialized object + */ + public static byte[] serialize(final Serializable... objs) { try { final ByteArrayOutputStream bas = new ByteArrayOutputStream(8192); - final ObjectOutputStream oos = new ObjectOutputStream(bas); - oos.writeObject(obj); + final ObjectOutput oos = new ObjectOutputStream(bas); + for (final Object obj : objs) { + oos.writeObject(obj); + } oos.flush(); return bas.toByteArray(); } catch (final Exception ex) { @@ -58,16 +72,33 @@ public static byte[] serialize(final Serializable obj) { @SuppressFBWarnings("OBJECT_DESERIALIZATION") public static T deserialize(final byte[] data) { try { - final ByteArrayInputStream bas = new ByteArrayInputStream(data); - final ObjectInputStream ois; - if (Constants.JAVA_MAJOR_VERSION == 8) { - ois = new FilteredObjectInputStream(bas); - } else { - ois = new ObjectInputStream(bas); - } + final ObjectInputStream ois = getObjectInputStream(data); return (T) ois.readObject(); } catch (final Exception ex) { throw new IllegalStateException("Could not deserialize", ex); } } + + /** + * Creates an {@link ObjectInputStream} adapted to the current Java version. + * @param data data to deserialize, + * @return an object input stream. + */ + @SuppressFBWarnings("OBJECT_DESERIALIZATION") + public static ObjectInputStream getObjectInputStream(final byte[] data) throws IOException { + final ByteArrayInputStream bas = new ByteArrayInputStream(data); + return getObjectInputStream(bas); + } + + /** + * Creates an {@link ObjectInputStream} adapted to the current Java version. + * @param stream stream of data to deserialize, + * @return an object input stream. + */ + @SuppressFBWarnings("OBJECT_DESERIALIZATION") + public static ObjectInputStream getObjectInputStream(final InputStream stream) throws IOException { + return Constants.JAVA_MAJOR_VERSION == 8 + ? new FilteredObjectInputStream(stream) + : new ObjectInputStream(stream); + } } diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/package-info.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/package-info.java index 5b84db02c2a..4ead204b3b0 100644 --- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/package-info.java +++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/package-info.java @@ -15,7 +15,7 @@ * limitations under the license. */ @Export -@Version("2.21.1") +@Version("2.23.0") package org.apache.logging.log4j.test.junit; import org.osgi.annotation.bundle.Export; diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java index 117cd47315c..b2ef7ca9784 100644 --- a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java @@ -92,7 +92,13 @@ public boolean equals(final Object other) { return other instanceof NonSerializable; // a very lenient equals() } } - return Stream.of("World", new NonSerializable(), new BigDecimal("123.456"), null); + return Stream.of( + "World", + new NonSerializable(), + new BigDecimal("123.456"), + // LOG4J2-3680 + new RuntimeException(), + null); } @ParameterizedTest diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java index 65ff5d9a965..1aad0d64d9e 100644 --- a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java @@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.math.BigDecimal; import java.util.stream.Stream; import org.apache.logging.log4j.test.junit.Mutable; import org.apache.logging.log4j.test.junit.SerialUtil; @@ -149,7 +150,20 @@ public void testSafeWithMutableParams() { // LOG4J2-763 } static Stream testSerializable() { - return Stream.of("World", new Object(), null); + @SuppressWarnings("EqualsHashCode") + class NonSerializable { + @Override + public boolean equals(final Object other) { + return other instanceof NonSerializable; // a very lenient equals() + } + } + return Stream.of( + "World", + new NonSerializable(), + new BigDecimal("123.456"), + // LOG4J2-3680 + new RuntimeException(), + null); } @ParameterizedTest diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java index 4904f313415..164ae85f3f7 100644 --- a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/SortedArrayStringMapTest.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.util; +import static org.apache.logging.log4j.test.junit.SerialUtil.deserialize; +import static org.apache.logging.log4j.test.junit.SerialUtil.serialize; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -27,14 +29,9 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; -import java.io.IOException; import java.io.InputStreamReader; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.net.URL; import java.net.URLDecoder; @@ -191,20 +188,6 @@ private String createClassPath(final Class cls) throws Exception { return location.isEmpty() ? "." : location; } - private byte[] serialize(final SortedArrayStringMap data) throws IOException { - final ByteArrayOutputStream arr = new ByteArrayOutputStream(); - final ObjectOutputStream out = new ObjectOutputStream(arr); - out.writeObject(data); - return arr.toByteArray(); - } - - private SortedArrayStringMap deserialize(final byte[] binary) throws IOException, ClassNotFoundException { - final ByteArrayInputStream inArr = new ByteArrayInputStream(binary); - try (final ObjectInputStream in = new FilteredObjectInputStream(inArr)) { - return (SortedArrayStringMap) in.readObject(); - } - } - @Test public void testPutAll() { final SortedArrayStringMap original = new SortedArrayStringMap(); diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/internal/SerializationUtilTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/internal/SerializationUtilTest.java new file mode 100644 index 00000000000..8b1d5d9dbba --- /dev/null +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/internal/SerializationUtilTest.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.logging.log4j.util.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class SerializationUtilTest { + + static Stream arrays() { + return Stream.of( + Arguments.of(boolean[].class, boolean.class), + Arguments.of(char[].class, char.class), + Arguments.of(byte[].class, byte.class), + Arguments.of(short[].class, short.class), + Arguments.of(int[].class, int.class), + Arguments.of(long[].class, long.class), + Arguments.of(float[].class, float.class), + Arguments.of(double[].class, double.class), + Arguments.of(String.class, String.class), + Arguments.of(String[].class, String.class), + Arguments.of(String[][].class, String.class)); + } + + @ParameterizedTest + @MethodSource("arrays") + void stripArrayClass(final Class arrayClass, final Class componentClazz) { + assertThat(SerializationUtil.stripArray(arrayClass)).isEqualTo(componentClazz.getName()); + } + + @ParameterizedTest + @MethodSource("arrays") + void stripArrayString(final Class arrayClass, final Class componentClazz) { + assertThat(SerializationUtil.stripArray(arrayClass.getName())).isEqualTo(componentClazz.getName()); + } +} diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/FilteredObjectInputStream.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/FilteredObjectInputStream.java index f7b139a110d..52b71507dea 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/FilteredObjectInputStream.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/FilteredObjectInputStream.java @@ -26,6 +26,7 @@ import java.io.ObjectStreamClass; import java.util.Collection; import java.util.Collections; +import org.apache.logging.log4j.util.internal.SerializationUtil; /** * Extends {@link ObjectInputStream} to only allow some built-in Log4j classes and caller-specified classes to be @@ -63,7 +64,7 @@ public Collection getAllowedClasses() { @Override protected Class resolveClass(final ObjectStreamClass desc) throws IOException, ClassNotFoundException { - final String name = desc.getName(); + final String name = SerializationUtil.stripArray(desc.getName()); if (!(isAllowedByDefault(name) || allowedExtraClasses.contains(name))) { throw new InvalidObjectException("Class is not allowed for deserialization: " + name); } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java index 95899dbe8a1..6180f47421f 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java @@ -79,12 +79,18 @@ public final class SerializationUtil { "java.math.BigInteger", // for Message delegate "java.rmi.MarshalledObject", - "[B", - // for MessagePatternAnalysis - "[I"); + // all primitives + "boolean", + "byte", + "char", + "double", + "float", + "int", + "long", + "short"); - public static final List REQUIRED_JAVA_PACKAGES = Arrays.asList( - "java.lang.", "java.time", "java.util.", "org.apache.logging.log4j.", "[Lorg.apache.logging.log4j."); + public static final List REQUIRED_JAVA_PACKAGES = + Arrays.asList("java.lang.", "java.time.", "java.util.", "org.apache.logging.log4j."); public static void writeWrappedObject(final Serializable obj, final ObjectOutputStream out) throws IOException { final ByteArrayOutputStream bout = new ByteArrayOutputStream(); @@ -133,5 +139,60 @@ public static void assertFiltered(final java.io.ObjectInputStream stream) { } } + /** + * Gets the class name of an array component recursively. + *

+ * If {@code clazz} is not an array class its name is returned. + *

+ * @param clazz the binary name of a class. + */ + public static String stripArray(final Class clazz) { + Class currentClazz = clazz; + while (currentClazz.isArray()) { + currentClazz = currentClazz.getComponentType(); + } + return currentClazz.getName(); + } + + /** + * Gets the class name of an array component recursively. + *

+ * If {@code name} is not the name of an array class it is returned unchanged. + *

+ * @param name the name of a class. + * @see Class#getName() + */ + public static String stripArray(final String name) { + final int offset = name.lastIndexOf('[') + 1; + if (offset == 0) { + return name; + } + // Reference types + if (name.charAt(offset) == 'L') { + return name.substring(offset + 1, name.length() - 1); + } + // Primitive classes + switch (name.substring(offset)) { + case "Z": + return "boolean"; + case "B": + return "byte"; + case "C": + return "char"; + case "D": + return "double"; + case "F": + return "float"; + case "I": + return "int"; + case "J": + return "long"; + case "S": + return "short"; + default: + throw new IllegalArgumentException("Unsupported array class signature '" + name + "'"); + } + } + private SerializationUtil() {} } diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LogEventTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LogEventTest.java index 525578812ab..ec462b1f4d9 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LogEventTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LogEventTest.java @@ -16,22 +16,18 @@ */ package org.apache.logging.log4j.core; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LoggingException; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.SimpleMessage; -import org.apache.logging.log4j.util.FilteredObjectInputStream; +import org.apache.logging.log4j.test.junit.SerialUtil; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -61,28 +57,17 @@ public void testSerialization() throws Exception { .setThrown(child) // .build(); - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - final ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(event1); - oos.writeObject(event2); - - final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - final ObjectInputStream ois = new FilteredObjectInputStream(bais); - try { - ois.readObject(); - } catch (final IOException ioe) { - fail("Exception processing event1"); - } - try { - ois.readObject(); - } catch (final IOException ioe) { - fail("Exception processing event2"); + final byte[] data = SerialUtil.serialize(event1, event2); + + try (final ObjectInputStream ois = SerialUtil.getObjectInputStream(data)) { + assertDoesNotThrow(ois::readObject, "Failed to deserialize event1"); + assertDoesNotThrow(ois::readObject, "Failed to deserialize event1"); } } @Test public void testNanoTimeIsNotSerialized1() throws Exception { - final LogEvent event1 = Log4jLogEvent.newBuilder() // + final LogEvent event = Log4jLogEvent.newBuilder() // .setLoggerName(this.getClass().getName()) // .setLoggerFqcn("org.apache.logging.log4j.core.Logger") // .setLevel(Level.INFO) // @@ -90,24 +75,18 @@ public void testNanoTimeIsNotSerialized1() throws Exception { .setThreadName("this must be initialized or the test fails") // .setNanoTime(12345678L) // .build(); - final LogEvent copy = new Log4jLogEvent.Builder(event1).build(); - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - final ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(event1); - final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - final ObjectInputStream ois = new FilteredObjectInputStream(bais); + final LogEvent expected = new Log4jLogEvent.Builder(event).build(); + final LogEvent actual = SerialUtil.deserialize(SerialUtil.serialize(event)); - final LogEvent actual = (LogEvent) ois.readObject(); - assertNotEquals(copy, actual, "Different event: nanoTime"); - assertNotEquals(copy.getNanoTime(), actual.getNanoTime(), "Different nanoTime"); + assertNotEquals(expected, actual, "Different event: nanoTime"); + assertNotEquals(expected.getNanoTime(), actual.getNanoTime(), "Different nanoTime"); assertEquals(0, actual.getNanoTime(), "deserialized nanoTime is zero"); } @Test public void testNanoTimeIsNotSerialized2() throws Exception { - final LogEvent event1 = Log4jLogEvent.newBuilder() // + final LogEvent event = Log4jLogEvent.newBuilder() // .setLoggerName(this.getClass().getName()) // .setLoggerFqcn("org.apache.logging.log4j.core.Logger") // .setLevel(Level.INFO) // @@ -117,17 +96,10 @@ public void testNanoTimeIsNotSerialized2() throws Exception { .setThreadPriority(2) // this must be initialized or the test fails .setNanoTime(0) // .build(); - final LogEvent event2 = new Log4jLogEvent.Builder(event1).build(); - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - final ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(event1); - - final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - final ObjectInputStream ois = new FilteredObjectInputStream(bais); - final LogEvent actual = (LogEvent) ois.readObject(); - assertEquals(event2, actual, "both zero nanoTime"); + final LogEvent expected = new Log4jLogEvent.Builder(event).build(); + final LogEvent actual = SerialUtil.deserialize(SerialUtil.serialize(event)); + assertEquals(expected, actual, "both zero nanoTime"); } @Test diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaAppenderTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaAppenderTest.java index 8df1ce062c4..ac69d2e9445 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaAppenderTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/mom/kafka/KafkaAppenderTest.java @@ -22,9 +22,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInput; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.time.Duration; @@ -46,7 +43,7 @@ import org.apache.logging.log4j.core.test.categories.Appenders; import org.apache.logging.log4j.core.test.junit.LoggerContextRule; import org.apache.logging.log4j.message.SimpleMessage; -import org.apache.logging.log4j.util.FilteredObjectInputStream; +import org.apache.logging.log4j.test.junit.SerialUtil; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; @@ -141,7 +138,9 @@ public void testAppendWithSerializedLayout() throws Exception { assertNotNull(item); assertEquals(TOPIC_NAME, item.topic()); assertNull(item.key()); - assertEquals(LOG_MESSAGE, deserializeLogEvent(item.value()).getMessage().getFormattedMessage()); + final byte[] data = item.value(); + assertEquals( + LOG_MESSAGE, SerialUtil.deserialize(data).getMessage().getFormattedMessage()); } @Test @@ -224,13 +223,6 @@ public void testAppenderNoEventTimestamp() throws Exception { assertEquals(LOG_MESSAGE, new String(item.value(), StandardCharsets.UTF_8)); } - private LogEvent deserializeLogEvent(final byte[] data) throws IOException, ClassNotFoundException { - final ByteArrayInputStream bis = new ByteArrayInputStream(data); - try (final ObjectInput ois = new FilteredObjectInputStream(bis)) { - return (LogEvent) ois.readObject(); - } - } - // public void shouldRetryWhenTimeoutExceptionOccursOnSend() throws Exception { // final AtomicInteger attempt = new AtomicInteger(0); // final RecordCollectorImpl collector = new RecordCollectorImpl( diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java index a1ae1efe31c..ffd26bed644 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java @@ -24,11 +24,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.util.Arrays; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; @@ -44,7 +40,7 @@ import org.apache.logging.log4j.message.ReusableMessageFactory; import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.spi.MutableThreadContextStack; -import org.apache.logging.log4j.util.FilteredObjectInputStream; +import org.apache.logging.log4j.test.junit.SerialUtil; import org.apache.logging.log4j.util.StringMap; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Tag; @@ -227,12 +223,7 @@ void testSerializationDeserialization() throws IOException, ClassNotFoundExcepti new DummyNanoClock(1)); ((StringMap) evt.getContextData()).putValue("key", "value"); - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - final ObjectOutputStream out = new ObjectOutputStream(baos); - out.writeObject(evt); - - final ObjectInputStream in = new FilteredObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); - final RingBufferLogEvent other = (RingBufferLogEvent) in.readObject(); + final RingBufferLogEvent other = SerialUtil.deserialize(SerialUtil.serialize(evt)); assertThat(other.getLoggerName()).isEqualTo(loggerName); assertThat(other.getMarker()).isEqualTo(marker); assertThat(other.getLoggerFqcn()).isEqualTo(fqcn); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java index 3cfd9235d1b..119e3dd1ccc 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.core.impl; +import static org.apache.logging.log4j.test.junit.SerialUtil.deserialize; +import static org.apache.logging.log4j.test.junit.SerialUtil.serialize; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -25,11 +27,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.lang.reflect.Field; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; @@ -47,7 +44,6 @@ import org.apache.logging.log4j.message.ReusableMessage; import org.apache.logging.log4j.message.ReusableObjectMessage; import org.apache.logging.log4j.message.SimpleMessage; -import org.apache.logging.log4j.util.FilteredObjectInputStream; import org.apache.logging.log4j.util.SortedArrayStringMap; import org.apache.logging.log4j.util.StringMap; import org.apache.logging.log4j.util.Strings; @@ -158,20 +154,6 @@ public void testJavaIoSerializableWithThrown() throws Exception { assertEquals(evt.isIncludeLocation(), evt2.isIncludeLocation()); } - private byte[] serialize(final Log4jLogEvent event) throws IOException { - final ByteArrayOutputStream arr = new ByteArrayOutputStream(); - final ObjectOutputStream out = new ObjectOutputStream(arr); - out.writeObject(event); - return arr.toByteArray(); - } - - private Log4jLogEvent deserialize(final byte[] binary) throws IOException, ClassNotFoundException { - final ByteArrayInputStream inArr = new ByteArrayInputStream(binary); - final ObjectInputStream in = new FilteredObjectInputStream(inArr); - final Log4jLogEvent result = (Log4jLogEvent) in.readObject(); - return result; - } - // DO NOT REMOVE THIS COMMENT: // UNCOMMENT WHEN GENERATING SERIALIZED EVENT FOR #testJavaIoSerializableWithUnknownThrowable // public static class DeletedException extends Exception { diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java index ce7b1c543b7..0723d6ff735 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.core.impl; +import static org.apache.logging.log4j.test.junit.SerialUtil.deserialize; +import static org.apache.logging.log4j.test.junit.SerialUtil.serialize; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -27,11 +29,6 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.util.Arrays; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.MarkerManager; @@ -43,10 +40,8 @@ import org.apache.logging.log4j.message.ReusableSimpleMessage; import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.spi.MutableThreadContextStack; -import org.apache.logging.log4j.util.FilteredObjectInputStream; import org.apache.logging.log4j.util.SortedArrayStringMap; import org.apache.logging.log4j.util.StringMap; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; /** @@ -56,8 +51,6 @@ public class MutableLogEventTest { private static final StringMap CONTEXT_DATA = createContextData(); private static final ThreadContext.ContextStack STACK = new MutableThreadContextStack(Arrays.asList("abc", "xyz")); - static boolean useObjectInputStream = false; - private static StringMap createContextData() { final StringMap result = new SortedArrayStringMap(); result.putValue("a", "1"); @@ -65,16 +58,6 @@ private static StringMap createContextData() { return result; } - @BeforeAll - public static void setupClass() { - try { - Class.forName("java.io.ObjectInputFilter"); - useObjectInputStream = true; - } catch (final ClassNotFoundException ex) { - // Ignore the exception - } - } - @Test public void testToImmutable() { final LogEvent logEvent = new MutableLogEvent(); @@ -383,19 +366,4 @@ public void testPreservesLocation() { final Log4jLogEvent immutable = mutable.toImmutable(); assertThat(immutable.getSource()).isEqualTo(source); } - - private byte[] serialize(final MutableLogEvent event) throws IOException { - final ByteArrayOutputStream arr = new ByteArrayOutputStream(); - final ObjectOutputStream out = new ObjectOutputStream(arr); - out.writeObject(event); - return arr.toByteArray(); - } - - private Log4jLogEvent deserialize(final byte[] binary) throws IOException, ClassNotFoundException { - final ByteArrayInputStream inArr = new ByteArrayInputStream(binary); - final ObjectInputStream in = - useObjectInputStream ? new ObjectInputStream(inArr) : new FilteredObjectInputStream(inArr); - final Log4jLogEvent result = (Log4jLogEvent) in.readObject(); - return result; - } } diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/SerializedLayoutTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/SerializedLayoutTest.java index 1c7c03ac62d..7f1fb2e849b 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/SerializedLayoutTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/SerializedLayoutTest.java @@ -16,14 +16,13 @@ */ package org.apache.logging.log4j.core.layout; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.IOException; import java.io.ObjectInputStream; import java.util.List; import java.util.Map; @@ -39,8 +38,8 @@ import org.apache.logging.log4j.core.test.BasicConfigurationFactory; import org.apache.logging.log4j.core.test.appender.ListAppender; import org.apache.logging.log4j.message.SimpleMessage; +import org.apache.logging.log4j.test.junit.SerialUtil; import org.apache.logging.log4j.test.junit.ThreadContextRule; -import org.apache.logging.log4j.util.FilteredObjectInputStream; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; @@ -56,19 +55,11 @@ public class SerializedLayoutTest { static ConfigurationFactory cf = new BasicConfigurationFactory(); - static boolean useObjectInputStream = false; - @Rule public final ThreadContextRule threadContextRule = new ThreadContextRule(); @BeforeClass public static void setupClass() { - try { - Class.forName("java.io.ObjectInputFilter"); - useObjectInputStream = true; - } catch (final ClassNotFoundException ex) { - // Ignore the exception - } ConfigurationFactory.setConfigurationFactory(cf); final LoggerContext ctx = LoggerContext.getContext(); ctx.reconfigure(); @@ -130,20 +121,11 @@ public void testLayout() throws Exception { appender.stop(); final List data = appender.getData(); - assertTrue(data.size() > 0); + assertFalse(data.isEmpty()); int i = 0; for (final byte[] item : data) { - final ByteArrayInputStream bais = new ByteArrayInputStream(item); - final ObjectInputStream ois = - useObjectInputStream ? new ObjectInputStream(bais) : new FilteredObjectInputStream(bais); - LogEvent event; - try { - event = (LogEvent) ois.readObject(); - } catch (final IOException ioe) { - System.err.println("Exception processing item " + i); - throw ioe; - } - assertTrue("Incorrect event", event.toString().equals(expected[i])); + assertEquals( + "Incorrect event", expected[i], SerialUtil.deserialize(item).toString()); ++i; } for (final Appender app : appenders.values()) { @@ -175,8 +157,7 @@ public void testDeserialization() throws Exception { testSerialization(); final File file = new File(DAT_PATH); final FileInputStream fis = new FileInputStream(file); - try (final ObjectInputStream ois = - useObjectInputStream ? new ObjectInputStream(fis) : new FilteredObjectInputStream(fis)) { + try (final ObjectInputStream ois = SerialUtil.getObjectInputStream(fis)) { final LogEvent event = (LogEvent) ois.readObject(); assertNotNull(event); } diff --git a/src/changelog/.2.x.x/LOG4J2-3680_fix_serial_filter_arrays.xml b/src/changelog/.2.x.x/LOG4J2-3680_fix_serial_filter_arrays.xml new file mode 100644 index 00000000000..a8b871472f7 --- /dev/null +++ b/src/changelog/.2.x.x/LOG4J2-3680_fix_serial_filter_arrays.xml @@ -0,0 +1,10 @@ + + + + + Allow deserialization of all arrays of allowed classes. + + diff --git a/src/changelog/2.22.0/1906_harden_serialization_process.xml b/src/changelog/2.22.0/1906_harden_serialization_process.xml new file mode 100644 index 00000000000..0164b143efd --- /dev/null +++ b/src/changelog/2.22.0/1906_harden_serialization_process.xml @@ -0,0 +1,10 @@ + + + + + Harden deserialization process by requiring the usage of `FilteredObjectInputStream` on Java 8 and `ObjectInputFilter` on Java 9+ to deserialize custom classes. + +