error[vulnerability]: Quadratic run time when checking a start tag for duplicate attribute names
┌─ /workspaces/client-toolkit/Cargo.lock:136:1
│
136 │ quick-xml 0.39.4 registry+https://github.com/rust-lang/crates.io-index
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
│
├ ID: RUSTSEC-2026-0194
├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0194
├ `BytesStart::attributes()` returns an `Attributes` iterator which, by default
(`with_checks(true)`), rejects a start tag that repeats an attribute name. For
each attribute yielded, the iterator compared the new name against every name
seen so far in the same tag using a linear scan, so a start tag with `N`
distinct attribute names cost `O(N²)` byte comparisons. There was no bound on
`N` other than the size of the buffered start tag.
## Impact
Any code that parses untrusted XML and iterates a start tag's attributes with
the default duplicate check enabled can be made to spend CPU time quadratic in
the number of attributes on a single tag. Because the check is pure computation
with no `.await`/I/O, an I/O-based timeout on the consumer (for example a read
or request timeout) cannot interrupt it while it runs.
Measured cost of a single start tag, release build:
| Attributes on one tag | Time |
|---|---|
| 80,000 | ~6 s |
| 800,000 | ~10 min |
The cost grows with the square of the attribute count, so a start tag of a few
tens of megabytes can stall a parsing thread for hours. No memory is exhausted
and the parser does not crash; the effect is CPU exhaustion on the thread doing
the parsing: a single crafted start tag can pin a CPU core for minutes to hours,
denying service to that worker. A deployment that places a wall-clock bound on
parsing, or confines it to a non-critical thread, may consider the availability
impact lower.
## Affected code paths
* `BytesStart::attributes()` / `Attributes` iterated with checks enabled (the
default), and `BytesStart::try_get_attribute`.
* `NsReader`, which resolves namespaces by iterating a tag's attributes and so
reaches the same check internally.
Consumers that iterate attributes with `.attributes().with_checks(false)` and do
not use `NsReader` are not affected.
This was reported as reachable by a remote, unauthenticated attacker in a
real-world RPKI relying party (NLnet Labs Routinator) via a crafted RRDP
`snapshot.xml`.
## Remediation
Upgrade to `quick-xml >= 0.41.0`, where the duplicate check keeps the linear
scan for start tags with a small number of attributes and switches to an `O(1)`
hash pre-filter above a threshold, making the whole tag `O(N)`. The reported
`AttrError::Duplicated` positions are unchanged.
If upgrading is not possible and duplicate-name detection is not required,
disable it with `.attributes().with_checks(false)` (this does not help
`NsReader` consumers, which have no equivalent opt-out before 0.41.0).
├ Announcement: https://github.com/tafia/quick-xml/issues/969
├ Solution: Upgrade to >=0.41.0 (try `cargo update -p quick-xml`)
├ quick-xml v0.39.4
└── wayland-scanner v0.31.10
├── smithay-client-toolkit v0.20.0
├── wayland-client v0.31.14
│ ├── calloop-wayland-source v0.4.1
│ │ └── smithay-client-toolkit v0.20.0 (*)
│ ├── smithay-client-toolkit v0.20.0 (*)
│ ├── wayland-cursor v0.31.14
│ │ └── smithay-client-toolkit v0.20.0 (*)
│ ├── wayland-protocols v0.32.13
│ │ ├── smithay-client-toolkit v0.20.0 (*)
│ │ ├── wayland-protocols-experimental v20251230.0.3
│ │ │ └── smithay-client-toolkit v0.20.0 (*)
│ │ ├── wayland-protocols-misc v0.3.12
│ │ │ └── smithay-client-toolkit v0.20.0 (*)
│ │ └── wayland-protocols-wlr v0.3.12
│ │ └── smithay-client-toolkit v0.20.0 (*)
│ ├── wayland-protocols-experimental v20251230.0.3 (*)
│ ├── wayland-protocols-misc v0.3.12 (*)
│ └── wayland-protocols-wlr v0.3.12 (*)
├── wayland-protocols v0.32.13 (*)
├── wayland-protocols-experimental v20251230.0.3 (*)
├── wayland-protocols-misc v0.3.12 (*)
└── wayland-protocols-wlr v0.3.12 (*)
error[vulnerability]: Unbounded namespace-declaration allocation in `NsReader` enables memory-exhaustion denial of service
┌─ /workspaces/client-toolkit/Cargo.lock:136:1
│
136 │ quick-xml 0.39.4 registry+https://github.com/rust-lang/crates.io-index
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
│
├ ID: RUSTSEC-2026-0195
├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0195
├ `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.
├ Announcement: https://github.com/tafia/quick-xml/issues/970
├ Solution: Upgrade to >=0.41.0 (try `cargo update -p quick-xml`)
├ quick-xml v0.39.4
└── wayland-scanner v0.31.10
├── smithay-client-toolkit v0.20.0
├── wayland-client v0.31.14
│ ├── calloop-wayland-source v0.4.1
│ │ └── smithay-client-toolkit v0.20.0 (*)
│ ├── smithay-client-toolkit v0.20.0 (*)
│ ├── wayland-cursor v0.31.14
│ │ └── smithay-client-toolkit v0.20.0 (*)
│ ├── wayland-protocols v0.32.13
│ │ ├── smithay-client-toolkit v0.20.0 (*)
│ │ ├── wayland-protocols-experimental v20251230.0.3
│ │ │ └── smithay-client-toolkit v0.20.0 (*)
│ │ ├── wayland-protocols-misc v0.3.12
│ │ │ └── smithay-client-toolkit v0.20.0 (*)
│ │ └── wayland-protocols-wlr v0.3.12
│ │ └── smithay-client-toolkit v0.20.0 (*)
│ ├── wayland-protocols-experimental v20251230.0.3 (*)
│ ├── wayland-protocols-misc v0.3.12 (*)
│ └── wayland-protocols-wlr v0.3.12 (*)
├── wayland-protocols v0.32.13 (*)
├── wayland-protocols-experimental v20251230.0.3 (*)
├── wayland-protocols-misc v0.3.12 (*)
└── wayland-protocols-wlr v0.3.12 (*)
advisories FAILED
Hi,
I'm trying to address RUSTSEC-2026-0194 and RUSTSEC-2026-0195 in my crate and noticed that the version of
wayland-scannerused by this crate uses the old version of quick-xml. There is currently an updated versionwayland-scannerthat uses a newer non-affected version. When possible could the upgrade of this transitive dependency be looked into please.Output of `cargo deny check advisories`