Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CVE-2022-40151] Fix StackOverflowError #316

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions xstream/src/java/com/thoughtworks/xstream/XStream.java
Expand Up @@ -189,6 +189,7 @@
import com.thoughtworks.xstream.converters.time.YearMonthConverter;
import com.thoughtworks.xstream.converters.time.ZoneIdConverter;
import com.thoughtworks.xstream.converters.time.ZonedDateTimeConverter;
import com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy;
import com.thoughtworks.xstream.core.ClassLoaderReference;
import com.thoughtworks.xstream.core.DefaultConverterLookup;
import com.thoughtworks.xstream.core.JVM;
Expand Down Expand Up @@ -1134,6 +1135,20 @@ public void setMarshallingStrategy(final MarshallingStrategy marshallingStrategy
this.marshallingStrategy = marshallingStrategy;
}

/**
* Set the recursion depth limit.
*
* @param recursionDepthLimit the recursion depth limit
* @throws UnsupportedOperationException if the recursion depth limit cannot be set
*/
public void setRecursionDepthLimit(int recursionDepthLimit) {
if (marshallingStrategy instanceof AbstractTreeMarshallingStrategy) {
((AbstractTreeMarshallingStrategy) marshallingStrategy).setRecursionDepthLimit(recursionDepthLimit);
} else {
throw new UnsupportedOperationException("Recursion depth limit can only be set for tree marshalling strategy");
}
}

/**
* Serialize an object to a pretty-printed XML String.
*
Expand Down
Expand Up @@ -27,10 +27,19 @@
*/
public abstract class AbstractTreeMarshallingStrategy implements MarshallingStrategy {

private int recursionDepthLimit = -1;

public void setRecursionDepthLimit(int recursionDepthLimit) {
this.recursionDepthLimit = recursionDepthLimit;
}

@Override
public Object unmarshal(final Object root, final HierarchicalStreamReader reader, final DataHolder dataHolder,
final ConverterLookup converterLookup, final Mapper mapper) {
final TreeUnmarshaller context = createUnmarshallingContext(root, reader, converterLookup, mapper);
if (recursionDepthLimit != -1) {
context.setRecursionDepthLimit(recursionDepthLimit);
}
return context.start(dataHolder);
}

Expand Down
Expand Up @@ -37,6 +37,7 @@ public class TreeUnmarshaller implements UnmarshallingContext {
private final FastStack<Class<?>> types = new FastStack<>(16);
private DataHolder dataHolder;
private final PrioritizedList<Runnable> validationList = new PrioritizedList<>();
private int recursionDepthLimit = 500;

public TreeUnmarshaller(
final Object root, final HierarchicalStreamReader reader, final ConverterLookup converterLookup,
Expand All @@ -47,6 +48,10 @@ public TreeUnmarshaller(
this.mapper = mapper;
}

protected void setRecursionDepthLimit(int recursionDepthLimit) {
this.recursionDepthLimit = recursionDepthLimit;
}

@Override
public Object convertAnother(final Object parent, final Class<?> type) {
return convertAnother(parent, type, null);
Expand All @@ -70,6 +75,9 @@ public Object convertAnother(final Object parent, Class<?> type, Converter conve

protected Object convert(final Object parent, final Class<?> type, final Converter converter) {
types.push(type);
if (types.size() > recursionDepthLimit) {
throw new ConversionException("TreeUnmarshaller has reached recursion depth limit of " + recursionDepthLimit);
}
try {
return converter.unmarshal(reader, this);
} catch (final ConversionException conversionException) {
Expand Down
Binary file not shown.
Expand Up @@ -17,6 +17,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Iterator;

import com.thoughtworks.xstream.XStreamException;
Expand Down Expand Up @@ -252,4 +255,18 @@ public void testExplicitlyUnmarshalEndlessByteArryInputStream() throws IOExcepti
assertEquals("Unlimited reads of ByteArrayInputStream returning 0 bytes expected", 0, i);
}
}

public void testCVE202240151() throws Exception {
final String xml = new String(
Files.readAllBytes(Paths.get(getClass().getResource("CVE-2022-40151.xml").toURI())),
StandardCharsets.UTF_8);

try {
xstream.setRecursionDepthLimit(600);
xstream.fromXML(xml);
fail("Thrown " + ConversionException.class.getName() + " expected");
} catch (final ConversionException e) {
assertTrue(e.getMessage().contains("TreeUnmarshaller has reached recursion depth limit of 600"));
}
}
}