CVE-2026-69213 Overview
Http4s is a Scala interface for HTTP services. CVE-2026-69213 is a resource exhaustion vulnerability [CWE-400] in the Ember HTTP/2 implementation. Prior to versions 0.23.35 and 1.0.0-M47, Ember serializes outbound frames through a single unbounded queue consumed by writeLoop. An unauthenticated remote attacker can send PING, SETTINGS, or DATA frames while refusing to read responses, forcing the server to enqueue acknowledgments and WINDOW_UPDATE frames faster than they drain. The behavior exhausts JVM heap memory on any server built with withHttp2, and it also affects ember-client when connected to a hostile HTTP/2 server.
Critical Impact
An unauthenticated network attacker can exhaust heap memory on Ember HTTP/2 servers and clients, causing denial of service.
Affected Products
- Http4s Ember Server with HTTP/2 enabled (withHttp2) prior to 0.23.35
- Http4s Ember Client with HTTP/2 enabled prior to 0.23.35
- Http4s 1.0.0-M1 through 1.0.0-M46 milestone releases
Discovery Timeline
- 2026-09-15 - CVE-2026-69213 published to NVD
- 2026-09-17 - Last updated in NVD database
Technical Details for CVE-2026-69213
Vulnerability Analysis
The Ember HTTP/2 transport layer maintains one outbound frame queue per connection, consumed by an asynchronous writeLoop fiber. The queue is declared with cats.effect.std.Queue.unbounded, so producers never block regardless of consumer progress. When a peer stops reading from the underlying TCP socket, the writer fiber stalls while inbound control frames continue to arrive. Each inbound PING triggers a PING ACK enqueue, each SETTINGS triggers a SETTINGS ACK, and each DATA frame triggers a WINDOW_UPDATE. The producer side grows without bound while the consumer side is TCP-blocked.
Root Cause
The root cause is the absence of backpressure between HTTP/2 frame producers and the socket writer. The unbounded queue decouples frame generation from network flush progress. A // TODO revisit comment in both H2Client.scala and H2Server.scala marks the queue as unbounded, and no upstream flow control mechanism compensates for a slow or malicious peer.
Attack Vector
An unauthenticated attacker establishes an HTTP/2 connection to a vulnerable Ember server, then stops reading from the socket while flooding control frames. Because HTTP/2 mandates acknowledgment of PING and SETTINGS, the server is forced to enqueue responses. Symmetric exploitation applies to ember-client: a hostile HTTP/2 server can trigger the same heap exhaustion on connecting clients.
The upstream patch replaces the unbounded queue with a bounded, backpressured variant:
// Before (vulnerable):
queue <- cats.effect.std.Queue.unbounded[F, Chunk[H2Frame]] // TODO revisit
// After (patched):
queue <- cats.effect.std.Queue.bounded[F, Chunk[H2Frame]](128)
Source: http4s commit 13fe24d
Detection Methods for CVE-2026-69213
Indicators of Compromise
- Sustained JVM heap growth on Ember HTTP/2 processes without corresponding request throughput
- OutOfMemoryError events in Ember server logs referencing H2Server or H2Client classes
- High volumes of inbound HTTP/2 PING or SETTINGS frames from a single client with minimal read activity on the reverse channel
- TCP send buffers persistently full on HTTP/2 sockets while connection state remains ESTABLISHED
Detection Strategies
- Instrument Ember with JVM heap and GC metrics, and alert on rapid old-generation growth tied to HTTP/2 connections
- Correlate HTTP/2 frame counters (via a reverse proxy or eBPF probe) against socket write throughput to identify producer/consumer imbalance
- Inventory Scala services for http4s-ember-server and http4s-ember-client dependencies below 0.23.35 or milestone 1.0.0-M46 and earlier
Monitoring Recommendations
- Track per-connection HTTP/2 frame type ratios at ingress and flag clients with skewed control-to-data ratios
- Monitor process memory, thread state, and file descriptor counts for Ember-based JVMs
- Ingest application logs and JVM telemetry into a centralized data lake to correlate DoS symptoms across replicas
How to Mitigate CVE-2026-69213
Immediate Actions Required
- Upgrade http4s-ember-server and http4s-ember-client to 0.23.35 for the stable line or 1.0.0-M47 for the milestone line
- Audit dependency graphs for transitive Ember usage and rebuild affected artifacts
- Restart long-running Ember services after upgrade to release any accumulated heap pressure
Patch Information
The fix landed in the v0.23.35 release and the v1.0.0-M47 release. The patch replaces the unbounded outbound queue in H2Client.scala and H2Server.scala with a bounded queue of capacity 128, applying backpressure to frame producers when the socket writer falls behind. Full details are in GHSA-8f3q-3jmv-7prw.
Workarounds
- Disable HTTP/2 by removing withHttp2 from Ember server and client builders until upgrade is possible
- Terminate HTTP/2 at a hardened reverse proxy such as Envoy or Nginx and downgrade to HTTP/1.1 for the Ember backend
- Apply per-connection rate limits on HTTP/2 PING and SETTINGS frames at the edge
- Constrain JVM heap sizing and enable OOM restart policies to bound blast radius
# sbt dependency upgrade
libraryDependencies ++= Seq(
"org.http4s" %% "http4s-ember-server" % "0.23.35",
"org.http4s" %% "http4s-ember-client" % "0.23.35"
)
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.