rustsec-2026-0195

debian_rustsec
Description

`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.

Timeline
Published
unknown
Last Modified
unknown
CVSS Details

CVSS details not available.

Affected Products

No product information available.

References

No references available.

Linked Vulnerabilities

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"
}
View JSON API Download JSON