GitHub Advisoryadvisory · Packagist
GHSA-x424-64qh-5j54 react/http: A malformed HTTP chunked body can lead to a denial-of-service and peg the CPU
HIGH
Official page ↗
- Aliases
- CVE-2026-84997
- Ecosystem
- Packagist
- Packages
- react/http
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Weaknesses
- CWE-835
- Reviewed
- 2026-09-17
Affected packages
• Packagist react/http: >= 0.6.0, < 1.11.1 → fixed in 1.11.1
Description
Summary
A malformed HTTP message using `Transfer-Encoding: chunked` can drive `React\Http\Io\ChunkedDecoder` into an infinite loop, pegging a CPU core and freezing the event loop. Because ReactPHP is single-threaded, one such message stalls the entire process for every client until it is killed.
Both directions are affected. `ChunkedDecoder` decodes chunked request bodies for `React\Http\HttpServer` and chunked response bodies for `React\Http\Browser`, so a server can be attacked by a malicious client and a client can be attacked by a malicious or compromised server.
Details
`ChunkedDecoder::handleData()` loops `while ($this->buffer !== '')` and relies on the buffer shrinking each iteration. Two states leave the buffer unchanged while the loop condition stays true.
Terminal-chunk trailer. After the terminating `0` chunk, any remaining buffer is treated as trailer data to skip:
```php
} elseif ($this->chunkSize === 0) {
$this->buffer = (string)\substr($this->buffer, $positionCrlf);
}
```
When the trailer holds no CRLF yet, `strpos()` returns `false`, PHP coerces that to `0` in `substr()`, and the buffer is never advanced. Neither the error guard (which requires a non-zero chunk size) nor the wait guard (which requires fewer than two bytes remaining) can fire, so the loop re-enters with identical state.
Off-by-one after a completed chunk. Once a non-terminal chunk has been fully transferred, the "chunk does not end with a CRLF" error guard requires `strlen($this->buffer) > 2` while the wait guard requires `< 2`. Exactly two non-CRLF bytes slip past both, and because the chunk is already complete nothing is consumed on the next iteration.
PoC
Run the example server from the `reactphp/reactphp` README and send a malformed request. Note the missing trailing `\r\n`:
```
POST / HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nab
```
The PHP process pegs at 100% CPU and stops answering legitimate requests. A body of `1\r\nAAB` triggers the second state.
The client side is reachable the same way: a `Browser` request to a server that answers with `Transfer-Encoding: chunked` and either malformed body shape hangs the client process.
Impact
Denial of service. The affected process stops responding entirely and has to be killed.
Servers behind a reverse proxy that parses and re-frames HTTP, such as a typical nginx setup, are not affected on the server side, because the proxy normalises the request before it reaches PHP. That mitigation does not extend to the client side: outbound requests made with `Browser` reach the remote server directly, so an application fetching attacker-influenced URLs is affected regardless of what sits in front of it.
Related
Alias · CVE (1)
- CVE-2026-84997 react/http is an event-driven, streaming HTTP client and server implementation for ReactPHP. vulnerability
Weakness · CWE (1)
- CWE-835 Loop with Unreachable Exit Condition weakness
References
- https://github.com/reactphp/http/security/advisories/GHSA-x424-64qh-5j54 web
- https://nvd.nist.gov/vuln/detail/CVE-2026-84997 advisory
- https://github.com/reactphp/http/commit/b6d4688790adf3797071fcf88a3fc4225f30486a web
- https://github.com/FriendsOfPHP/security-advisories/blob/master/react/http/CVE-2026-84997.yaml web
- https://github.com/reactphp/http package
- https://github.com/reactphp/http/releases/tag/v1.11.1 web