rustsec-2026-0195
debian_rustsec`NsReader` resolves namespaces by calling `NamespaceResolver::push` for every `Start`/`Empty` event *before* the event is returned to the caller. `push` iterated all `xmlns` / `xmlns:*` attributes on the start tag and, for each one, appended the prefix bytes to an internal buffer and pushed a `NamespaceBinding` (32 bytes on 64-bit) to an internal `Vec`, with no upper bound on the number of declarations. ## Impact A start tag with `N` namespace declarations drove roughly `3×` the tag's byte size in `NamespaceResolver` heap, allocated *inside* `quick-xml` before the `NsReader` consumer ever received the event and could inspect or reject it. A consumer that bounds its *input* size therefore still cannot bound this allocation: an `M`-byte start tag yields on the order of `3 × M` bytes of resolver heap the caller never sees. On untrusted XML this lets a remote, unauthenticated attacker force large heap allocations with a single start tag. With several `NsReader`s running concurrently on independent inputs (a common server pattern), the allocations stack and can exhaust process memory, causing the operating system to kill the process (OOM). This was confirmed against a real-world RPKI relying party (NLnet Labs Routinator), where concurrent RRDP validation workers parsing a crafted `snapshot.xml` exceeded the memory limit and the process was OOM-killed. ## Affected code paths Consumers using `NsReader` (which always calls `NamespaceResolver::push` before yielding `Start`/`Empty`), or calling `NamespaceResolver::push` directly. A plain `Reader` that does not perform namespace resolution is not affected. ## Remediation Upgrade to `quick-xml >= 0.41.0`. `NamespaceResolver::push` now rejects a start tag that declares more than `DEFAULT_MAX_DECLARATIONS_PER_ELEMENT` (256) namespace bindings, returning the new `NamespaceError::TooManyDeclarations` instead of allocating without limit. The limit is configurable via `NamespaceResolver::set_max_declarations_per_element` (use `usize::MAX` to restore the previous unbounded behavior), and `NsReader::resolver_mut()` is provided to reach it. There is no clean workaround for `NsReader` consumers before 0.41.0, as the allocation happens inside the reader with no configuration knob to cap it.
- Published
- unknown
- Last Modified
- unknown
CVSS details not available.
No product information available.
No references available.
No linked vulnerabilities found.
{
"aliases": [],
"categories": [
"denial-of-service"
],
"crate": "quick-xml",
"cves": [],
"cvss": 0.0,
"database_specific": {
"severity": "high"
},
"date": "2026-06-29",
"deb_packages": [
{
"crate_name": "bat",
"deb_name": "bat",
"version": "0.26.1+ds-2"
},
{
"crate_name": "bmap-rs",
"deb_name": "bmap-rs",
"version": "0.2.3-1"
},
{
"crate_name": "broot",
"deb_name": "broot",
"version": "1.60.2+ds-1"
},
{
"crate_name": "czkawka-cli",
"deb_name": "czkawka-cli",
"version": "12.0.2-2"
},
{
"crate_name": "czkawka-gui",
"deb_name": "czkawka-gui",
"version": "12.0.2-1"
},
{
"crate_name": "doxx",
"deb_name": "doxx",
"version": "0.1.4+ds-1"
},
{
"crate_name": "eza",
"deb_name": "eza",
"version": "0.23.5-5"
},
{
"crate_name": "feluda",
"deb_name": "feluda",
"version": "1.17.0-2"
},
{
"crate_name": "grcov",
"deb_name": "grcov",
"version": "0.10.7-2"
},
{
"crate_name": "inquest",
"deb_name": "inquest",
"version": "0.3.6-1"
},
{
"crate_name": "quick-xml",
"deb_name": "librust-quick-xml-dev",
"version": "0.38.4-1+b1"
},
{
"crate_name": "numbat-cli",
"deb_name": "numbat",
"version": "1.24.0-1"
},
{
"crate_name": "presenterm",
"deb_name": "presenterm",
"version": "0.16.1+ds-1"
},
{
"crate_name": "rockusb",
"deb_name": "rockusb",
"version": "0.2.0-3+b1"
},
{
"crate_name": null,
"deb_name": "ty",
"version": "0.0.85+ruff0.16.10+ds-2"
},
{
"crate_name": "xh",
"deb_name": "xh",
"version": "0.26.3-1"
},
{
"crate_name": "xleak",
"deb_name": "xleak",
"version": "0.2.6+dfsg-1+b1"
}
],
"description": "`NsReader` resolves namespaces by calling `NamespaceResolver::push` for every\n`Start`/`Empty` event *before* the event is returned to the caller. `push`\niterated all `xmlns` / `xmlns:*` attributes on the start tag and, for each one,\nappended the prefix bytes to an internal buffer and pushed a `NamespaceBinding`\n(32 bytes on 64-bit) to an internal `Vec`, with no upper bound on the number of\ndeclarations.\n\n## Impact\n\nA start tag with `N` namespace declarations drove roughly `3×` the tag's byte\nsize in `NamespaceResolver` heap, allocated *inside* `quick-xml` before the\n`NsReader` consumer ever received the event and could inspect or reject it. A\nconsumer that bounds its *input* size therefore still cannot bound this\nallocation: an `M`-byte start tag yields on the order of `3 × M` bytes of\nresolver heap the caller never sees.\n\nOn untrusted XML this lets a remote, unauthenticated attacker force large heap\nallocations with a single start tag. With several `NsReader`s running\nconcurrently on independent inputs (a common server pattern), the allocations\nstack and can exhaust process memory, causing the operating system to kill the\nprocess (OOM). This was confirmed against a real-world RPKI relying party (NLnet\nLabs Routinator), where concurrent RRDP validation workers parsing a crafted\n`snapshot.xml` exceeded the memory limit and the process was OOM-killed.\n\n## Affected code paths\n\nConsumers using `NsReader` (which always calls `NamespaceResolver::push` before\nyielding `Start`/`Empty`), or calling `NamespaceResolver::push` directly. A plain\n`Reader` that does not perform namespace resolution is not affected.\n\n## Remediation\n\nUpgrade to `quick-xml >= 0.41.0`. `NamespaceResolver::push` now rejects a start\ntag that declares more than `DEFAULT_MAX_DECLARATIONS_PER_ELEMENT` (256)\nnamespace bindings, returning the new `NamespaceError::TooManyDeclarations`\ninstead of allocating without limit. The limit is configurable via\n`NamespaceResolver::set_max_declarations_per_element` (use `usize::MAX` to\nrestore the previous unbounded behavior), and `NsReader::resolver_mut()` is\nprovided to reach it.\n\nThere is no clean workaround for `NsReader` consumers before 0.41.0, as the\nallocation happens inside the reader with no configuration knob to cap it.",
"id": "RUSTSEC-2026-0195",
"keywords": [
"xml",
"parser",
"dos",
"memory",
"namespace"
],
"patched_versions": [
">=0.41.0"
],
"source": "debian_rustsec",
"title": "Unbounded namespace-declaration allocation in `NsReader` enables memory-exhaustion denial of service",
"url": "https://github.com/tafia/quick-xml/issues/970"
}