← Back to Research
Research · August 10, 2026

Walrus storage-node HTTP/2 Rapid Reset: memory exhaustion via u32::MAX default

Simon Morley·6 min read

The Walrus storage-node service ships an HTTP/2 server whose http2_max_pending_accept_reset_streams is defaulted to u32::MAX, a value plumbed straight into the axum-server 0.8 HTTP/2 builder and reinforced in the published configuration example. That setting disables the post-CVE-2023-44487 accounting limit that the Rust h2 crate added specifically to bound Rapid Reset accumulation. An unauthenticated attacker opens HTTP/2 streams and immediately cancels each one (HEADERSRST_STREAM) in a rapid cycle — the classic HTTP/2 Rapid Reset (CVE-2023-44487 class) — so pending-accept-reset stream state accumulates without bound, pinning memory until OOM, and the server never issues a GOAWAY to shed load. This is a resource-bounding misconfiguration / hardening class, not a novel implementation flaw.

Why this matters

The exposed party is any Walrus storage node running the default configuration. The HTTP/2 REST endpoint is public-by-default — no auth, no allowlist, no token required to reach it. The only gate between an attacker and the unbounded memory pin is the operator's choice to override the compiled default (config.rs:1800) from walrus-node.yaml.

Two properties make this more than a configuration warning. First, the disabled limit is the same accounting knob that the Rust h2 crate added as the specific mitigation for CVE-2023-44487 — the setting exists precisely to bound Rapid Reset accumulation, and Walrus's default opts back out of it by setting it to u32::MAX (4,294,967,295). Second, the measurement is production-faithful and severe: on a mirror with the same axum-server 0.8 + h2 0.4 + rustls 0.23 crate set as upstream walrus-service, a single attacker host with 4 concurrent TLS connections pins ~28 GiB RSS (~4,933× growth) and ~12.4 cores in ~60 seconds, at sub-volumetric traffic (~40 MiB/s out). The server issues zero GOAWAY frames to protect itself; memory is not reclaimed after disconnect → OOM under sustained attack.

The impact is availability-only: the storage-node HTTP/2 REST surface is denied via OOM. There is no consensus-safety break, no funds at risk, no authentication bypass, and no data corruption. A node configured with a bounded http2_max_pending_accept_reset_streams (e.g. 200, or the h2 library default of 20) is not affected.

How the attack works

  • Entry point: the storage-node's HTTP/2 REST port, which is public-by-default in the upstream walrus-node configuration. No auth required.
  • Preconditions: the attacker can reach the HTTP/2 port from any IP. No credentials, no session, no application-layer protocol compliance beyond HTTP/2. Remote, unauthenticated.
  • Processing path:
    1. The disabled limit. crates/walrus-service/src/node/config.rs:1800 defaults http2_max_pending_accept_reset_streams = u32::MAX. The h2 crate added max_pending_accept_reset_streams (default 20) specifically to bound the Rapid-Reset accumulation post CVE-2023-44487; setting it to u32::MAX opts back out.
    2. Plumbed into the server. The value flows into the axum-server 0.8 http_builder().http2() chain at server.rs, so the live server advertises/uses the unbounded limit (runtime dump prints http2_max_pending_accept_reset_streams: 4294967295).
    3. The attack pattern. The client opens a stream (HEADERS) and immediately sends RST_STREAM before the server finishes accepting it. Repeated rapidly, the "pending accept reset" queue grows unbounded. Each cancelled stream pins memory representing the not-yet-accepted state.
    4. No GOAWAY, no reclaim. The server emits no GOAWAY frame to protect itself, and memory is not reclaimed after attacker disconnect → OOM under sustained attack.
  • Boundary crossed: the pre-CVE-2023-44487 h2 accounting limit, which exists precisely to prevent this class, is disabled at compile-time by default. Unbounded pending-accept-reset state accumulates on the server's heap.
  • Resulting behaviour: memory pin → OOM on the HTTP/2 REST surface. The attack is sub-volumetric (~40 MiB/s attacker traffic for ~28 GiB RSS victim growth), so network-layer volumetric gating does not help.

The mechanism is a resource-bounding misconfiguration / hardening class — the code path is the post-CVE-2023-44487 h2 accounting, and the finding is that Walrus's default disables it. This is not a novel implementation flaw; it is a CVE-2023-44487-class issue.

Affected systems

  • Severity: High (per NR-2026-038) — measured OOM on production-faithful mirror: 4 connections, 60 seconds → ~28 GiB RSS (~4,933×), ~12.4 cores, sub-volumetric traffic (~40 MiB/s), zero GOAWAY.
  • Chain: Walrus.
  • Implementation: walrus-node (boot-verified on 1.48.1; every storage node running the default config carries the same u32::MAX value).
  • Component: storage-node HTTP/2 REST API (axum-server 0.8 / h2 crate), configured via http2_max_pending_accept_reset_streams.
  • Gated on: the operator's configuration. A node configured with a bounded http2_max_pending_accept_reset_streams (e.g. 200, or the h2 default of 20) is not affected. The default is the vulnerability; the fix is a one-line walrus-node.yaml change effective on next restart, no binary upgrade required.

Evidence

  • Publicly documented: CVE-2023-44487 is the class (HTTP/2 Rapid Reset), but this is not a Walrus-assigned CVE. The finding is our own measurement of the walrus-specific u32::MAX default — a CVE-2023-44487-class misconfiguration, not a novel implementation flaw. The advisory explicitly states: "not an assigned Walrus CVE."
  • Measured on production-faithful mirror: a single attacker host with 4 concurrent TLS connections, 60 seconds sustained on a mirror with the same axum-server 0.8 + h2 0.4 + rustls 0.23 crate set:
    • Victim RSS: 5,960 KiB → ~28 GiB pinned (~4,933× growth)
    • Victim CPU: ~1,245% (~12.4 cores)
    • Attacker traffic: ~2.34 GiB out (~40 MiB/s — well below any volumetric threshold)
    • Server GOAWAY frames issued: zero; memory not reclaimed after disconnect → OOM under sustained attack
  • Safe-mode baseline: the same mirror, limit left at the h2 library default of 20, tears the attacker down inside ~5 ms with the expected h2 warning log — no memory growth.
  • Independently reproduced by NullRabbit (lab): the published corpus reproducer (walrus_http2_rapid_reset, source_class=original, shipped in NullRabbit/nr-bundles-public) captures the attack traffic — rapid HEADERSRST_STREAM cycles across postures, with zero GOAWAY.
  • Not established: no live/mainnet Walrus node was crashed. The measurement is on a production-faithful mirror, not a live production node.

Operator actions

Available today; no binary upgrade required (per NR-2026-038):

  • Set a bounded limithttp2_max_pending_accept_reset_streams: 200 (or the h2 default of 20) in walrus-node.yaml — effective on next restart. This is the one-line fix.
  • Change the compiled default — modify crates/walrus-service/src/node/config.rs:1800 and node_config_example.yaml so new deployments inherit a safe value.
  • Front the HTTP/2 REST port with a rapid-reset-aware, GOAWAY-emitting gateway where a bounded upstream cannot be relied upon.

Canonical references

Track attacks affecting Walrus on NRDAX and in NullRabbit research.

Production-faithful mirror measurement + reproducer. No live mainnet crash demonstrated.

walrushttp2rapid-resetmemory-ampinfrastructure-security
Publication policy:Why we publish these findings →

Related Posts