PHP2Shell is a failure at the boundary between an HTTP request and the PHP worker that serves it. PHP normally frees a request's temporary memory when that request ends. In two vulnerable paths, the worker can keep internal references to that memory, or enter shutdown with partially initialized data. A later operation then treats invalid memory as live state. Researchers demonstrated both unauthenticated memory disclosure and remote code execution from these conditions.
The PHP security team tracks the issues as CVE-2026-109618 and CVE-2026-109619. They were disclosed with emergency releases on 21 October 2026. This is our technical reading of the public advisories and research, with the operational work that follows from them. The PHP team developed and documented the fixes.
The trust boundary is a worker lifetime
A PHP worker can serve many HTTP requests. Each request gets temporary allocations; the worker also holds process-level state for errors, sessions, extensions and compiled code. That design is safe only if every reference from long-lived state to request-owned memory is cleared before the request heap is released.
Request A: publish request-owned state → fatal interruption → free request heap
Worker: a global still references the freed allocation
Request B: early initialization reads that global → stale memory is used
Outcome: memory disclosure, corruption, or code execution
The second request matters. A use-after-free can be hard to exploit when freed memory contains unpredictable bytes. If an attacker can influence how that memory is reused, the stale reference can point into attacker-shaped data. The public advisories describe producer and consumer operations that cross this request boundary, and the researchers showed how to turn the resulting primitives into code execution. The exact chain depends on the PHP build, SAPI, configuration and application behavior.
CVE-2026-109618: shutdown after an out-of-memory jump
Historically, PHP used longjmp() to leave the current execution context after an out-of-memory condition. That jump can bypass the cleanup code of an operation that has published a temporary pointer but has not finished initializing the object behind it. Request shutdown then encounters state that looks valid enough to inspect, although its initialization was interrupted.
Multipart request parsing is one way to reach that condition before application code runs. PHP may allocate much more memory to represent form fields, file metadata and part headers than the bytes visible on the wire suggest. The PHP advisory documents cases in which the usual input limits do not fully bound those allocations. On Apache with mod_php, shutdown can still run after startup fails, so an unauthenticated request can reach the vulnerable cleanup path without a WordPress, Laravel or custom application bug.
PHP-FPM handles a failed startup differently and can terminate that child. That narrows this particular startup path; it does not make FPM a safe exception. Fatal allocation failures after startup can still reach susceptible state, and the related stale-state issue has a validated FPM path.
CVE-2026-109619: state from one request survives into another
The second CVE concerns worker globals that refer to request-owned memory. A fatal error can interrupt the code that would clear them. A diagnostic emitted late in shutdown can also repopulate error state after its normal reset. When the same worker accepts another request, input parsing or configuration handling may touch those globals before normal initialization replaces them.
Session upload progress is a concrete example. PHP can process it while reading a multipart body, before application code calls session_start(). A previous request can leave session or decoding state behind; a later upload operation can consume that stale state. The published research also describes a fatal-error backtrace path and an OPcache state mismatch. These are distinct routes through the same lifetime mistake: request-owned data outlives the request that owned it.
The demonstrated impact includes reading PHP worker memory that may hold tokens or credentials, and executing code with the web worker's privileges. A successful exploit may therefore leave little useful evidence in application logs: the first vulnerable operation can happen during PHP's own request startup.
What the PHP patch changes
For CVE-2026-109618, the PHP fix removes the abrupt longjmp() path for memory-limit failures. It defers a recoverable failure to a VM safe point and uses an immediate process exit at a higher hard limit. That prevents request shutdown from walking partially initialized state after an arbitrary jump.
For CVE-2026-109619, the complete fix resets request-owned global state before the memory it references is released, so early operations in the next request cannot consume stale pointers. These are runtime changes. Updating a CMS theme, plugin or application package does not apply them.
What operators need to fix today
- Inventory the PHP that serves HTTP. Identify the effective version and SAPI for every public site, API, container image, FPM pool, Apache module and managed hosting environment. The
php -voutput from a shell is only a starting point: CLI PHP can differ from the binary serving web traffic. - Deploy a fixed runtime. PHP published 8.5.12, 8.4.27, 8.3.36 and 8.2.35. For older or vendor-maintained branches, use a vendor package that explicitly backports both fixes, or move to a supported release. Rebuild images and restart every worker so old processes stop accepting requests.
- Verify the running fleet. Check the package or image actually deployed at each origin and the runtime reported by its HTTP-facing process. Confirm that every pool and replica restarted. A patched host package does not update a PHP binary bundled inside an appliance or container.
- Review signs of exploitation. Correlate unusual multipart traffic with memory-limit or fatal errors, bursts of HTTP 500s, unexpected worker restarts, malformed or oversized session cookies and unexplained process execution. These are leads for investigation, not a reliable vulnerability test or proof of compromise. If compromise is credible, preserve evidence and assess whether credentials and tokens handled by the worker need rotation.
Teams that cannot deploy immediately should follow the PHP security advisory for temporary exposure reduction. It describes disabling session.upload_progress.enabled and restarting workers, disabling fatal_error_backtraces on PHP 8.5, and considering an administratively enforced memory_limit=-1 alongside strict request-size and container limits. Each measure interrupts a demonstrated path and carries operational tradeoffs. None covers every variant. Keep managed WAF protections enabled where available while the runtime rollout completes.
How to know the work is done
The closure condition is a running, patched PHP runtime on every HTTP-serving worker, including vendor backports where applicable. Record the deployed package or image digest, its security advisory, the worker restart and the origin it serves. Then test ordinary uploads, sessions and error handling after the upgrade. A clean scan of application code, a version string from the wrong SAPI or an absence of suspicious logs cannot establish that this runtime flaw is gone.
The useful lesson reaches beyond these two CVEs. Persistent workers must treat the end of a request as a strict ownership boundary. Fatal paths, error reporting and early request parsing deserve the same lifecycle guarantees as the happy path. PHP's emergency patches enforce that boundary in the runtime; operators have to put those patches in the processes that are actually answering requests.
Sources
This is an independent OliveX analysis of public research. See the PHP security advisory and the PHP team's advisories for CVE-2026-109618 and CVE-2026-109619. OliveX did not discover or exploit these vulnerabilities.
