[CONFIGURATION-841] StackOverflowError calling
ListDelimiterHandler.flatten(Object, int) with a cyclical object tree
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 82dfd26..7645c20 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -26,7 +26,8 @@
<release version="2.10.1" date="YYYY-MM-DD" description="Minor release with new features and updated dependencies.">
<!-- FIX -->
<action type="fix" issue="CONFIGURATION-840" dev="ggregory" due-to="Gary Gregory">StackOverflowError adding property in AbstractListDelimiterHandler.flattenIterator().</action>
- <action type="fix" issue="CONFIGURATION-840" dev="ggregory" due-to="Bob Marinier, Gary Gregory">Version 2.10.0 fails java.lang.module.FindException: Module servlet.api not found.</action>
+ <action type="fix" issue="CONFIGURATION-840" dev="ggregory" due-to="Bob Marinier, Gary Gregory">java.lang.module.FindException: Module servlet.api not found.</action>
+ <action type="fix" issue="CONFIGURATION-841" dev="ggregory" due-to="Gary Gregory">StackOverflowError calling ListDelimiterHandler.flatten(Object, int) with a cyclical object tree.</action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Dependabot">Bump jackson-databind from 2.16.1 to 2.17.0 #297, #303, #326, #331, #340, #378.</action>
<action type="update" dev="ggregory" due-to="Dependabot">Bump log4j.version from 2.23.0 to 2.23.1 #379.</action>
diff --git a/src/main/java/org/apache/commons/configuration2/convert/ListDelimiterHandler.java b/src/main/java/org/apache/commons/configuration2/convert/ListDelimiterHandler.java
index 902e9cd..61b2407 100644
--- a/src/main/java/org/apache/commons/configuration2/convert/ListDelimiterHandler.java
+++ b/src/main/java/org/apache/commons/configuration2/convert/ListDelimiterHandler.java
@@ -20,7 +20,6 @@
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
-import java.util.Set;
/**
* <p>
@@ -94,9 +93,7 @@
* @since 2.9.0
*/
default Collection<?> flatten(final Object value, final int limit) {
- final Set<Object> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
- dejaVu.add(value);
- return AbstractListDelimiterHandler.flatten(this, value, limit, dejaVu);
+ return AbstractListDelimiterHandler.flatten(this, value, limit, Collections.newSetFromMap(new IdentityHashMap<>()));
}
/**
diff --git a/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java b/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java
index 40462be..da85e4b 100644
--- a/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestPropertiesConfiguration.java
@@ -466,6 +466,22 @@
assertEquals(object, result);
}
+ @ParameterizedTest
+ @ValueSource(ints = { 0, 2, 4, 8, 16 })
+ public void testCompress840ArrayListCycle(final int size) {
+ final ArrayList<Object> object = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ object.add(i);
+ object.add(object);
+ object.add(new ArrayList<>(object));
+ }
+ final Collection<?> result = testCompress840(object);
+ assertNotNull(result);
+ assertEquals(size, result.size());
+ object.add(object);
+ testCompress840(object);
+ }
+
@Test
public void testCompress840BeanContextServicesSupport() {
testCompress840(new BeanContextServicesSupport());
@@ -494,6 +510,28 @@
@ParameterizedTest
@ValueSource(ints = { 0, 2, 4, 8, 16 })
+ public void testCompress840Exception(final int size) {
+ final ArrayList<Object> object = new ArrayList<>();
+ final Exception bottom = new Exception();
+ object.add(bottom);
+ Exception top = bottom;
+ for (int i = 0; i < size; i++) {
+ object.add(i);
+ top = new Exception(top);
+ object.add(top);
+ }
+ if (bottom != top) {
+ // direct self-causation is not allowed.
+ bottom.initCause(top);
+ }
+ final Collection<?> result = testCompress840(object);
+ assertNotNull(result);
+ assertEquals(size * 2 + 1, result.size());
+ assertEquals(object, result);
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { 0, 2, 4, 8, 16 })
public void testCompress840Path(final int size) {
final PriorityQueue<Object> object = new PriorityQueue<>();
for (int i = 0; i < size; i++) {