Deep dive

CVE-2026-109619 Explained: RCE via Stale Global State in PHP

Published 21 October 2026 · 6 min read · by PHP2Shell Scanner

CVE-2026-109619, titled “RCE via stale global state,” is the second half of PHP2Shell. Like its sibling it is CVSS 9.2 and leads to unauthenticated remote code execution — but through a different mechanism.

The root cause

PHP is usually run as a pool of persistent worker processes (PHP-FPM, or Apache workers with mod_php) that handle one request after another. Each request builds up “request-owned” global state: error context, session data, object stores and extension state. Normally that state is torn down cleanly at the end of every request.

The bug: when a request ends with a fatal error — including an out-of-memory condition — some of that global state is left behind in the worker after its backing memory has been freed. On the next request, certain operations run before normal request initialization and read that stale pointer. The result is a use-after-free, and with a crafted heap layout it becomes code execution.

The early consumers

Two code paths were named as reading the stale state early:

The strongest confirmed case involves PHP 8.5's fatal_error_backtraces setting, which captures a backtrace when a fatal error occurs — exactly the moment the dangling state is created.

Mitigations (if you can't patch yet)

Updating is the only complete fix, but these partial mitigations reduce exposure:

; php.ini
session.upload_progress.enabled = Off
fatal_error_backtraces = 0   ; PHP 8.5 only

Restart PHP-FPM/Apache after changing php.ini. See the full list in Temporary mitigations for PHP2Shell.

The fix

The patch ensures request-owned global state is invalidated when a request aborts, so a reused worker can no longer read freed memory. Fixed in 8.2.35, 8.3.36, 8.4.27 and 8.5.12.