One nesting level is <BooleanQuery><Clause occurs="must">...</Clause></BooleanQuery>, 60 bytes of XML; the repro wraps that around one <TermQuery> 100,000 times.
CoreParser.parse(InputStream)reads the XML text with the JDK's XML parser into a DOM tree, then callsgetQuery(Element)on the root. Applications may also callgetQuery(Element)with a DOM they built.QueryBuilderFactorylooks up a builder by element name (BooleanQuery,TermQuery,SpanOr, ...) and calls it.- A compound builder such as
BooleanQueryBuilderbuilds each child by calling the factory again. So the depth of the Java call stack grows with the depth of the XML.
StackOverflowError escapes where a Query or ParserException is expected. Select the still to open it at full size. Generated with AI, checked against the code.- You parse a query with
CoreParser.parse(InputStream):<BooleanQuery><Clause occurs="must">repeated 3,000 times around one<TermQuery>, about 180 KB of well-formed XML. The shipped DTD says elements nest to "an arbitrary depth and complexity", and 40 levels parse fine. - You expect a
Query, or theParserExceptionthe method declares, whichparseXMLalready throws for everyExceptionfrom the XML parser. - Instead, on 10.6.0 with Java 21 and a 1 MB thread stack,
java.lang.StackOverflowErrorescapes (2,000 levels still parse; on the 2 MB default stack it usually takes 5,000, about 300 KB).catch (ParserException e)andcatch (Exception e)miss it. - Why:
BooleanQueryBuilderbuilds its child by callingQueryBuilderFactory.getQuery, which calls the next builder: two Java frames per level, never counted. The DisjunctionMax, ConstantScore and span builders do the same. - The JDK decides where it shows: Java 25, main's minimum, rejects XML text deeper than 100 elements with a
ParserException, but no JDK XML setting applies togetQuery(Element)on a DOM the application builds: it overflowed on every JDK tested (3,000 levels at-Xss1mon main).
Applications using the opt-in XML query parser (CoreParser and subclasses, default settings) that rely on ParserException for bad input: on 10.x with Java 21 through plain parse(InputStream); on any JDK through getQuery(Element) on an application-built DOM. Real queries nest a handful of levels; this needs thousands. Only the error type is wrong: no query is returned, the JVM continues.
Mechanism, terms and numbers797 words
CoreParser turns query XML into a Lucene Query by recursion: each compound builder (BooleanQuery, DisjunctionMaxQuery, ConstantScoreQuery and the span builders) calls back into QueryBuilderFactory or SpanQueryBuilderFactory for its children, so every nesting level adds two Java frames, and nothing counts them. Well-formed XML from the documented vocabulary, nested a few thousand levels deep, therefore throws java.lang.StackOverflowError out of parse(InputStream), getQuery(Element) and getSpanQuery(Element), which declare only ParserException. Whether the text entry gets that far depends on the JDK's XML parser: Java 21, the 10.x minimum, has no element-depth limit by default, so on 10.6.0 plain parse(InputStream) overflows at 3,000 nested BooleanQuery levels (about 180 KB) on a 1 MB stack and 5,000 (about 300 KB) on the 2 MB default; Java 25, main's minimum, rejects text deeper than 100 elements with a ParserException (tested builds: OpenJDK 21.0.10 and 25.0.3). No JDK XML setting applies to the DOM entries; they overflowed on every JDK tested. Nothing else goes wrong: no query is returned, no index effect, and the JVM continues. The recursion dates from the parser's original contribution (2006; in the 1.9 release tag, first published as a jar in 1.9.1). A nesting limit in CoreParser, the approach #16423 took for the expressions parser in 10.6.0, fixes it; the repro and the 69 existing XML-parser tests pass with it.
CoreParser: the XML query parser inlucene-queryparser(org.apache.lucene.queryparser.xml), with the subclassesCorePlusQueriesParserandCorePlusExtensionsParser. Its element vocabulary is documented inLuceneCoreQuery.dtd.- Entry points:
parse(InputStream)(XML text), and the publicgetQuery(Element)/getSpanQuery(Element)(a DOM element the caller supplies). All three declare onlyParserException. - Element-depth limit: the JDK's XML parser setting
jdk.xml.maxElementDepth. In the tested OpenJDK 25.0.3 build,conf/jaxp.properties:184sets it to 100; in the tested 21.0.10 build, line 163 is commented out (# jdk.xml.maxElementDepth=0), so there is no limit (other builds were not checked).
CoreParser.java:141-143:parse(InputStream)returnsgetQuery(parseXML(xmlStream).getDocumentElement()).parseXML(:182-206) turns off validation (:184), enablesFEATURE_SECURE_PROCESSING(:186; aParserConfigurationExceptionthere is ignored,:187-192) and wraps everyExceptionfrom creating the parser or parsing the text in aParserException(:196-198,:203-205); it catchesException, notError. Lucene sets no depth limit of its own.CoreParser.java:208-216:getQuery(Element)andgetSpanQuery(Element)hand the element straight toqueryFactory/spanFactory, with no depth state.QueryBuilderFactory.java:29-35looks up the builder by element name and calls it (:34).BooleanQueryBuilder.java:57callsfactory.getQuery(clauseQuery)for each clause;DisjunctionMaxQueryBuilder.java:54andConstantScoreQueryBuilder.java:41do the same, and the span builders callSpanQueryBuilderFactory.java:46(SpanOrBuilder.java:41,SpanNearBuilder.java:45,SpanFirstBuilder.java:38,SpanNotBuilder.java:42-43,SpanPositionRangeBuilder.java:39).- Each nesting level adds a builder frame and a factory frame. The executed trace cycles
BooleanQueryBuilder.getQuery(BooleanQueryBuilder.java:57)andQueryBuilderFactory.getQuery(QueryBuilderFactory.java:34)untilStackOverflowError. - Two more recursions, found by the hunt's static model and exercised only in its sweep:
DOMUtils.getTextBuffer(DOMUtils.java:157-177, once per nested element at:167and per entity reference at:172) andDOMUtils.getAttributeWithInheritance(:88-101, once per ancestor). These four cycles are the XML module's own; the classicQueryParserthatUserInputQueryBuildercalls (UserInputQueryBuilder.java:59-73) and custom builders are outside the model.
| Lucene, JDK, entry | -Xss1m | default stack (2 MB) |
|---|---|---|
10.6.0 commit, Java 21, parse(InputStream), nested BooleanQuery | 2,000 parse, 3,000 (about 180 KB) overflow | 3,000 parse, 5,000 (about 300 KB) overflow (7 of 10 runs in the fact check; 7,500 in 4 of 4) |
same, nested <SpanOr> | 2,000 parse, 3,000 (about 51 KB) overflow | 5,000 parse, 7,500 overflow |
main, Java 25, getQuery(Element), nested BooleanQuery | 2,000 parse, 3,000 overflow | 5,000 parse, 7,500 overflow |
main, Java 25, parse(InputStream) | ParserException from 101 elements (the JDK's limit) | same |
main, Java 25, parse(InputStream), -Djdk.xml.maxElementDepth=0 | 5,000 parse, 10,000 overflow |
Rows 1-4 come from the hunt's 152-JVM sweep: one JVM per shape, entry, stack and JDK, trying depths 50, 100, ..., 2,000, 3,000, 5,000, 7,500, ... in ascending order until the first overflow. The 1,000 / 3,000 / 5,000 / 10,000-level points of rows 1 and 3 were also run as cold single calls, one call per fresh JVM, with the same outcome, and row 5 comes from those cold single calls only; the audit probe (ascending, one JVM) agrees on row 3. The fact check re-ran row 1 on the 10.6.0 release jars: at 1 MB, 2,000 levels parsed and 3,000 overflowed 6 of 6 times each. The main rows ran on a9779ef3c193; no commit touches the xml module between it and 645d4fe14a20. Warm JVMs need several times more levels (the independent reproduction: 1,933 to 24,872 on the 10.6.0 commit's jars with Java 21, 256 KB to 2 MB stacks). x86_64 was not measured.
Exposure and likelihood255 words
- 10.x and 9.x on Java 21: the plain
parse(InputStream)of well-formed XML, default settings (executed on the 10.6.0 commit, 10.5.2 and 9.12.3; 9.12.3 also on Java 17). - Any JDK (no JDK XML setting applies to a DOM):
getQuery(Element)(executed on Java 17, 21 and 25) /getSpanQuery(Element)(21 and 25) with a DOM the application builds (a 2019 java-user@ thread shows applications doing this). - Main: also with another JAXP implementation on the classpath (xercesImpl 2.12.2, executed) or a relaxed
jdk.xml.maxElementDepth(executed with 0). - Default vs opt-in: the XML query parser is an opt-in module class; the trigger needs no non-default setting.
- Likelihood: contrived. On 1-2 MB stacks it takes about 3,000-5,000 levels; only an adversary or a broken generator produces that.
- Effect: the calling thread gets
java.lang.StackOverflowError, whichcatch (ParserException)andcatch (Exception)handlers miss. No query is returned, no index or data is touched, and the JVM and later parses continue (CoreParserkeeps no per-parse state;QueryBuilder.java:22-26requires thread-safe builders). - The parser fails first: a nest of one clause per level around one term still searches where the parser already overflows (1 MB stack: 2,000 levels parse and
IndexSearcher.countreturns 1, 3,000 overflow in the parser; 2 MB: 5,000 parse and search, 10,000 overflow in the parser). Core search is not unbounded either:IndexSearcher.countandtoStringof the same tree built in Java overflowed at 5,000 and 7,500 levels on a 1 MB stack (JIT-dependent), so the parser is the earliest limit, not the only one.
One JUnit class, one test method, queryparser module, public API: it builds 100,000 nested <BooleanQuery><Clause occurs="must"> levels as a DOM around one <TermQuery> and calls CoreParser.getQuery(Element). A Query or ParserException passes; StackOverflowError fails. Deterministic, single thread, no index; fails on main (Java 25), and no JDK XML setting protects the DOM entry.
/*
* 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.lucene.queryparser.xml;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.lucene.tests.analysis.MockAnalyzer;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class TestCoreParserDeepNesting extends LuceneTestCase {
public void testDeeplyNestedBooleanQuery() throws Exception {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element query = doc.createElement("TermQuery");
query.setAttribute("fieldName", "f");
query.setTextContent("a");
for (int i = 0; i < 100_000; i++) {
Element clause = doc.createElement("Clause");
clause.setAttribute("occurs", "must");
clause.appendChild(query);
query = doc.createElement("BooleanQuery");
query.appendChild(clause);
}
CoreParser parser = new CoreParser("f", new MockAnalyzer(random()));
// expected: a Query or the declared ParserException; actual: StackOverflowError
try {
parser.getQuery(query);
} catch (@SuppressWarnings("unused") ParserException expected) {
} catch (StackOverflowError e) {
throw new AssertionError("expected a Query or ParserException, got StackOverflowError", e);
}
}
}
cd $LUCENE && ./gradlew :lucene:queryparser:test --tests TestCoreParserDeepNesting -Ptests.seed=DEADBEEF -Ptests.asserts=true
- Expected
getQuery(Element)returns aQueryor throws the declaredParserException.- Observed
expected a Query or ParserException, got StackOverflowError, the stack cyclingBooleanQueryBuilder.java:57andQueryBuilderFactory.java:34, with assertions on and off.
More files, environment, full output407 words
It uses the DOM entry because that fails on main, where Java 25's XML depth limit guards the text path. 100,000 levels give a wide margin over the 3,000-10,000 needed on 1-2 MB stacks; on a much larger thread stack the stock code could return a Query and the test would pass (not tried). The whole test method, DOM build and failing call, runs in about 0.1-0.3 s (gradle test reports). ./gradlew tidy re-wraps the one-line catch (@SuppressWarnings("unused") ParserException expected) onto three lines; nothing else changes. The line numbers in the observed trace are those of the file as shipped.
On 10.x the text path behaves as follows (no second test needed): on 10.6.0 with Java 21 and -Xss1m, parse(InputStream) of 3,000 nested BooleanQuery levels (about 180 KB) throws StackOverflowError, while Java 25 gives ParserException from the JDK's depth limit. The hunt's and the independent reproduction's runs swapped only the JDK binaries in the harness's generated command (the harness is pinned to Java 25); the fact check re-ran it with its own probe on the 10.6.0 release jars and OpenJDK 21.0.10: 2,000 levels parse and 3,000 overflow, 6 of 6 times each.
Regression test: this same class (or its one method added to TestCoreParser). It passes with the recommended fix and asserts only the contract (a Query or ParserException), not a particular limit. On its own it does not show whether the fix's depth check or its fallback fired; the validation-only 511/512 method covers the boundary.
| lucene | main 645d4fe14a20 (11.0.0-SNAPSHOT): fails; 10.6.0 and 9.12.3 release jars: fail; with the fix: passes; upstream main 2f424fd428da has the same code |
|---|---|
| jdk | OpenJDK 25.0.3 (build 25.0.3+9-LTS) |
| os | Linux, aarch64 (the only platform run) |
cp $REPRO/TestCoreParserDeepNesting.java $LUCENE/lucene/queryparser/src/test/org/apache/lucene/queryparser/xml/
cd $LUCENE && ./gradlew :lucene:queryparser:test --tests TestCoreParserDeepNesting -Ptests.seed=DEADBEEF -Ptests.asserts=true
# assertions off fails the same way: -Ptests.asserts=false
# gradle, stock 645d4fe14a20, -Ptests.seed=DEADBEEF -Ptests.asserts=false (test-result XML; stack trimmed)
java.lang.AssertionError: expected a Query or ParserException, got StackOverflowError
at __randomizedtesting.SeedInfo.seed([DEADBEEF:176E3F6D1EC607DC]:0)
at org.apache.lucene.queryparser.xml.TestCoreParserDeepNesting.testDeeplyNestedBooleanQuery(TestCoreParserDeepNesting.java:45)
Caused by: java.lang.StackOverflowError
at java.base/java.lang.StringLatin1.compareTo(StringLatin1.java:114)
...
at org.apache.lucene.queryparser.xml.builders.BooleanQueryBuilder.getOccursValue(BooleanQueryBuilder.java:71)
at org.apache.lucene.queryparser.xml.builders.BooleanQueryBuilder.getQuery(BooleanQueryBuilder.java:54)
at org.apache.lucene.queryparser.xml.QueryBuilderFactory.getQuery(QueryBuilderFactory.java:34)
at org.apache.lucene.queryparser.xml.builders.BooleanQueryBuilder.getQuery(BooleanQueryBuilder.java:57)
at org.apache.lucene.queryparser.xml.QueryBuilderFactory.getQuery(QueryBuilderFactory.java:34)
... (the last two frames repeat to the end of the recorded trace)
:lucene:queryparser:test (FAILURE): 1 test, 1 failure
# -Ptests.asserts=true: the same failure (1 test completed, 1 failed). The frame where the stack runs out varies
# between runs. The standalone runner gives the same message on main (-ea, -da) and on the 10.6.0 and 9.12.3 jars (-da).- The three entries declare only
ParserException;parseXMLturns everyExceptionof the XML parser into one. - The DTD says elements nest "to produce queries/filters of an arbitrary depth and complexity"; nothing documents a limit.
- One input and the same 10.6.0 jars give
ParserExceptionon Java 25 andStackOverflowErroron Java 21. #16423 fixed the same defect in the expressions parser.
The full argument254 words
- Declared exception.
parse(InputStream),getQuery(Element)andgetSpanQuery(Element)declare onlyParserException(CoreParser.java:141,:209,:214), documented as "Thrown when the xml queryparser encounters invalid syntax/configuration" (ParserException.java:19);parseXMLwraps everyExceptionfrom the XML parser (CoreParser.java:196-205; it catchesException, so anErroris not wrapped). This is an expectation from the throws clause, not a@throwsguarantee:parsehas no@throwstext (CoreParser.java:137-140), and a checkedthrowsclause promises nothing aboutErrors. - Documented input.
LuceneCoreQuery.dtd:54-57: "Many of the elements can nest other elements to produce queries/filters of an arbitrary depth and complexity. The BooleanQuery element is one such example ... (including other BooleanQueries)". The DTD documents the vocabulary; it is not a runtime contract, sinceCoreParserdoes not validate against it (dbf.setValidating(false),CoreParser.java:184). The upstream XML tests check only shallow nesting. - JDK-dependent outcome. For one input and one Lucene release, the result flips between
ParserException(Java 25) andStackOverflowError(Java 21). That points to an accident, not a design. - Precedent. #16423 (uschindler, approved by rmuir and msokolov; 10.6.0) added a nesting limit of 1,024 and a
StackOverflowErrorfallback to the expressions parser;lucene/CHANGES.txt:551-555says it "no longer throws virtual machine errors" (JavascriptCompiler.java:104-105,:138,:266-271). On #11687 (SimpleQueryParser stack overflow, open since 2022) mikemccand asked "I wonder if our other query parsers have this problem too?"; nobody answered. - SECURITY.md:46: issues outside the security scope "are still valid bug reports and hardening suggestions" that can be filed publicly.
The devil's-advocate check250 words
The strongest case for "intended" (rated strong by reading-only cross-model lenses, moderate by the intended-behaviour check): (1) a throws clause says nothing about VirtualMachineErrors, and deep nesting is valid syntax, so "invalid syntax/configuration" arguably does not apply; (2) SECURITY.md:38 names "deeply nested queries" as input the application must bound, and SECURITY.md:42 trusts caller-supplied data structures such as a DOM; (3) on #752 rmuir wrote "If you hit StackOverFlowError, really you should let the VM exit", and on #16423 uschindler wrote "The stopped JVM is actually not a bug in Lucene" (#16423 is labelled an enhancement); (4) on main the stock text path is already guarded by the JDK 25 limit; (5) core search also overflows on deep trees; (6) #16820 proposes deprecating the XML parser for Lucene 11 ("mainly a source of security issues", romseygeek).
Why it fails: none of this makes the Error the documented behaviour of a method whose only declared failure is ParserException, for well-formed input the DTD invites; the result for one input flips with the JDK; and the same reviewers approved a nesting limit for the expressions parser in #16423, whose description says "this should be handled". On #752 msokolov wrote "we should not be catching Error" but "If we could somehow detect the problem before it gets to that, then throwing IAE would make sense", which is what the recommended fix does with the parser's own exception. Points (2) and (6) settle the security rating (none), not whether it is a bug.
Recommended A deterministic nesting limit in CoreParser, with a StackOverflowError fallback
In CoreParser.getQuery(Element) and getSpanQuery(Element), first walk the element's ancestors and subtree without recursion and throw ParserException past a depth of 1,024 DOM nodes, about 511 BooleanQuery levels (overridable, as #16423 did for expressions), then convert any StackOverflowError from the builders into ParserException. The repro and the 69 existing XML-parser tests pass.
@Override
public Query getQuery(Element e) throws ParserException {
checkNestingDepth(e);
try {
return queryFactory.getQuery(e);
} catch (StackOverflowError soe) {
// the depth check should catch this before, but a large limit or a small stack may not
throw new ParserException("Query nesting level too deep", soe);
}
}All options and trade-offs4 options ยท 825 words
One fix was validated in a scratch worktree of 645d4fe14a20; the alternatives were not run on their own. Whether a fix is wanted at all, given the proposal to deprecate the XML parser for Lucene 11 (#16820), is the maintainers' call; 10.6 is the last 10.x minor, so a 10.x fix would go into a 10.6.x bugfix release.
A deterministic nesting limit in CoreParser, with a StackOverflowError fallback (validated by the checks) (recommended)
The change is +73/-3 lines in CoreParser.java and LuceneCoreQuery.dtd (the hunt's sketch with wording edits, formatted with ./gradlew tidy): a public DEFAULT_MAX_NESTING_DEPTH = 1024 and a protected getMaxNestingDepth(); getQuery(Element) / getSpanQuery(Element) first run an iterative check of the element's ancestors and subtree that throws ParserException("Query nesting level too deep (maximum 1024 levels)"), then call the factory inside a StackOverflowError catch that throws ParserException. The DTD's "arbitrary depth" sentence now names the limit. The same shape uschindler used in #16423.
@Override
public Query getQuery(Element e) throws ParserException {
checkNestingDepth(e);
try {
return queryFactory.getQuery(e);
} catch (StackOverflowError soe) {
// the depth check should catch this before, but a large limit or a small stack may not
throw new ParserException("Query nesting level too deep", soe);
}
}Trade-offs
- Deterministic once a DOM reaches
getQuery(Element)/getSpanQuery(Element), whatever the JDK or JAXP implementation, with the parser's own exception; overridable like the expressions limit. On the text path the JAXP parser still builds the DOM first, and anErrorthrown there (anOutOfMemoryError, say) is not converted. The fallback turns anyStackOverflowErrorfrom a builder, a custom builder's included, into "nesting too deep". - The check counts the depth in DOM nodes (elements, text and comment nodes, and the ancestors of the element passed in; not attributes), a leaf's text node included: 511 nested
BooleanQuerylevels (1,024 nodes) are accepted, 512 rejected. So the "1024 levels" in the message and the DTD sentence means nodes. On Java 25 the JDK already rejects text deeper than 100 elements, so main's text path does not change; on Java 21, queries from 512 levels up to today's overflow point (2,000-5,000 levels cold on 1-2 MB stacks, more when warm: the independent reproduction measured up to about 24,000) would now be rejected. Counting builder levels instead is a small change. - One extra iterative pass over the whole DOM per parse, even for a large flat query; its cost was not measured.
- 1,024 nodes can still overflow a very small stack (500
BooleanQuerylevels overflow at-Xss256kin the hunt's sweep), hence the fallback. On #752 rmuir and msokolov objected to catchingStackOverflowError; #16423 later kept such a catch behind its limit. The catch can be dropped. - Covers
CoreParserand its subclasses, not direct use of the publicQueryBuilderFactory, the builders or the internalDOMUtils. - New public API (a constant and a protected method), which could stay private; the change also needs a CHANGES entry.
- The regression test asserts only the contract, so it does not show which of the two mechanisms fired; the validation-only 511/512 method covers the boundary.
What the checks validated
- Gradle
--no-daemon, scratch worktree of645d4fe14a20, OpenJDK 25.0.3, 2026-10-31. Stock: the repro fails with assertions on and off. - With the fix: the repro passes with assertions on and off;
:lucene:queryparser:test --tests 'org.apache.lucene.queryparser.xml.*'passes 73 tests, 0 failures, with seeds DEADBEEF and CAFEBABE: the 69 upstream tests (TestCoreParser22,TestCorePlusQueriesParser23,TestCorePlusExtensionsParser24), the repro, and 3 validation-only methods (511 levels parse, 512 get the depthParserException; 100,000 nestedSpanOrthroughgetSpanQueryget it too; with the limit raised toInteger.MAX_VALUE, 100,000 levels get aParserExceptioncaused byStackOverflowError). checkGoogleJavaFormat,forbiddenApisMain,ecjLintMainandrenderJavadocof:lucene:queryparserpass.- Earlier, the hunt's sketch passed the hunt's tests on main (
-ea,-da, with xercesImpl) and on the 10.6.0 commit with Java 25 and 21, and the independent reproduction's 4-test class. Not run: the whole:lucene:queryparser:test.
Wrap the factory calls in the catch above, without a limit.
Trade-offs
Smallest change, but where it fails still depends on stack size and JIT state, and it is what rmuir and msokolov objected to on #752. Not run on its own (the third validation-only method exercises the same catch).
Track the depth per call and throw ParserException past the limit.
Trade-offs
Also covers direct use of the factories, but needs per-call state (one CoreParser may parse on many threads at once, QueryBuilder.java:22-26), for example a ThreadLocal or a changed builder signature, and adds work to every builder call. Not run.
Say in the javadoc and the DTD that callers must bound nesting (for text, with jdk.xml.maxElementDepth).
Trade-offs
No code change, but it does not help the DOM entries or other JAXP implementations, and the result for one input still depends on the JDK. Not run.
Solr's xmlparser query parser passes the request's query text to SolrCoreParser.parse(InputStream) and catches only ParserException (code read, not run).
A hunt planning probe printed StackOverflowError for parse(InputStream) on the 10.6.0 jars with Java 21; an earlier hunt had tested Java 25 only. A static recursion model over the parser's bytecode then found the unbounded recursive cycles, a 152-JVM depth sweep mapped the failing depth, and a public-API JUnit followed.
The method in full255 words
Method: non-formal (the first signal was an executed probe; the model and the sweep came after, to characterise it).
- Lead. The hunt planners asked whether the XML query parser is a sibling of the expressions nesting fix (#16423) once the Java 25 XML limit is out of the way. Their
main()probes printedStackOverflowErrorforCoreParser.parse(InputStream)at 5,000 and 20,000 nestedBooleanQuerylevels on the 10.6.0 commit's jars with Java 21 (1,000 parsed), forparseon main with xercesImpl on the classpath, and forgetQuery(Element)on main at 20,000. That overturned an earlier hunt's negative, which had tested Java 25 only. - Model. A static recursion model over the real bytecode (class-hierarchy call graph, Tarjan strongly connected components, guard ranges and a depth-state detector) found four stateless, unguarded recursive cycles in the XML module's own code, reachable from
parse,getQueryandgetSpanQuery(the classicQueryParserthatUserInputQueryBuildercalls, and custom builders, are outside it); its vacuity probes were classified correctly, and theCoreParserentries flip to safe with the fix. - Sweep. 19 shape and entry pairs (10 shapes x 2 entries, minus the DOM-only
inheritshape's text entry) x 4 stack sizes x {Java 21 + 10.6.0 commit jars, Java 25 + main jars} = 152 JVMs. Each tried depths from 50 upward in ascending order and stopped at the first overflow, giving the last passing and first overflowing grid point per combination. - Checked by an independent reproducer, a devil's-advocate check, reading-only cross-model reviews, an adjudication, a credibility audit and a noise audit (see below).
No prior report found: 513 query records on 2026-10-28 (GitHub incl. the JIRA archive and downstream trackers, the mailing lists, advisories, the web) and the file lists of all 414 open PRs, re-checked 2026-10-31. Closest: #11687 (SimpleQueryParser overflow), #16423 (expressions limit, fixed) and #16820 (deprecation proposal).
Searches3 searches
Verdict new, meaning the searches below found no public report (keyword search cannot prove absence). Not fixed upstream: no commit touches the module between 645d4fe14a20 and upstream main 2f424fd428da. Related hunt rows, kept separate (other parsers, other fix sites): the surround, simple and classic parser rows (LFV-919, LFV-924, LFV-926); the flexible parser overflows the same way in its processor phase.
| Where | When | How | Found |
|---|---|---|---|
| GitHub issues, PRs and commits (apache/lucene incl. the migrated JIRA archive; Solr, Lucene.NET, Elasticsearch, OpenSearch, all of GitHub) | 2026-10-28 | five runs of a scripted prior-report search (16 GitHub, 3 commit, 2 downstream queries each), the finder's 51-query run re-read, 55 manual GitHub searches, 4 code searches, and the changed files of all 414 open apache/lucene PRs | background only: #11687, #16423, #752 / #11510, #16820, the external-entity items #15234, #16135, #9338 and SOLR-11477; stale open PRs #15053 and #12295 touch DOMUtils and keep the recursion |
| Mailing lists, JIRA, advisories and the web | 2026-10-28 | 20 list and 4 JIRA-archive queries per tool run, 41 direct list queries plus every dev@ mail of the last 40 days, 15 LUCENE and SOLR JIRA queries, GitHub security advisories and project security pages, 33 web queries | no report; a 2019 java-user@ thread shows applications calling getQuery(Element) on their own DOM |
| Refresh | 2026-10-31 | read-only gh searches (CoreParser, xml query parser, queryparser xml nesting, CoreParser StackOverflowError, nesting depth query parser) and every issue and PR created since 2026-10-28 | nothing new; #16949 touches only a surround-parser javadoc in lucene/queryparser |
Present since the parser's original contribution (87768c51c632, 2006-02-25), in the 1.9 release tag and first published as a jar in 1.9.1; never bounded. Executed as failing on 1.9.1, 2.1.0, 3.6.2, 4.0.0, 8.11.4, 9.0.0, 9.12.3, 10.0.0, 10.5.2, 10.6.0 and main.
Versions tested7 rows
Introduced by 87768c51c632 (Subversion r380874, 2006-02-25, Mark Harwood, "Added XML-query-parser module for new extensible query parser that handles queries expressed as XML"), the original contribution; no JIRA issue or PR. It already has the four original recursion families (the query and span builders calling back into their factories, DOMUtils.getTextBuffer, DOMUtils.getAttributeWithInheritance). With git blame -w -M -C -C at 645d4fe14a20, BooleanQueryBuilder.java:57, QueryBuilderFactory.java:34, SpanQueryBuilderFactory.java:46, the SpanOr, SpanNear, SpanFirst and SpanNot builder lines, DOMUtils.java:96, :167, :172 and CoreParser.java:142, :210 resolve to it; plain git blame stops them at e227a64b9d73 (LUCENE-3311, 2011, a cleanup). Later commits widened the recursion, and the other call sites blame to them: the DisjunctionMaxQuery builder (LUCENE-4004, 36bc3d70bb37, 4.0.0; DisjunctionMaxQueryBuilder.java:54), nested ConstantScoreQuery (LUCENE-6300, 544f5bf1e7b7, 5.1.0; ConstantScoreQueryBuilder.java:41), the public getSpanQuery(Element) (LUCENE-7658, 61ab4e338e35, 6.5.0; CoreParser.java:215, so earlier releases have two of the three entry points), SpanPositionRange (70162d3fb1a0, 8.3.0; SpanPositionRangeBuilder.java:39). git log -S depth / -S StackOverflowError over the module's three historical locations find no bound ever added. SOLR-11477 (2017) added FEATURE_SECURE_PROCESSING, which does not bound depth on Java 21. First release: the releases/lucene/1.9 tag (2006-02-27, "Tagged 1.9 final release") already has contrib/xml-query-parser, with no difference from 1.9.1 (read); 1.9.1 (2006-03-02) is the first version with a published lucene-xml-query-parser jar and the first one executed. On 1.9.1-2.0.0 the BooleanQuery shape runs out of heap first (a separate getElementsByTagName defect fixed for 2.1.0 in 6c53bc3c497), so 1.9.1 was run with SpanOr. Which JDK release first shipped the element-depth default of 100 was not determined.
| Version or commit | Verdict | Result |
|---|---|---|
main 645d4fe14a20 | affected | minimal repro fails with assertions on and off (DOM entry, Java 25); text entry gets ParserException from the JDK limit |
10.6.0 (release jars; commit 4ae946e5a9b9) | affected | minimal repro fails (Java 25); parse(InputStream) overflows on Java 21 (3,000 levels at -Xss1m) |
| 10.5.2, 10.0.0, 9.12.3, 9.0.0, 8.11.4 | affected | DOM entry overflows on Java 25 and 21; parse(InputStream) overflows on Java 21 (9.12.3 also on Java 17) |
| 4.0.0, 3.6.2, 2.1.0 | affected | the same pattern (history probe, depth 100,000) |
| 1.9.1 (first published jar) | SpanOr shape | the same pattern with nested SpanOr |
| 1.9 (release tag) | read | the same contrib/xml-query-parser source as 1.9.1 (read, not run) |
| main with the recommended fix | fix | repro passes; 69 upstream XML-parser tests pass |
The finder, an independent reproducer and a devil's-advocate check found it real; an adjudication confirmed it (medium confidence, about acceptance); two audits kept it; fact checks re-ran it. The cross-model fact check (OpenAI GPT-5.6) ran a probe and agrees it is real, but calls it a plausible Low hardening issue of uncertain acceptance.
Checks and verdicts534 words
Every executed check ran on stock builds or harness-checked release jars and reproduced the failure; the Java 21 legs rest on the finder's, the independent reproduction's and separate runs and on the fact checks' own probes on the 10.6.0 release jars.
| Check | Verdict | What it found |
|---|---|---|
| Finder (planning probe, recursion model, depth sweep) | candidate | Its JUnit fails on main (-ea, -da; with xercesImpl both deep tests), on the 10.6.0 commit, 10.5.2 and 9.12.3 with Java 25 (DOM test) and with Java 21 (both deep tests); the model finds four unguarded recursive cycles in the XML module's own code. |
| Refute lens (OpenAI GPT-5.6, reading only) | not real, medium | Agreed on the root cause and the inputs; rated the throws-clause / SECURITY.md argument strong. Could not run Java. |
| Intended lens (OpenAI GPT-5.6, reading only) | real, medium | Intended argument weak: the DTD documents arbitrary nesting and nothing states a limit. |
| Independent reproducer | real | Own 4-test class fails 2 of 4 on Java 25 (main, 10.6.0 commit, 10.5.2, 9.12.3) and 3 of 4 on Java 21; 4 of 4 pass with the fix; measured the cold and warm thresholds. |
| Root-cause lens (OpenAI GPT-5.6, reading only) and separate re-runs | uncertain, medium; re-runs fail | Root cause agrees; the re-runs fail on main and on the 10.6.0 commit (Java 25 and 21) and pass with the fix. |
| Intended-behaviour devil's advocate | real | Intended case moderate and rejected; fails on main, 10.5.2, and main with -Djdk.xml.maxElementDepth=0 (both entries). |
| Adjudication | confirmed, medium | Re-ran the independent reproduction's class (2 of 4 fail) and the finder's (1 of 3) on main; the doubt is about acceptance, not the facts. |
| Credibility audit | keep | Re-ran on main; its probe shows the parser overflows before search does. |
| Noise audit (2026-10-29) | kept as Low | Challenged as an absurd-parameter loud failure and kept; noted that the parser-nesting rows need one consistent rule. |
| This doc (2026-10-31) | real; fix validated | Gradle in a scratch worktree: stock fails -ea and -da, the fix passes with the module's XML tests and checks; the repro fails on the 10.6.0 and 9.12.3 jars. |
| Independent fact check (2026-10-31) | real; 3 wording errors, fixed | Ran the documented gradle command in its own clean scratch worktree (fails with assertions on and off), the standalone runner on main, 10.6.0 and 9.12.3, the recommended fix (73 of 73 XML tests pass) and its own Java 21 probe on the 10.6.0 release jars. |
| Cross-model fact check (OpenAI GPT-5.6) | real and reproducible; 5 errors, fixed | Read the code and the follow-up, checked that the fix applies, and ran a small probe (one thread with a 1 MB stack per parse, in one JVM) on main with Java 25 and on the 10.6.0 jars with Java 21 and 25: 50 levels give the JDK's ParserException on Java 25, 3,000 overflow on Java 21, later shallow parses work. Rates the case for "acceptable hardening" strong and would treat it as a plausible Low hardening issue whose acceptance is uncertain. |
| Diagram check (2026-10-31) | pass | Stills of the PNG checked against the code at 645d4fe14a20 and the executed runs; no wrong fact, four wording nits. |
The diagram as a still image, generated with AI and checked against the code. It is under 10 MB, the limit for a GitHub attachment.
- PNG 586 KB
Finding record, Lucene and upstream sources, repro files and diagram notes1,806 words
- Registry title
- XML query parser: CoreParser.getQuery(Element) and getSpanQuery(Element) on any JDK, and plain CoreParser.parse(InputStream) on JDK 21 (the 10.x minimum, including the 10.6.0 RC), throw StackOverflowError instead of the declared ParserException on deeply nested queries (on a 1 MB thread stack about 3,000 nesting levels: about 180 KB of nested BooleanQuery XML or 51 KB of nested SpanOr; every release since 1.9.1)
- Area
- queryparser / xml (CoreParser, QueryBuilderFactory and SpanQueryBuilderFactory with the compound and span builders, DOMUtils)
- Priority
- Low, rank 820: The XML query parser lets a StackOverflowError escape on deep nesting, the same class as LFV-926 and LFV-924, but the parser is niche.
- Impact class
- wrong error
- Impact note
- Hits applications that use the opt-in XML query parser (CoreParser and its CorePlusQueriesParser and CorePlusExtensionsParser subclasses, default settings) on deeply nested query XML and rely on its declared ParserException: on the 10.x line running on JDK 21 (the 10.x minimum; executed on the 10.6.0 RC and 10.5.2, and on 9.12.3 with JDK 17 and 21) the plain parse(InputStream) of well-formed XML overflows at about 3,000 nested BooleanQuery levels (about 180 KB) on a 1 MB thread stack and 5,000 (about 300 KB) on the 2 MB aarch64 default, while on main (JDK 25 only) the JDK XML parser's default element-depth limit of 100 turns the same text into ParserException, so main is reached only through the public getQuery(Element)/getSpanQuery(Element) on an application-built DOM (any JDK), another JAXP implementation such as xercesImpl, or a relaxed jdk.xml.maxElementDepth; java.lang.StackOverflowError escapes with -ea and -da in every release since 1.9.1, the depth needed depends on stack size and JIT state (only aarch64 measured), and nothing else goes wrong (no query is built, no wrong hits, no index effect, the JVM and later parses continue).
- Summary
- You give the XML query parser a well-formed query whose elements nest a few thousand levels deep, for example <BooleanQuery><Clause occurs="must"> repeated 3,000 times around one <TermQuery> (about 180 KB; enough on a 1 MB thread stack, while the 2 MB aarch64 default needs 5,000 or more levels), which the shipped DTD allows ('queries/filters of an arbitrary depth and complexity') and which parses fine at 40 levels, and you expect a Query or the ParserException that CoreParser.parse(InputStream), getQuery(Element) and getSpanQuery(Element) declare. Instead java.lang.StackOverflowError escapes, because each compound builder (BooleanQuery, DisjunctionMaxQuery, ConstantScoreQuery and the span builders) calls back into QueryBuilderFactory or SpanQueryBuilderFactory once per nesting level and nothing counts the depth or turns the Error into a ParserException. Whether the plain text path fails depends on the JDK, not on Lucene: JDK 21, the minimum for 10.x including the 10.6.0 RC, has no XML element-depth limit by default and overflows, while JDK 25 (main's minimum) rejects more than 100 nested elements with a ParserException; the DOM entries overflow on every JDK.
- Introduced
- 87768c51c632a3eefd0e78836d900ced46f0e10e (Subversion r380874, 2006-02-25, 'Added XML-query-parser module for new extensible query parser that handles queries expressed as XML ...', the module's original contribution; the message names no JIRA issue and there is no pull request). The unbounded recursion was born with the parser and is not a regression [read: git blame of the recursive call sites on main (BooleanQueryBuilder.java:57, QueryBuilderFactory.java:34, SpanQueryBuilderFactory.java:46, the SpanOr/SpanNear/SpanFirst/SpanNot builders, DOMUtils.java:96, :167 and :172, CoreParser.java:142 and :210) resolves them to 87768c51c632; git log -S depth and -S StackOverflowError over contrib/, lucene/contrib/ and lucene/queryparser/.../xml find no bound ever added; later commits only widened the recursion: DisjunctionMaxQuery builder (LUCENE-4004, 36bc3d70bb3, 4.0.0), nested ConstantScoreQuery (LUCENE-6300, 544f5bf1e7b, 5.1.0), the public getSpanQuery(Element) (LUCENE-7658, 61ab4e338e3, 6.5.0), SpanPositionRange (70162d3fb1a, 8.3.0); SOLR-11477 (926cc4d65b6, 2017) added FEATURE_SECURE_PROCESSING, which does not bound depth] (2006-02-25 (author date of 87768c51c632, r380874))
- First affected version
- 1.9.1 [exec] (2006-03-02, the first published artifact of the XML query parser, executed with the nested SpanOr shape); the 1.9 final source tag (2006-02-27) already contains the recursion [read, no artifact]; 1.9-rc1 has no XML query parser
- Affected versions
- Every release from 1.9.1 to 10.6.0 (branch_10_6 head 4ae946e5a9b9, the RC1 commit) and main a9779ef3c193; which entry fails depends on the JDK's XML parser, not on the Lucene version. [exec] the probe of the version-history runs (depth 100,000, -da) on 1.9.1 (SpanOr), 2.1.0, 3.6.2, 4.0.0, 8.11.4, 9.0.0, 9.12.3, 10.0.0, 10.5.2 (BooleanQuery and SpanOr), the RC and main (BooleanQuery and SpanOr): getQuery(Element) overflows on JDK 25.0.3 and JDK 21.0.10; parse(InputStream) overflows on JDK 21.0.10 and gives ParserException on JDK 25.0.3 (main was run on JDK 25 only). The finder's JUnit fails on main (-ea and -da: DOM leg; with xercesImpl 2.12.2 as the JAXP implementation also the XML leg), on the RC and 10.5.2 (JDK 25 -ea/-da: DOM leg; JDK 21 -ea/-da: both legs) and on 9.12.3 (JDK 25 -da: DOM leg; JDK 17.0.21 and 21.0.10 -da: both legs); the independent reproduction's own JUnit agrees on main, the RC, 10.5.2 and 9.12.3; the intended-behaviour check re-ran main (-ea, -da, and -Djdk.xml.maxElementDepth=0: both legs) and 10.5.2 (JDK 25 -da); the adjudication re-ran main; the registration check re-ran main and the RC (JDK 25 -da). The JDK 21 and 17 legs were run by re-running repro.sh's generated command with only the JDK binaries swapped (repro.sh is pinned to JDK 25). [read] the recursion is unchanged since 87768c51c632; releases not listed were not executed; on 1.9.1-2.0.0 the BooleanQuery shape is not usable as evidence (a separate getElementsByTagName defect, fixed in 6c53bc3c497 for 2.1.0, runs out of heap first), so 1.9.1 was run with SpanOr. Which JDK release first shipped the element-depth default of 100 was not determined. Not fixed upstream: the queryparser/xml directory is unchanged since dbf0d1426f13 (#16056, 2026-05-14) on upstream main 645d4fe14a20 (2026-10-28), branch_10_6 4ae946e5a9b9, branch_10x and branch_11x. All runs on Linux aarch64; x86_64 thresholds not measured.
- Status
- documented (brief, checks and a per-finding doc exist)
- Verification
- Confirmed by AI-run checks: an independent repro, a cross-model review that disputed it (the adjudication ruled it real), an intended-behaviour check, a prior-report search and a skeptical audit; not yet triaged by a person.
- Found by
- non-formal
- Upstream and prior reports
- none found (the prior-report record, verdict new, searched 2026-10-28, read-only, 513 query records): five scripted prior-report searches (394 records: 16 GitHub issue/PR, 3 commit, 2 downstream, 20 mailing-list and 4 JIRA-archive queries each, plus 33 web queries) and a re-read of the finder's 51-query run (1,108 hits, 0 high); 55 manual GitHub searches (apache/lucene, all of GitHub, apache/solr, Lucene.NET, Elasticsearch, OpenSearch), 4 code searches, 15 LUCENE and SOLR JIRA queries, 41 list queries plus the 94 dev@ mails of the last 40 days, GitHub security advisories and project security pages, and the file lists of all 414 open apache/lucene PRs: nothing reports or discusses the XML query parser overflowing on deep nesting or asks for a nesting limit there. Closest items, not reports of this bug: #11687 (SimpleQueryParser stack overflow, open since 2022, where a maintainer asked whether the other query parsers have the problem, unanswered); #16423 (merged 2026-07-27, 10.6.0), the same defect class fixed with a nesting limit in lucene/expressions only; #752 and #11510 (RegExp), where maintainers preferred an up-front check over catching StackOverflowError; #16820 (open, 2026-10-12), a proposal to deprecate the XML query parser for 11.0; the external-entity items #15234, #16135, #9338 (LUCENE-8291) and SOLR-11477 in the same package, with no depth discussion; #15053 and #12295, stale open PRs that touch DOMUtils and keep the recursion. Not fixed: the queryparser/xml directory is unchanged since dbf0d1426f13 (#16056, 2026-05-14) on upstream main 645d4fe14a20, branch_10_6 4ae946e5a9b9, branch_10x and branch_11x. Refresh 2026-10-28 (GitHub search and REST API, read-only): none of the 20 PRs created since 2026-10-26 or the newest items through #16937 touches the XML query parser (#16919, which turns some AssertionErrors into exceptions, changes other files; #16937 is the tracking issue and does not mention it), the issue and PR searches 'CoreParser StackOverflowError', 'xml query parser nesting', 'CoreParser depth' and 'XML queryparser stack overflow' return nothing, and neither upstream main 645d4fe14a20 nor branch_10_6 4ae946e5a9b9 has a newer commit on the directory.
- Lucene commit tested
- main
645d4fe14a20(2026-10-28): the minimal test fails with assertions on and off through gradle in a scratch worktree and through a standalone runner; the same test fails on the 10.6.0 and 9.12.3 release jars (Java 25). With the recommended fix it passes, as do the 69 upstream XML-parser tests with two seeds and the module's format, lint and javadoc checks. Java 21 legs (10.6.0's commit, 10.5.2, 9.12.3) come from the hunt's, the independent reproduction's and separate runs, and the fact check re-ran the 10.x text path on the 10.6.0 release jars with Java 21 in its own probe; the history probe ran 1.9.1 to 10.6.0 and main. Upstream main2f424fd428da(2026-10-30) has the same code [read].
- Finding record
lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/CoreParser.java:137-143 (at a9779ef3c193) (parse(InputStream): javadoc mentions only external entities, throws ParserException, delegates to getQuery(Element)), :182-205 (parseXML: DocumentBuilderFactory.newInstance(), FEATURE_SECURE_PROCESSING at :186, every XML-parser exception wrapped in ParserException), :208-216 (public getQuery(Element)/getSpanQuery(Element) delegate to the factories with no depth state)lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/QueryBuilderFactory.java:29-35 (at a9779ef3c193) (dispatch, builder.getQuery at :34), builders/BooleanQueryBuilder.java:44-57 (factory.getQuery(clauseQuery) at :57), builders/DisjunctionMaxQueryBuilder.java:54, builders/ConstantScoreQueryBuilder.java:41, builders/SpanQueryBuilderFactory.java:46, builders/SpanOrBuilder.java:41 (and SpanNearBuilder.java:45, SpanFirstBuilder.java:38, SpanNotBuilder.java:42-43, SpanPositionRangeBuilder.java:39), DOMUtils.java:96 (getAttributeWithInheritance) and :157-175 (getTextBuffer)lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/ParserException.java:19 (at a9779ef3c193) ('Thrown when the xml queryparser encounters invalid syntax/configuration.'); lucene/queryparser/src/resources/org/apache/lucene/queryparser/xml/LuceneCoreQuery.dtd:54-56 ('Nesting elements': elements nest 'to produce queries/filters of an arbitrary depth and complexity', BooleanQuery inside BooleanQuery)lucene/CHANGES.txt:541-545 (at a9779ef3c193) (GITHUB#16423, expressions nesting guard, 10.6.0 Improvements); SECURITY.md:28, :36-38 ('Untrusted query and analysis input', deeply nested queries), :40-42, :44-46, :48-50; gradle/libs.versions.toml:41 (minJava = '25')JDK facts read by the independent reproduction and the intended-behaviour check and re-read in the registration check: JDK 25.0.3 conf/jaxp.properties:184 'jdk.xml.maxElementDepth=100'; JDK 21.0.10 conf/jaxp.properties:163 '# jdk.xml.maxElementDepth=0' (commented out, so no limit)
- The finding in 30 seconds
a Lucene main checkout (645d4fe14a20) lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/CoreParser.java:137-143, :182-206, :208-216a Lucene main checkout (645d4fe14a20) lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/QueryBuilderFactory.java:29-35; builders/BooleanQueryBuilder.java:44-68; builders/DisjunctionMaxQueryBuilder.java:54; builders/ConstantScoreQueryBuilder.java:41a Lucene main checkout (645d4fe14a20) lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/builders/SpanQueryBuilderFactory.java:41-47; builders/SpanOrBuilder.java:41; SpanNearBuilder.java:45; SpanFirstBuilder.java:38; SpanNotBuilder.java:42-43; SpanPositionRangeBuilder.java:39; DOMUtils.java:88-101, :157-177JDK 25.0.3 conf/jaxp.properties:184 (jdk.xml.maxElementDepth=100); JDK 21.0.10 conf/jaxp.properties:163 (commented out) [read]
- Who is affected
a Lucene main checkout (645d4fe14a20) lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/QueryBuilder.java:22-26
- Why it looks unintended
a Lucene main checkout (645d4fe14a20) lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/CoreParser.java:137-143, :184, :196-205, :208-216; ParserException.java:19a Lucene main checkout (645d4fe14a20) lucene/queryparser/src/resources/org/apache/lucene/queryparser/xml/LuceneCoreQuery.dtd:54-57a Lucene main checkout (645d4fe14a20) lucene/CHANGES.txt:551-555 (10.6.0 Improvements); lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java:104-105, :138, :266-271GitHub apache/lucene#16423 reviews (msokolov, rmuir: APPROVED; label type:enhancement), read 2026-10-31SECURITY.md:38, :42, :46
- Suggested fix
a Lucene main checkout (645d4fe14a20) lucene/queryparser/src/java/org/apache/lucene/queryparser/xml/QueryBuilder.java:22-26
- Prior reports
GitHub apache/lucene read-only searches, 2026-10-31 (this doc)
- History
a Lucene clone tag releases/lucene/10.6.0 -> 4ae946e5a9b9; gradle/libs.versions.toml:42 minJava = "21" [read]
- TestCoreParserDeepNesting.java (one test class):
repro/LFV-965/src/org/apache/lucene/queryparser/xml/TestCoreParserDeepNesting.java(48 lines)
Made in parallel with this doc from the code at 645d4fe14a20 and the executed runs cited in the sections above; re-rendered on 2026-10-31 after the fact checks, with wording fixes only (no new numbers).
assets/LFV-965/bug.png(586 KB)
