Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@
public final class SerializationUtil {
public static final List<String> REQUIRED_JAVA_CLASSES = List.of();
public static final List<String> REQUIRED_JAVA_PACKAGES = List.of();

public static String stripArray(final Class<?> clazz) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -58,16 +72,33 @@ public static byte[] serialize(final Serializable obj) {
@SuppressFBWarnings("OBJECT_DESERIALIZATION")
public static <T> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -149,7 +150,20 @@ public void testSafeWithMutableParams() { // LOG4J2-763
}

static Stream<Object> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Arguments> 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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -63,7 +64,7 @@ public Collection<String> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> REQUIRED_JAVA_PACKAGES = Arrays.asList(
"java.lang.", "java.time", "java.util.", "org.apache.logging.log4j.", "[Lorg.apache.logging.log4j.");
public static final List<String> 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();
Expand Down Expand Up @@ -133,5 +139,60 @@ public static void assertFiltered(final java.io.ObjectInputStream stream) {
}
}

/**
* Gets the class name of an array component recursively.
* <p>
* If {@code clazz} is not an array class its name is returned.
* </p>
* @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.
* <p>
* If {@code name} is not the name of an array class it is returned unchanged.
* </p>
* @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() {}
}
Loading