diff --git a/lucene/core/src/java/org/apache/lucene/util/graph/GraphTokenStreamFiniteStrings.java b/lucene/core/src/java/org/apache/lucene/util/graph/GraphTokenStreamFiniteStrings.java index a7005012b730..9d74daaf218b 100644 --- a/lucene/core/src/java/org/apache/lucene/util/graph/GraphTokenStreamFiniteStrings.java +++ b/lucene/core/src/java/org/apache/lucene/util/graph/GraphTokenStreamFiniteStrings.java @@ -48,6 +48,10 @@ * This class also provides helpers to explore the different paths of the {@link Automaton}. */ public final class GraphTokenStreamFiniteStrings { + + /** Maximum level of recursion allowed in recursive operations. */ + private static final int MAX_RECURSION_LEVEL = 1000; + private final Map idToTerm = new HashMap<>(); private final Map idToInc = new HashMap<>(); private final Automaton det; @@ -262,8 +266,15 @@ private int getTermID(int incr, int prevIncr, BytesRef term) { return id; } - private static void articulationPointsRecurse(Automaton a, int state, int d, int[] depth, int[] low, int[] parent, - BitSet visited, List points) { + private static void articulationPointsRecurse( + Automaton a, + int state, + int d, + int[] depth, + int[] low, + int[] parent, + BitSet visited, + List points) { visited.set(state); depth[state] = d; low[state] = d; @@ -275,7 +286,12 @@ private static void articulationPointsRecurse(Automaton a, int state, int d, int a.getNextTransition(t); if (visited.get(t.dest) == false) { parent[t.dest] = state; - articulationPointsRecurse(a, t.dest, d + 1, depth, low, parent, visited, points); + if (d < MAX_RECURSION_LEVEL) { + articulationPointsRecurse(a, t.dest, d + 1, depth, low, parent, visited, points); + } else { + throw new IllegalArgumentException( + "Exceeded maximum recursion level during graph analysis"); + } childCount++; if (low[t.dest] >= depth[state]) { isArticulation = true; diff --git a/lucene/core/src/test/org/apache/lucene/util/graph/TestGraphTokenStreamFiniteStrings.java b/lucene/core/src/test/org/apache/lucene/util/graph/TestGraphTokenStreamFiniteStrings.java index 44b7b7c4dec4..27a70d14deae 100644 --- a/lucene/core/src/test/org/apache/lucene/util/graph/TestGraphTokenStreamFiniteStrings.java +++ b/lucene/core/src/test/org/apache/lucene/util/graph/TestGraphTokenStreamFiniteStrings.java @@ -16,6 +16,7 @@ */ package org.apache.lucene.util.graph; +import java.util.ArrayList; import java.util.Iterator; import org.apache.lucene.analysis.CannedTokenStream; @@ -596,4 +597,27 @@ public void testMultipleSidePaths() throws Exception { terms = graph.getTerms("field", 7); assertArrayEquals(terms, new Term[] {new Term("field", "network")}); } + + public void testLongTokenStreamStackOverflowError() throws Exception { + + ArrayList tokens = + new ArrayList() { + { + add(token("fast", 1, 1)); + add(token("wi", 1, 1)); + add(token("wifi", 0, 2)); + add(token("fi", 1, 1)); + } + }; + + // Add in too many tokens to get a high depth graph + for (int i = 0; i < 1024 + 1; i++) { + tokens.add(token("network", 1, 1)); + } + + TokenStream ts = new CannedTokenStream(tokens.toArray(new Token[0])); + GraphTokenStreamFiniteStrings graph = new GraphTokenStreamFiniteStrings(ts); + + assertThrows(IllegalArgumentException.class, graph::articulationPoints); + } } diff --git a/lucene/ivy-versions.properties b/lucene/ivy-versions.properties index 1c6be1ebf13b..2b6f3ae9664a 100644 --- a/lucene/ivy-versions.properties +++ b/lucene/ivy-versions.properties @@ -82,7 +82,7 @@ io.prometheus.version = 0.2.0 /javax.activation/activation = 1.1.1 /javax.servlet/javax.servlet-api = 3.1.0 /joda-time/joda-time = 2.2 -/junit/junit = 4.12 +/junit/junit = 4.13.2 /mecab/mecab-ipadic = 2.7.0-20070801 /mecab/mecab-ko-dic = 2.0.3-20170922 diff --git a/lucene/version.properties b/lucene/version.properties index cc949ac66637..7b84caadfe75 100644 --- a/lucene/version.properties +++ b/lucene/version.properties @@ -2,7 +2,7 @@ # RELEASE MANAGER must change this file after creating a release and # enter new base version (format "x.y.z", no prefix/appendix): -version.base=7.7.3 +version.base=7.7.3.1 # Other version property defaults, don't change: version.suffix=SNAPSHOT