Bound NamespaceResolver nesting depth (fixes #977)#979
Merged
dralley merged 3 commits intoJul 20, 2026
Conversation
`NamespaceResolver::push` incremented a `u16` `nesting_level` with an unguarded `+= 1`; a document nested past `u16::MAX` overflowed it — a panic under `overflow-checks`, or a silent wrap that corrupts namespace-scope truncation in release (bindings from closed scopes leak / in-scope bindings vanish). `pop` already saturates; the increment did not. Return a new `NamespaceError::TooDeeplyNested` at the `u16` boundary instead, matching the existing per-element `TooManyDeclarations` guard. A `saturating_add` would stop the panic but not the scope corruption (at saturation deep levels collapse and `set_level` still over-truncates). Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #979 +/- ##
==========================================
- Coverage 57.31% 56.91% -0.40%
==========================================
Files 46 47 +1
Lines 18197 18521 +324
==========================================
+ Hits 10429 10541 +112
- Misses 7768 7980 +212
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Mingun
approved these changes
Jul 19, 2026
Mingun
left a comment
Collaborator
There was a problem hiding this comment.
Only one small pit, and need to fix formatting.
|
|
||
| [#969]: https://github.com/tafia/quick-xml/issues/969 | ||
| [#970]: https://github.com/tafia/quick-xml/issues/970 | ||
| [#977]: https://github.com/tafia/quick-xml/issues/977 |
Collaborator
There was a problem hiding this comment.
This should be on line 30, in the Unreleased section
Per review: the reference link belonged with its Unreleased entry, not under the already-released 0.41.0 section.
Collaborator
|
@scadastrangelove Please run |
Contributor
Author
|
Done — pushed |
dralley
approved these changes
Jul 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #977.
NamespaceResolver::pushincremented itsu16nesting_levelwith an unguarded+= 1. A document nested pastu16::MAXoverflowed it — a panic underoverflow-checksbuilds, or a silent wrap that corrupts namespace-scope truncation in a stock release build (bindings from closed scopes leak; in-scope bindings vanish). The siblingpopalready saturates (saturating_sub); only the increment was unguarded. Reachable from the publicNsReader::read_resolved_event*on ~490 KB of deeply nested XML. Still present onmaster; orthogonal to #970/#972 (which capped declarations per element — this is the depth counter).Fix
Return a new
NamespaceError::TooDeeplyNestedat theu16boundary, matching the existing per-elementTooManyDeclarationsguard:A
saturating_add(mirroringpop) was considered but is insufficient — it removes the panic but not the scope corruption: at saturation all deep levels collapse tou16::MAXandset_level'srposition(|n| n.level <= level)truncation still over-truncates, so the namespace misresolution persists (verified). The clean error rejects the pathological document instead.Validation
cargo testandcargo test --features serialize: all green (incl. the existingpush_rejects_too_many_declarations).push_rejects_pathological_nesting_depthnext to theNamespaceResolver::push— unbounded per-xmlnsheap allocation insideNsReader, before the event is returned → OOM on untrusted XML #970 test —u16::MAXsuccessful pushes, then a cleanErr(TooDeeplyNested).Err(TooDeeplyNested)instead of panicking, and the namespace-misresolution demonstration no longer occurs.Alternative considered: widen
nesting_leveltou32/usize(overflow becomes practically unreachable). I chose the explicit cap + error to mirrormax_declarations_per_elementand keep the depth bound intentional on untrusted input — happy to switch if you'd prefer no new variant.Found by the rust-in-peace security pipeline.