Sep 16, 2026·6 min read·20 visits
Unauthenticated remote peers can trigger heap memory exhaustion and Denial of Service in http4s Ember HTTP/2 implementations by sending frames with large declared size headers.
An uncontrolled resource consumption vulnerability in the http4s Ember HTTP/2 server and client implementation leads to unauthenticated heap memory exhaustion and denial of service. The vulnerability stems from deferring frame size validation until the entire declared payload size is buffered.
The vulnerability affects http4s, a minimal and idiomatic Scala interface for HTTP services. Specifically, the flaw exists within the HTTP/2 implementation of Ember, which is the built-in, native HTTP server and client backend for http4s. Ember exposes an attack surface to remote unauthenticated clients when configured to handle HTTP/2 traffic.
The vulnerability is classified under CWE-400 (Uncontrolled Resource Consumption) and manifests as an application-level Denial of Service. Because the read loop process fails to enforce limits early, it allows arbitrary remote hosts to dictate resource allocation boundaries before validating them. This behavior results in a high-severity availability issue that affects both servers and clients leveraging the vulnerable component.
HTTP/2 protocol specifications (RFC 9113) define communication through distinct binary frames. Each frame begins with a fixed 9-byte header followed by a variable-length payload. The first three bytes of this header encode a 24-bit integer representing the payload length. During connection negotiation, receiving endpoints advertise their maximum allowable frame size via the SETTINGS_MAX_FRAME_SIZE parameter. The default value is 16 KiB, and it may be negotiated up to 16 MiB.
In vulnerable versions of http4s Ember, the H2Connection.readLoop function reads incoming TCP streams and appends data to an internal accumulator. The read loop holds these bytes in memory until the accumulator length is at least equal to the declared length of the frame plus the 9-byte header. The code does not check the declared length against the configured SETTINGS_MAX_FRAME_SIZE until the reassembly of the frame is complete.
Consequently, a remote peer can declare a massive frame payload size of up to 16 MiB while providing minimal payload data. The server keeps the stream buffer open and continues allocating memory in an attempt to fulfill the expected payload length. If multiple malicious connections execute this sequence simultaneously, the accumulated buffers deplete the Java Virtual Machine heap, leading to severe memory pressure.
The vulnerability was addressed in commit 87cf334fa3f608ef7d3eb359e71e037ba3336d29 by shifting the frame size check into the earliest stage of the read loop.
Historically, the read loop evaluated the frame only after complete reassembly using the fromByteVector parser. This parsing method deferred validation until all bytes defined by the header were already buffered.
// Vulnerable Read Loop Behavior
// The loop accumulated socket data and only parsed it after
// receiving the entire declared frame payload
H2Frame.RawFrame.fromByteVector(acc) match {
case Some((rawFrame, remaining)) =>
// Post-reassembly check occurs here within specific handlers
// after the allocation has already occurred
}The security patch introduced a pre-buffering validation phase. It implemented a helper method named peekDeclaredLength within H2Frame.scala to extract the declared size directly from the first 3 bytes of the buffer without waiting for the payload.
// Patched Implementation in H2Frame.scala
object RawFrame {
/**
* The payload length a frame declares, decoded from the first three bytes.
* Allows an oversized frame to be rejected without reading that payload.
*/
def peekDeclaredLength(bv: ByteVector): Option[Int] =
if (bv.length >= 3)
Some((bv(2) & 0xff) | ((bv(1) & 0xff) << 8) | ((bv(0) & 0xff) << 16))
else None
}The H2Connection.readLoop was updated to perform a proactive check against this value immediately as bytes are received. If the declared size exceeds the local settings limit, the connection is instantly closed.
// Proactive Validation Check in H2Connection.scala
} else if (
H2Frame.RawFrame.peekDeclaredLength(acc).exists(_ > localSettings.maxFrameSize.frameSize)
) {
logger.warn(
"Received Frame Size Larger than Allowed Frame Size - Frame Size Error - Issuing GoAway"
) >> goAway(H2Error.FrameSizeError) >> F.pure(None)
} else
H2Frame.RawFrame.fromByteVector(acc) match {
// Process valid frame
}An attack targeting this vulnerability is carried out over an established HTTP/2 connection. No authentication credentials or special application-level privileges are required. The attacker only needs network-level access to the port on which the Ember-based service is listening.
The attack sequence is executed in the following steps:
During the attack, the peer transmits an initial frame header stating a size of 16 MiB. The attacker then either pauses transmission or trickles a minimal volume of bytes. The vulnerable server pauses processing of subsequent frames on that connection and retains the growing buffer in memory while waiting for the payload completion. By establishing multiple concurrent streams, the attacker triggers memory amplification up to 1024-fold per connection, leading to a denial of service.
The impact of successful exploitation is localized entirely to the availability of the application. There is no risk of unauthorized disclosure of confidential data, nor is there any opportunity for unauthorized modification of system data. This is reflected in the CVSS v3.1 vector string: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H.
The consequences on the JVM runtime include memory fragmentation, prolonged garbage collection pauses, and eventually an OutOfMemoryError. Once an OutOfMemoryError occurs, the JVM may become completely unresponsive or terminate abruptly, necessitating a manual restart or process-manager recovery.
At the time of writing, there are no known public weaponized exploit frameworks targeting this specific flaw. Additionally, the vulnerability is not listed in the CISA Known Exploited Vulnerabilities catalog.
The primary remediation strategy is upgrading the http4s dependencies to the patched releases. For projects using the 0.23.x release line, upgrade to version 0.23.37 or newer. For projects utilizing the 1.0.0 milestone line, upgrade to version 1.0.0-M48 or newer.
// Example sbt build definition update
libraryDependencies += "org.http4s" %% "http4s-ember-server" % "0.23.37"If upgrading is not immediately possible, you can mitigate the vulnerability by disabling HTTP/2 support on your Ember servers. This forces clients to fallback to HTTP/1.1, bypassing the vulnerable code path in H2Connection entirely.
// Ensuring HTTP/2 is not enabled on EmberServerBuilder
val server = EmberServerBuilder
.default[IO]
.withHost(host"0.0.0.0")
.withPort(port"8080")
.withHttpApp(httpApp)
// Do not call .withHttp2 or .withHttp2(true)
.buildTo detect exploitation attempts, check server log files for the warning message: Received Frame Size Larger than Allowed Frame Size - Frame Size Error - Issuing GoAway. This pattern indicates the updated security logic is rejecting connection streams.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
http4s-ember-server http4s | < 0.23.37 | 0.23.37 |
http4s-ember-client http4s | < 0.23.37 | 0.23.37 |
http4s-ember-server http4s | >= 1.0.0-M1 < 1.0.0-M48 | 1.0.0-M48 |
http4s-ember-client http4s | >= 1.0.0-M1 < 1.0.0-M48 | 1.0.0-M48 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 |
| Attack Vector | Network |
| CVSS Severity | 7.5 (High) |
| Exploit Status | None |
| Impact | Denial of Service |
The software does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed.
An OS command injection and argument injection vulnerability exists in the Nx build system and its official Maven (@nx/maven) and Gradle (@nx/gradle) plugins. When constructing the project dependency graph, the plugins invoke underlying Java build wrappers via subshells while interpolating unsanitized plugin options directly from nx.json into the command arguments. An attacker who supplies a crafted nx.json file via a repository or pull request can execute arbitrary shell commands with the privileges of the user or CI/CD runner invoking any Nx command that computes the project graph.
AdminNeo versions 4.6.2 through 5.4.1 contain an improper input validation vulnerability (CWE-20) in the version verification endpoint (?script=version). An unauthenticated remote attacker can submit a crafted HTTP POST request containing an array parameter for 'version', which is serialized into a shared server-side temporary cache file without type checking or authentication. On subsequent requests, the application deserializes this cached array and passes it to cookie() and rawurlencode(), triggering an uncaught fatal TypeError in PHP 8.x environments and causing a persistent 24-hour Denial of Service (DoS) for all users.
Charmbracelet Soft Serve versions 0.5.0 through 0.12.2 contain an authorization bypass and observable response discrepancy vulnerability in the HTTP Git server. Remote attackers can determine the existence, exact names, and clone URLs of private repositories by issuing crafted Go remote import path discovery requests (?go-get=1) or by observing differential HTTP status codes across Git Smart HTTP, Dumb HTTP, and Git LFS endpoints.
A Time-of-Check to Time-of-Use (TOCTOU) race condition in Charmbracelet Soft Serve versions 0.6.0 through 0.12.2 allows unauthenticated users to retain read access to repositories after their visibility is changed from public to private. The flaw stems from premature cache invalidation in the SetPrivate method prior to committing the visibility update to the underlying database. Concurrent read requests arriving during the database transaction window repopulate the non-expiring in-memory LRU cache with stale public metadata, bypassing access controls across HTTP, SSH, and Git protocols until the server process is restarted.
A resource consumption vulnerability exists in the Authlib joserfc Python library prior to version 1.7.5. When processing JSON Web Encryption (JWE) objects formatted using General JSON Serialization, the library fails to enforce an upper bound on the number of elements within the recipients array. An unauthenticated attacker can supply a JWE containing thousands of recipient entries targeting an RSA key management algorithm, forcing the server to perform an equivalent number of computationally expensive RSA private-key decryption operations per request and exhausting available CPU resources.
CVE-2026-119284 (GHSA-9pg5-8c7x-5hfx) is an uncontrolled resource consumption vulnerability (CWE-400) in the compact serialization deserialization routines of joserfc prior to version 1.7.6. The library enforced individual segment size limits only after invoking Python's bytes.split(b'.') on untrusted input tokens. By sending oversized compact JWS or JWE tokens, an unauthenticated remote attacker can force the Python interpreter to duplicate and allocate memory for every segment before size validation executes, resulting in memory exhaustion and an Endpoint Denial of Service (DoS).