Skip to main content
Vulnerability Database/CVE-2026-88975

CVE-2026-88975: Http4s Ember HTTP/2 Memory Amplification DoS Flaw

CVE-2026-88975 is a denial of service flaw in Http4s Ember HTTP/2 that enables unauthenticated attackers to trigger 1024-fold memory amplification. This article covers the technical details, affected versions, and mitigation strategies.

Published:

CVE-2026-88975 Overview

Http4s is a Scala interface for HTTP services. A resource exhaustion vulnerability [CWE-400] affects the Ember backend's HTTP/2 read loop in versions prior to 0.23.37 and 1.0.0-M48. The read loop parses a frame's 24-bit declared length but waits to buffer the entire payload before comparing it against SETTINGS_MAX_FRAME_SIZE. An unauthenticated remote peer can declare payloads near 16 MiB on connections where Ember advertised 16 KiB, producing up to 1024-fold memory amplification per connection before processFrame rejects the frame.

Critical Impact

Unauthenticated attackers can trigger up to 1024x memory amplification per HTTP/2 connection, enabling denial-of-service against Ember-based http4s servers and clients using withHttp2.

Affected Products

  • http4s Ember server and client with HTTP/2 enabled (withHttp2) prior to 0.23.37
  • http4s milestone releases prior to 1.0.0-M48
  • Applications using the shared H2Connection.readLoop code path

Discovery Timeline

  • 2026-09-15 - CVE-2026-88975 published to NVD
  • 2026-09-16 - Last updated in NVD database

Technical Details for CVE-2026-88975

Vulnerability Analysis

The flaw resides in Ember's HTTP/2 frame parsing logic within H2Connection.readLoop. HTTP/2 frames begin with a 9-byte header, the first three bytes of which encode a 24-bit payload length. The vulnerable implementation reads this length, then allocates and buffers the full declared payload before applying the SETTINGS_MAX_FRAME_SIZE check inside processFrame.

Because the maximum value representable in 24 bits is roughly 16 MiB while Ember advertises a default SETTINGS_MAX_FRAME_SIZE of 16 KiB, an attacker can declare payloads three orders of magnitude larger than permitted. The mismatch produces up to 1024-fold memory amplification per connection. Attackers can also stream the payload slowly to prolong buffer retention. HTTP/2-disabled configurations are unaffected because the vulnerable read loop is not invoked.

Root Cause

The vulnerability stems from validating a frame's declared length after buffering rather than before. The implementation trusts the 24-bit length field long enough to allocate storage for it, deferring policy enforcement until the frame is fully assembled.

Attack Vector

An unauthenticated network peer opens an HTTP/2 connection to a vulnerable Ember server or connects a client to a malicious server. The attacker sends frame headers declaring payload sizes approaching the 16 MiB ceiling and either transmits the full payload or streams bytes slowly. Repeating this across concurrent connections exhausts server memory.

scala
// Security patch: peek the declared length before buffering the payload
// Source: https://github.com/http4s/http4s/commit/87cf334fa3f608ef7d3eb359e71e037ba3336d29
object RawFrame {

  /** The payload length a frame declares, decoded from the first three bytes.
    *
    * Available long before the payload it describes. RFC 9113 4.2 allows an
    * oversized frame to be rejected without reading that payload, so a caller
    * can consult this instead of buffering `9 + length` bytes to find out.
    */
  def peekDeclaredLength(bv: ByteVector): Option[Int] =
    if (bv.length >= 3)
      Some((bv(2) & 0xff) | ((bv(1) & 0xff) << 8) | ((bv(0) & 0xff) << 16))
    else None

  def fromByteVector(bv: ByteVector): Option[(RawFrame, ByteVector)] =
    if (bv.length >= 9) {
      val length = (bv(2) & 0xff) | ((bv(1) & 0xff) << 8) | ((bv(0) & 0xff) << 16)
      // ...
    }
}

The patch introduces peekDeclaredLength, which decodes the 24-bit length from the first three bytes of the header so callers can reject oversized frames before allocating buffers. See the GitHub Security Advisory GHSA-gq9p-f254-h286 for full details.

Detection Methods for CVE-2026-88975

Indicators of Compromise

  • Sudden growth in JVM heap usage on Ember HTTP/2 endpoints without a corresponding rise in successful request throughput.
  • HTTP/2 connections receiving FRAME_SIZE_ERROR (0x6) responses shortly before OutOfMemoryError events.
  • Repeated inbound HTTP/2 connections from a small set of source IPs sending unusually large frame length headers.

Detection Strategies

  • Monitor http4s Ember application logs for repeated frame rejection or connection resets tied to HTTP/2 traffic.
  • Instrument the JVM with allocation and GC metrics to detect large short-lived byte buffer allocations correlated with inbound HTTP/2 sessions.
  • Deploy network monitoring rules that flag HTTP/2 DATA or HEADERS frames declaring lengths above the negotiated SETTINGS_MAX_FRAME_SIZE.

Monitoring Recommendations

  • Track heap utilization, connection counts, and per-connection buffer sizes for services using withHttp2.
  • Alert on abnormal ratios of bytes buffered to bytes processed on HTTP/2 endpoints.
  • Correlate application-level HTTP/2 errors with upstream reverse-proxy or load-balancer telemetry to identify abusive peers.

How to Mitigate CVE-2026-88975

Immediate Actions Required

  • Upgrade http4s Ember to version 0.23.37 or 1.0.0-M48, which reject oversized frames before buffering payloads.
  • Inventory all services and clients built on http4s Ember with HTTP/2 enabled via withHttp2 and prioritize them for patching.
  • Restrict exposure of affected endpoints behind an HTTP/2-aware reverse proxy that enforces frame size limits until patching completes.

Patch Information

The fix is available in http4s v0.23.37 and http4s v1.0.0-M48. The corrective change is implemented in commit 87cf334, which adds RawFrame.peekDeclaredLength and rejects frames exceeding SETTINGS_MAX_FRAME_SIZE before allocating buffers.

Workarounds

  • Disable HTTP/2 on Ember servers and clients by removing the withHttp2 builder call; HTTP/1.1-only configurations are not affected.
  • Terminate HTTP/2 at an upstream proxy (for example, Envoy or NGINX) that enforces strict SETTINGS_MAX_FRAME_SIZE policies and downgrade to HTTP/1.1 toward the http4s backend.
  • Apply per-connection memory limits at the container or JVM level to bound the impact of amplification while patches are rolled out.
bash
# Update the http4s Ember dependency in build.sbt
libraryDependencies ++= Seq(
  "org.http4s" %% "http4s-ember-server" % "0.23.37",
  "org.http4s" %% "http4s-ember-client" % "0.23.37"
)

# Or for the milestone line
libraryDependencies ++= Seq(
  "org.http4s" %% "http4s-ember-server" % "1.0.0-M48",
  "org.http4s" %% "http4s-ember-client" % "1.0.0-M48"
)

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.