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

cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git


The following commit(s) were added to refs/heads/master by this push:
     new f9797cdfe0 FELIX-6619 : Configadmin interpolator can run into a 
StackOverflow
f9797cdfe0 is described below

commit f9797cdfe0036b378afba2699602e0a679282249
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Thu Jul 20 07:47:22 2023 +0200

    FELIX-6619 : Configadmin interpolator can run into a StackOverflow
---
 .../plugin/interpolation/Interpolator.java         | 42 ++++++++++++++--------
 .../plugin/interpolation/InterpolatorTest.java     | 11 ++++++
 2 files changed, 39 insertions(+), 14 deletions(-)

diff --git 
a/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Interpolator.java
 
b/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Interpolator.java
index 8fd727869f..640af5956f 100755
--- 
a/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Interpolator.java
+++ 
b/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Interpolator.java
@@ -18,7 +18,9 @@ package org.apache.felix.configadmin.plugin.interpolation;
 
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -109,6 +111,10 @@ public class Interpolator {
      * @return Replaced object (or original value)
      */
     public static Object replace(final String value, final Provider provider) {
+        return replaceInternal(value, provider, new HashSet<>());
+    }
+
+    private static Object replaceInternal(final String value, final Provider 
provider, final Set<String> visited) {
         String result = value;
         int index = -1;
         while (index < result.length()) {
@@ -136,7 +142,7 @@ public class Interpolator {
             final Map<String, String> directives;
             final String name;
             if (dirPos != -1) {
-                directives = parseDirectives(replace(postfix.substring(dirPos 
+ 1), provider).toString());
+                directives = 
parseDirectives(replaceInternal(postfix.substring(dirPos + 1), provider, 
visited).toString());
                 name = postfix.substring(0, dirPos);
             } else {
                 directives = Collections.emptyMap();
@@ -144,22 +150,30 @@ public class Interpolator {
             }
 
             // recursive replacement
-            final Object newName = replace(name, provider);
+            final Object newName = replaceInternal(name, provider, visited);
+
+            final String id = type.concat(":").concat(name);
+            if (!visited.add(id)) {
+                // endless recursion -> leave as is and continue
+               index = index + START.length();
 
-            Object replacement = provider.provide(type, newName.toString(), 
directives);
-            if (replacement == null) {
-                // no replacement found -> leave as is and continue
-                index = index + START.length();
             } else {
-                // if replacement is not a string and placeholder is complete 
string, return that object
-                if (!(replacement instanceof String) && boundaries[0] == 0 && 
boundaries[1] == result.length() - 1) {
-                    return replacement;
+                Object replacement = provider.provide(type, 
newName.toString(), directives);
+                if (replacement == null) {
+                    // no replacement found -> leave as is and continue
+                    index = index + START.length();
+                } else {
+                    // if replacement is not a string and placeholder is 
complete string, return that object
+                    if (!(replacement instanceof String) && boundaries[0] == 0 
&& boundaries[1] == result.length() - 1) {
+                        return replacement;
+                    }
+                    // replace and continue with replacement
+                    replacement = replaceInternal(replacement.toString(), 
provider, visited);
+                    final String val = replacement.toString();
+                    result = result.substring(0, 
boundaries[0]).concat(val).concat(result.substring(boundaries[1] + 1));
+                    index = boundaries[0] + val.length();
                 }
-                // replace and continue with replacement
-                replacement = replace(replacement.toString(), provider);
-                final String val = replacement.toString();
-                result = result.substring(0, 
boundaries[0]).concat(val).concat(result.substring(boundaries[1] + 1));
-                index = boundaries[0] + val.length();
+                visited.remove(id);
             }
         }
         return result;
diff --git 
a/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolatorTest.java
 
b/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolatorTest.java
index b2132cad33..9197c5d9f8 100755
--- 
a/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolatorTest.java
+++ 
b/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolatorTest.java
@@ -64,6 +64,17 @@ public class InterpolatorTest {
                 }));
     }
 
+    @Test
+    public void testRecursionViaPlaceholder() {
+        assertEquals("$[foo:bar]",
+                Interpolator.replace("$[foo:bar]", (type, name, dir) -> {
+                    if ("foo".equals(type) && "bar".equals(name)) {
+                        return "$[foo:bar]";
+                    }
+                    return null;
+                }));
+    }
+
     @Test
     public void testEscaping() {
         final Provider p = new Provider() {

Reply via email to