Introduction
The crosshash/Cargo.lock file locks the exact dependency graph used to build the crosshash project — including transitive dependencies pulled in by the Quinn QUIC stack. One of those transitive dependencies, quinn-proto 0.11.14, contained a flaw in how it reassembles QUIC stream data that arrives out of order. Because QUIC (like TCP) allows packets to arrive in any order over the network, the protocol implementation has to buffer data until the missing pieces show up. If that buffering has no upper bound, a remote attacker who controls a QUIC connection can weaponize normal-looking traffic into a memory-exhaustion denial-of-service attack against any server using this library.
This isn't a bug in application code written for crosshash — it's a bug in a widely-used protocol library that crosshash depends on. That's exactly why dependency-level vulnerabilities deserve the same attention as first-party code: the vulnerable code path runs inside your process, with your process's memory limits, regardless of who wrote it.
The Vulnerability Explained
QUIC streams support out-of-order delivery: a receiver can get bytes 1000–2000 of a stream before it gets bytes 0–1000. To handle this correctly, quinn-proto's stream state machine keeps a reassembly buffer that stores "future" chunks until the gaps preceding them are filled, at which point the buffered data is delivered to the application in order.
The vulnerable behavior in quinn-proto 0.11.14 was that this reassembly buffering was effectively unbounded relative to the attacker's ability to generate out-of-order chunks. A malicious or misbehaving peer could:
- Open one or more QUIC streams.
- Send many small chunks of data at large, scattered offsets within each stream — deliberately skipping the low offsets so the data can never be delivered to the application.
- Keep the connection alive, repeating this pattern across many streams or connections.
Because the receiving endpoint has to assume the missing gaps might arrive eventually, it keeps accumulating and holding onto all of this out-of-order data in memory. Multiply this across many streams and connections, and a single attacker (or a small botnet) can drive the target's memory usage up dramatically without ever completing a legitimate data transfer — a classic CWE-770: Allocation of Resources Without Limits or Throttling, closely related to CWE-400: Uncontrolled Resource Consumption.
For any service built on Quinn — file-transfer daemons, HTTP/3 servers, custom RPC layers, or in this case whatever network-facing component of crosshash uses the Quinn stack — this translates into a straightforward remote DoS primitive: no authentication bypass is needed, just the ability to open a QUIC connection and send crafted frames.
Example attack scenario: An attacker connects to a crosshash-based service that accepts QUIC connections. Instead of sending a legitimate request, the attacker's client opens a stream and immediately sends a chunk at offset 10,000,000, then another at offset 20,000,000, and so on — never sending the initial bytes at offset 0. quinn-proto's reassembly logic buffers each of these chunks, waiting for the gap to be filled. The attacker repeats this on hundreds of streams across a handful of connections. The server's memory footprint balloons, degrading performance for legitimate users and potentially triggering an OOM kill of the process — a full outage from what looks, at the network layer, like ordinary QUIC traffic.
The Fix
The fix here is intentionally minimal and surgical: bump the pinned version of quinn-proto in crosshash/Cargo.lock from the vulnerable 0.11.14 to the patched 0.11.15.
Before:
[[package]]
name = "quinn-proto"
version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
After:
[[package]]
name = "quinn-proto"
version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e"
Version 0.11.15 upstream (published by the Quinn maintainers in response to GHSA-4w2j-m93h-cj5j) introduces bounds on how much out-of-order stream data the reassembly logic will retain per stream/connection before it either drops, rejects, or otherwise limits further buffering. This directly removes the exploit primitive: an attacker can no longer force unbounded buffer growth simply by sending scattered, gap-filled chunks.
Because this change is purely a dependency version bump — with the lockfile's checksum updated to match the new, verified crate contents — no application logic in crosshash needed to change. The public API surface used by crosshash remains identical between 0.11.14 and 0.11.15, so this is a drop-in, behavior-preserving upgrade for all valid, well-formed QUIC traffic. Only the handling of adversarial, out-of-order input is tightened.
This is also a good example of why "just bump the lockfile" changes are legitimate security fixes, not busywork: the vulnerable logic lives entirely inside the third-party crate, so the only way to remediate it without vendoring a patch is to consume the upstream fix.
Prevention & Best Practices
- Track dependency advisories continuously. Use
cargo auditorcargo denyin CI to catch known-vulnerable crate versions (likequinn-proto< 0.11.15) before they ship. - Pin dependencies, but review lockfile diffs.
Cargo.lockpins exact versions for reproducibility — make sure your review process treats lockfile-only PRs (like this one) as security-relevant, not "noise." - Apply resource limits at the application layer too. Even with a patched library, consider setting your own caps on concurrent streams, connections, and per-connection memory via Quinn's configuration APIs (
TransportConfig) as defense in depth. - Prefer software composition analysis (SCA) in your pipeline. Tools like Trivy,
cargo audit, and GitHub's Dependabot alerts specifically catch vulnerabilities like GHSA-4w2j-m93h-cj5j that live in transitive dependencies you may never directly interact with. - Understand your network stack's trust boundary. Any protocol implementation that buffers data on behalf of an untrusted peer (reassembly, decompression, deduplication) is a candidate for resource-exhaustion bugs — treat these subsystems as high-risk during dependency review.
Key Takeaways
- The vulnerable code path was entirely inside the
quinn-protodependency's stream reassembly logic —crosshashitself required zero code changes, only a version bump. - Out-of-order QUIC stream delivery is a legitimate protocol feature that becomes an attack surface when reassembly buffering isn't bounded — this is the specific mechanism behind GHSA-4w2j-m93h-cj5j.
- The fix is a single-line-per-field lockfile change:
quinn-proto0.11.14 → 0.11.15, with a corresponding checksum update incrosshash/Cargo.lock. - This is classified as CWE-770/CWE-400 (unbounded resource allocation), a pattern worth specifically checking for whenever you evaluate network protocol libraries.
- Lockfile-only PRs that bump a transitive dependency version deserve the same security scrutiny as source-code changes — they can close remotely exploitable DoS vectors.
How Orbis AppSec Detected This
- Source: Untrusted QUIC stream frames sent by a remote peer over the network, specifically out-of-order data chunks at arbitrary stream offsets.
- Sink:
quinn-proto's internal stream reassembly buffer (stream state machine in the vendoredquinn-proto0.11.14 dependency resolved viacrosshash/Cargo.lock). - Missing control: No upper bound on the amount of out-of-order data the reassembly buffer would retain per stream/connection before the corresponding gaps were filled.
- CWE: CWE-770 — Allocation of Resources Without Limits or Throttling (related: CWE-400 — Uncontrolled Resource Consumption).
- Fix: Upgraded
quinn-protofrom 0.11.14 to 0.11.15 incrosshash/Cargo.lock, pulling in upstream's bounded reassembly-buffer handling.
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
GHSA-4w2j-m93h-cj5j is a reminder that memory-safety issues aren't only about buffer overflows and use-after-free bugs — resource-exhaustion flaws in protocol-handling code can be just as damaging, and just as remotely exploitable, without a single line of unsafe code involved. In this case, the fix for crosshash was as simple as it gets: bump quinn-proto from 0.11.14 to 0.11.15 in Cargo.lock and pick up the upstream bound on out-of-order stream reassembly. The lesson for every Rust project consuming network protocol libraries is to treat lockfile updates for crates like quinn/quinn-proto as security-critical, keep automated dependency scanning in your CI pipeline, and layer your own resource limits on top of what the library provides.