Critical PHP Vulnerabilities Discovered October 2026
On October 21-22, 2026, the PHP development team released PHP 8.5.12 along with security updates for PHP 8.4.27, 8.3.36, and 8.2.35. Three CVEs were addressed.
| CVE | Severity | Title | Affected Versions | Patched In |
|---|---|---|---|---|
| CVE-2026-109618 | Critical | RCE via OOM corruption | < 8.2.35, < 8.3.36, < 8.4.27, < 8.5.12 | 8.2.35, 8.3.36, 8.4.27, 8.5.12 |
| CVE-2026-109619 | Critical | RCE via stale global state | < 8.2.35, < 8.3.36, < 8.4.27, < 8.5.12 | 8.2.35, 8.3.36, 8.4.27, 8.5.12 |
| CVE-2026-93682 | Moderate | Out-of-bounds read in HTTP stream wrapper | < 8.2.34, < 8.3.35, < 8.4.26, < 8.5.11 | 8.2.34, 8.3.35, 8.4.26, 8.5.11 |
CVE-2026-109618: RCE via OOM Corruption
Severity: Critical • CVSS 7.5 (AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H)
Summary
PHP uses longjmp() for out-of-memory (OOM) errors to exit the current context and abort request handling. In certain scenarios, when memory allocation fails at an early initialization point, the request memory is left in an invalid state. An attacker can trigger multiple OOM strategies through carefully crafted input, leading to remote code execution.
How the Vulnerability Works
The bug exploits PHP's memory management during request processing. Multiple OOM pathways can be triggered:
- Multipart requests without usable content length: A request without a declared content length may pass the initial
post_max_sizecheck and continue supplying decoded body data. - Unrestricted headers in multipart parts: A multipart part can contain an unrestricted number of headers, consuming substantially more memory than their wire representation.
- Application-specific pathways: Attacker-controlled input can cause large allocations or repeated memory use to reach OOM.
When OOM is triggered, PHP's longjmp() exits the current context without properly cleaning up request memory, leaving it in an invalid state. Subsequent request operations can then read or branch on this corrupted memory, and with a strategically crafted heap layout, this leads to full remote code execution.
Exploitation Requires Producer and Consumer
- Producer: A PHP operation that stores state owned by one request in a location accessible later in a subsequent request.
- Consumer: A subsequent PHP operation that reads, frees, branches, or otherwise acts on the stale state.
State can survive because:
- A fatal error (including one caused by memory exhaustion) can abandon an operation before it performs its matching reset.
- A warning or other nonfatal diagnostic can also repopulate error state after that state was already cleared during shutdown.
- The request heap is then freed while the worker global field still refers to it.
- A later operation that runs before normal request initialization can consume the stale value.
Patch Summary
The fix ensures proper reset of global state before request memory is released, and adds validation that state is properly initialized before being consumed in subsequent requests.
CVE-2026-109619: RCE via Stale Global State
Severity: Critical • CVSS 7.5 (AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H)
Summary
This vulnerability stems from improper handling of PHP's global state across request boundaries. When certain operations fail to reset global state, stale pointers can be consumed by subsequent requests, leading to use-after-free conditions and ultimately remote code execution.
How the Vulnerability Works
PHP contains various global state variables (error state, session state, object state, and extension state) that are retained by a worker process but point to memory owned by a specific request. This state must be properly reset before the request memory is released and the next request begins.
The failure scenario:
- A fatal error or warning occurs during request processing
- The error/warning handler repopulates global state (e.g.,
$php_errormsg,$_SESSION, error handlers) after that state was already cleared during shutdown - The request heap is freed while the worker global field still refers to the old memory location
- A subsequent request — before normal initialization completes — consumes the stale value
- Successful exploitation requires a strategically crafted heap layout to convert the use-after-free into RCE
Triggering Vectors
- Fatal errors (including those caused by memory exhaustion) that abandon operations before matching reset
- Warnings and other nonfatal diagnostics that repopulate error state after it was already cleared
- Session-related operations that don't properly reset state
- Error handling code paths that run before normal request initialization
Patch Summary
The fix adds comprehensive reset of all global state at request shutdown, ensures state is only accessed after proper initialization, and adds debugging assertions to catch state leaks during development.
CVE-2026-93682: Out-of-Bounds Read in HTTP Stream Wrapper
Severity: Moderate • CVSS 5.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)
Summary
A vulnerability in PHP's HTTP stream wrapper. When following a redirect with an empty Location header, the code reads one byte past the end of a heap buffer, which determines the redirect target. While this doesn't directly enable RCE, it can cause non-deterministic behavior and is detectable by security tools.
How the Vulnerability Works
The HTTP stream wrapper (php_stream_http_response_headers_parse()) stores the Location header value in a heap buffer of exactly location_len + 1 bytes. When the Location header has an empty value:
- The buffer is allocated with
emalloc(last_header_value_len + 1)wherelast_header_value_len = 0 - The buffer contains only the NUL terminator
- During redirect handling, the code reads the second byte of the location without proper bounds checking
- This reads one byte past the intended buffer, an out-of-bounds read
The read byte decides which redirect target is built:
- If the byte is NUL (0), the client is redirected to the host root (
/) - If the byte is any other value, the client is redirected to the current directory path
An attacker hosting a malicious server can control which target the client is sent to, potentially facilitating open redirect attacks.
Proof of Concept
$ctx = stream_context_create([
'http' => ['follow_location' => 1]
]);
file_get_contents("http://host/a/b", false, $ctx);A server responding with:
HTTP/1.1 302 Found
Location:
Content-Length: 0
Will cause non-deterministic redirect behavior depending on stray memory contents.
Mitigation and Recommendations
Immediate Actions
-
Upgrade PHP Immediately
- PHP 8.5.12, 8.4.27, 8.3.36, or 8.2.35
- Download from: https://www.php.net/downloads.php
-
Verify Current Version
php -v # Should show 8.5.12 or newer for 8.5 series -
For Load Balancers and Proxies
- Ensure reverse proxies (NGINX, Caddy, HAProxy) strip or validate
Locationheaders in 302 responses - Monitor for unexpected redirect behavior in application logs
- Ensure reverse proxies (NGINX, Caddy, HAProxy) strip or validate
-
For Multipart Form Submitters
- Limit
post_max_sizeandmax_upload_filesizeto appropriate values - Validate Content-Type and Content-Length on incoming requests
- Consider adding proxy-level multipart parsing validation
- Limit
Long-Term Hardening
- Enable opcache with
opcache.enable=1andopcache.revalidate_freq=2to reduce re-entry points - Use PHP-FPM with
pm.max_childrenset to prevent memory exhaustion across children - Monitor PHP error logs for repeated warnings about state corruption
- Implement health checks that verify PHP version and configuration after each deployment
Detection
Security teams can detect exploitation attempts by:
- Looking for
302 Foundresponses with emptyLocationheaders in web server logs - Monitoring for unusual redirect patterns in application analytics
- Checking for use-after-free crashes in PHP error logs (CVE-2026-109618/109619)
Affected PHP Versions Summary
| Series | Before Fix | After Fix |
|---|---|---|
| PHP 8.5 | < 8.5.11 | 8.5.12 |
| PHP 8.4 | < 8.4.26 | 8.4.27 |
| PHP 8.3 | < 8.3.35 | 8.3.36 |
| PHP 8.2 | < 8.2.34 | 8.2.35 |
| PHP 8.1 | Not affected (these CVEs affect 8.2+) | N/A |