Skip to content

Bound the content model a schema expands into - #73

Merged
garydgregory merged 2 commits into
apache:mainfrom
ppkarwasz:fix/schema-content-model-limit
Sep 1, 2026
Merged

Bound the content model a schema expands into#73
garydgregory merged 2 commits into
apache:mainfrom
ppkarwasz:fix/schema-content-model-limit

Conversation

@ppkarwasz

Copy link
Copy Markdown
Member

A schema loader expands a repeated particle into content-model nodes while building the DFA. That happens after parsing and without the reader, so none of the limits the injected secure reader carries can reach it — entity expansion is bounded there (BillionLaughsTest), content-model expansion is not.

The bound for it is the implementation's own secure-processing limit, and it is opt-in on external Xerces: maxOccurLimit is installed only when FEATURE_SECURE_PROCESSING is set on the SchemaFactory, which the wrapper deliberately did not set. The stock JDK applies its limit unconditionally and was never affected.

Measured on external Xerces through SecureSchemaFactory with a 512 MB heap, before this change: maxOccurs="10000" validated in 13.7 s, maxOccurs="100000" ended in OutOfMemoryError. Both are rejected once the feature is set.

The fix sets FEATURE_SECURE_PROCESSING on the wrapped factory, failing closed through the same helper shape the other recipes use. The JAXP 1.5 ACCESS_EXTERNAL_* properties are still not set explicitly, so the caller-resolver opt-in path the wrapper's Javadoc protects is untouched: schemaFetchesIdentifierOnlyOptIn (an identifier-only LSInput, the case where the implementation must fetch the named resource itself) and schemaResolvesAllowListed pass on every schema execution, on Java 8 as well as the default JDK.

One behavioural note for reviewers: because the set fails closed, a SchemaFactory implementation that rejects FEATURE_SECURE_PROCESSING now throws instead of returning an unsecured factory. That matches the documented contract for a required setting, but it is a change for implementations outside the recognized set.

The new SchemaContentModelLimitTest validates an instance rather than only compiling the schema, because the expansion is lazy on Xerces — newSchema returns in milliseconds whatever maxOccurs says. Its unbounded control skips where the implementation applies the limit unconditionally, since there is no unbounded run to compare against.

Verified with the full surefire matrix on the default JDK and on Java 8.

🤖 Generated with Claude Code

ppkarwasz and others added 2 commits September 1, 2026 11:53
A schema loader expands a repeated particle into content-model nodes
while building the DFA. That happens after parsing and without the
reader, so the limits the injected reader carries never reach it, and a
compact schema with a large maxOccurs was expanded unbounded on an
implementation whose limit is opt-in: external Xerces applies it only
under FEATURE_SECURE_PROCESSING, which the wrapper did not set (the
stock JDK applies it unconditionally).

Set FEATURE_SECURE_PROCESSING on the wrapped factory, failing closed as
the other recipes do. The JAXP 1.5 ACCESS_EXTERNAL_* properties stay
unset, so the caller-resolver opt-in path is unaffected; the schema
tests covering it pass on Java 8 as well.

The expansion is lazy on Xerces, so the new test validates an instance
rather than only compiling the schema, and skips its unbounded control
where the implementation applies the limit unconditionally.

Assisted-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CLnTBsvmYtxzNTWVGNyz33
Updated the test class description for clarity.
@garydgregory
garydgregory merged commit 6d40c0e into apache:main Sep 1, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants