PHP2Shell: Critical PHP Core RCE (CVE-2026-109618, CVE-2026-109619)
Short answer: CVE-2026-109618 and CVE-2026-109619 are memory-safety flaws in the PHP runtime itself, not in WordPress, Laravel, Drupal, or any other application. They can be triggered by crafted HTTP requests, before application code runs on Apache mod_php, and can lead to remote code execution as the web worker account. Patched releases are PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35. Updating PHP is the only complete fix; every configuration workaround removes specific paths, not the class of bug.
| Item | Detail |
|---|---|
| Identifiers | CVE-2026-109618 (OOM corruption), CVE-2026-109619 (stale global state) |
| Advisories | GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp |
| Severity | 9.2, CVSS v4.0 — AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H |
| Fixed versions | 8.5.12, 8.4.27, 8.3.36, 8.2.35 |
| Affected branches | All releases before the fixed version in 8.2–8.5; Wiz and Google Cloud state the flaw dates to PHP 5.4 |
| Authentication | None for the confirmed paths |
| Highest-risk SAPI | Apache mod_php |
| Patch date | 21 October 2026 |
What "PHP2Shell" actually refers to
PHP2Shell is the public name for a cluster of memory-safety issues. Cloudflare's write-up credits Wiz Research, Calif.io, OpenAI, and Quarkslab; the GHSA entries list individual researchers and do not name Calif or Quarkslab. Two CVEs were assigned, but the underlying problem is one architectural pattern: PHP can abort a request through an exceptional path while internal request state is only partially initialized or already published in worker-global storage. Cleanup or a later request then treats attacker-influenced memory as valid.
Both CVEs share the same root cause family, and the patch addresses the lifecycle rather than a single parser bug. Wiz describes four demonstrated primitives under the two identifiers — an upload-progress unserializer use-after-free, remote process-memory disclosure, a fatal-error backtrace use-after-free, and an OPcache type confusion — and states that additional variants likely exist. That matters for defenders: hunting for one request signature is not a substitute for patching.
The flaw is not application-specific. Wiz demonstrated unauthenticated code execution against current wordpress, drupal, and joomla container images, but also noted that a "Hello World" PHP script exposes the same engine-level path. Your CMS, framework, or WAF rule set does not remove the bug.
CVE-2026-109618: corruption during out-of-memory handling
PHP enforces memory_limit with longjmp(). When an allocation fails, PHP jumps out of the current context and aborts the request. In several startup paths, request memory is temporarily reachable before it has been fully initialized. If the allocation failure happens inside that window, PHP leaves the request in an invalid state, and request shutdown may read, branch on, or free memory it should not touch.
The failed allocation does not need to be related to the invalid structure. That is why a request can stay within its apparent PHP limits and still reach the bug:
- A multipart request without a usable declared
Content-Lengthcan pass the initialpost_max_sizecheck and keep supplying decoded body data. - A single multipart part can carry an unrestricted number of headers. PHP's in-memory representation costs substantially more than the wire format, so a body under
post_max_sizecan exhaustmemory_limit. - Empty file controls do not count toward
max_file_uploads, but they still create file metadata arrays. Nested field names multiply the generated array work.
The SAPI determines whether the bug is pre-authentication:
| SAPI | Behaviour | Practical risk |
|---|---|---|
Apache mod_php |
Calls request shutdown even when request startup failed | Application-independent, unauthenticated RCE before app code; confirmed with default limits |
| PHP-FPM | Exits the child when request startup fails | Pre-application startup path not demonstrated for FPM; RCE confirmed through application-level code paths. Still internet-facing and still urgent |
| FrankenPHP | Calls shutdown unconditionally, like mod_php |
Likely application-independent; source analysis only |
| LiteSpeed / LSAPI | Models FPM | Application-specific exploit likely; unvalidated |
php-cgi |
At risk per PHP's advisory | Not specifically validated |
The confirmed exploitation detail is the important one: over HTTP, against mod_php with default input and memory settings and a normal prefork configuration, the request never reaches the application. PHP executes the attacker's command while cleaning up the failed request. Multiple workers and unrelated traffic change the number of attempts required, not the existence of the condition.
Do not overcorrect that into "FPM is fine." The PHP Foundation's release note says remote code execution was also confirmed on PHP-FPM through application-level code paths, and that a use-after-free plus worker crash was confirmed for CVE-2026-109619 with RCE demonstrated against a real application on Apache mod_php. The distinction is about where the chain starts, not whether FPM is safe. mod_php is worse because a failed request startup can still run interpreter shutdown before any application code. FPM can be reached through an application operation that publishes state and a later request that consumes it.
CVE-2026-109618 also provides an application-agnostic memory disclosure primitive. Wiz demonstrated unauthenticated reads of worker memory that can contain credentials, tokens, and keys from other requests.
Inside the fix: OOM handling becomes a state machine
The patch is not a one-line bounds check. Commit 3584a98bda0f2c0d367255b046798b0d3a221a19, titled "Avoid state corruption via bailout," changes 389 files. The changelog entry is explicit: Fixed GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp (Avoid state corruption via bailout). Understanding the shape of the fix matters because it changes operational behaviour that some applications depend on.
Before the patch, a failed allocation under memory_limit eventually reached longjmp(). That unwinds the C stack from inside whatever parser, allocator, or extension happened to be running. If request startup was only half complete, the jump could skip initialization while leaving pointers that shutdown would later treat as valid. The bug class is the gap between "the request exists" and "the request is safe to destroy."
The patched runtime adds an explicit OOM lifecycle:
allocation request
|
+-- fallible allocation? --> return NULL, no longjmp
|
+-- non-fallible? --> soft limit reached?
|-- yes: record EG(delayed_oom), set vm_interrupt
| handle at a safe executor boundary
|
+-- hard limit (2x memory_limit)?
`_exit(137)`
The core pieces, as they appear in the 8.5.12 tree:
Zend/zend_alloc.hdefineszend_oom_statewithNONE,PENDING,HANDLING, andHANDLED, pluszend_oom_info { state, lineno, message, filename }. The per-executor record isEG(delayed_oom).ZEND_HAS_OOM()is a fast check on that state.- New fallible allocation entry points (
_femalloc,_fecalloc,_ferealloc,_ferealloc2,_safe_femalloc,_safe_ferealloc) returnNULLinstead of unwinding. Call sites that must survive OOM are converted to these APIs. That is why the diff is enormous: it is a conversion of allocation paths, not just new declarations. - A soft limit records the delayed OOM and requests a VM interrupt.
zend_execute_scripts()callszend_handle_delayed_oom()after execution, andzend_call_function()refuses to enter user code while the state is non-NONE. The error is raised at a boundary where request state is coherent. - A hard limit defaults to 200% of
memory_limitand calls_exit(137)when a non-fallible allocation crosses it. This is a last-resort containment, not a normal error path. It intentionally does not run user shutdown functions, because the point is to stop before corrupting state. main/main.ccallsphp_basic_early_rinit()immediately afterzend_activate()and beforesapi_activate(), so basic globals are reset before the SAPI can run request setup. The delayed-OOM state is cleared during request shutdown.Zend/zend_execute_API.cresets the user error and exception handlers, the handler arrays,record_errors,num_errors,errors,error_handling, the current exception, and the object store (object_buckets = NULL,top = 1) during executor shutdown. These are exactly the cross-request pointers that CVE-2026-109619 abused.Zend/zend_builtin_functions.cchangesdebug_backtrace_get_args()to returnbooland to use a packed, fallible array allocation. If the allocation fails, it returnsfalseandzend_fetch_debug_backtrace()aborts instead of walking an invalid frame. That path is the fatal-error backtrace consumer behind the confirmedmod_phpRCE.Zend/zend.csuppresses the uncaught-exception report while the OOM state isHANDLING, and callszend_mm_oom_info_dtorduring deactivation. The point is to avoid recursively invoking user handlers from inside OOM cleanup.ext/session/session.chardens upload-progress handling so the pre-RINIT path does not execute user code or touch classes that are not fully initialized. This is stronger than simply disabling the feature.SplFixedArrayand array helpers (array_fill,array_pad) are converted to fallible allocation with explicit failure handling. These were reachable OOM amplification points.
New PHPT coverage includes Zend/tests/oom_request_parsing.phpt, which drives php-cgi with memory_limit=8M, post_max_size=1G, max_input_vars=10000000, deeply nested variable names, and a value without a trailing &. There are also hard-limit, soft-limit, call-function, debug-backtrace, SplFixedArray, array_fill, and array_pad OOM tests, plus ext/session/tests/rfc1867_no_user_code.phpt.
For operators, the practical changes are:
- OOM is no longer guaranteed to abort at the exact allocation site. Fallible paths return
NULLand unwind through normal error handling; the fatal error is raised later, at a safe point. - There are now two thresholds.
memory_limitis the soft limit; roughly twice that is the hard_exit(137)limit. A process that previously died with a PHP fatal error may now disappear with exit code 137. Do not mistake that for the kernel OOM killer without checking. - User shutdown functions may not run on the hard-limit path. If your application relies on
register_shutdown_function()to flush buffers, release locks, or write an audit record after memory exhaustion, test that behavior explicitly. - Fallible allocation changes extension behaviour. A third-party extension built against the old API may still call non-fallible allocation and hit the hard limit. Rebuild extensions against the patched headers and test them.
- The fix does not make OOM free. An application can still exhaust memory, still return errors, and still fail requests. The fix removes the state-corruption window, not the availability problem.
This is also why "apply the patch and watch for 500s" is insufficient. Watch for exit code 137, worker restarts, and changed shutdown behaviour, then verify that your error handling still does what you think it does.
CVE-2026-109619: stale worker state across requests
The second CVE is a use-after-free across request boundaries. PHP keeps worker-global state — error, session, object, and extension state — that points into memory owned by one request. That state must be reset before the request heap is released. Some operations run before normal initialization of the next request and read the stale value first.
Exploitation needs a producer and a consumer:
- A reachable operation stores request-owned state in a globally accessible location.
- The state is not reset, or a later diagnostic repopulates it after the normal reset.
- PHP frees the request heap while the worker still holds the reference.
- The same worker handles another request.
- An operation reads, frees, branches on, or dereferences the stale value before initialization replaces it.
Two consumers are confirmed before normal request initialization:
- INI diagnostics. A warning, notice, or deprecation raised while applying configuration can consume stale error state. Invalid or deprecated
php.inivalues, per-directory Apache settings, andPHP_VALUE/PHP_ADMIN_VALUEparameters from a frontend are all candidates. The diagnostic does not need to be displayed or logged.error_reporting,@,display_errors=0, andlog_errors=0do not protect against this. - Session upload progress. PHP's RFC1867 upload-progress handling can reach session state before ordinary session initialization. It works even when the application never calls
session_start().
Confirmed primitives include the fatal-error backtrace path (the strongest case and the basis of the confirmed mod_php RCE), the session upload-progress lifecycle, and an OPcache type confusion across workers. The FPM path was demonstrated through application-level code paths, not through the pre-application startup path that makes mod_php so dangerous. In practice that means FPM exploitation depends on a reachable operation that publishes state, reaches a fatal error, and leaves a later upload-progress consumer on the same worker.
The session component deserves a precise warning. PHP's temporary mitigation guidance, as quoted by Datadog Security Labs, lists session.upload_progress.enabled=0 for CVE-2026-109619. Datadog states that community mitigations such as this setting "don't fully protect you." Whether the setting blocks only the confirmed session paths was not checked against PHP's advisory text in this audit. Treat it as risk reduction, never as a complete mitigation.
The patch's reset logic explains the class of bug better than any single exploit. During executor shutdown, PHP now clears the user error handler and exception handler, the handler arrays, the recorded error list and counts, error_handling, the current exception, and the object store. Those are worker-global pointers into request memory. If any one of them survives into the next request and is read before initialization replaces it, the next request dereferences freed memory. The fix makes the reset explicit and moves OOM handling to a point where the request can be torn down coherently.
There is one practical consequence that defenders miss: a request that never appears in application logs can still have corrupted the worker. Absence of a request in your framework log is not evidence of absence.
Who is affected
Every PHP installation that serves web requests is in scope, including PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, and LiteSpeed. The PHP project considers mod_php the highest-risk deployment.
| Branch | Last vulnerable release | Fixed release | Upstream status |
|---|---|---|---|
| 8.5 | 8.5.11 | 8.5.12 | Supported |
| 8.4 | 8.4.26 | 8.4.27 | Supported |
| 8.3 | 8.3.35 | 8.3.36 | Security fixes |
| 8.2 | 8.2.34 | 8.2.35 | Security fixes until 31 Dec 2026 |
| 8.1 and older | — | No upstream release | End of life; minimal source backports exist for 7.4, 8.0, and 8.1 |
| 5.4–7.3 | — | None | End of life, no upstream fix |
PHP's own advisories list affected releases in supported branches. Google Cloud and Wiz state that the vulnerable code dates to PHP 5.4; Google Cloud lists all versions from 5.4 through 8.5.11 as vulnerable. CERT-EU's advisory does not mention PHP 5.4. If you still run 5.4–7.3, you are relying on a distribution or vendor backport that may not exist. Treat those hosts as unpatched and exposed; confirm compromise only with host evidence, not from the version number alone.
Two frequently missed populations:
- Embedded PHP. Monitoring appliances, NAS firmware, print servers, VPN portals, and OT management interfaces often ship an old PHP behind a locked-down web UI. They are still network-reachable PHP SAPIs.
- Managed and serverless PHP. App Engine standard, Cloud Run, Cloud Functions, and similar platforms may patch base images automatically — but only when automatic updates are enabled and only for supported runtimes. A custom container, a pinned base image, or an end-of-life runtime does not inherit that protection.
Patch first, then verify the patch is actually running
The fix is in the 21 October 2026 releases. Distributions and LTS vendors received patches in advance. Do not assume that updating a base image tag, a lockfile, or a build manifest changed the runtime that is serving traffic.
Verification checklist:
- Confirm the running binary, not the build file. In every container and on every host:
php -vandphp -r 'echo PHP_VERSION, PHP_EOL;'. Distribution vendors often backport the fix without changing the upstream version string, so also check the package changelog or vendor advisory. Upstream 8.5.12, 8.4.27, 8.3.36, and 8.2.35 are the reference points, not the only valid build identifiers. - Confirm the SAPI.
php -i | grep 'Server API'distinguishesApache 2.0 Handler(mod_php),FPM/FastCGI,CGI/FastCGI, andLiteSpeed. Inside a container, also check the process that is actually serving traffic (/proc/<pid>/exe,ps, or your orchestrator's image digest), because a sidecar or supervisor can run a different PHP build than the one onPATH. - Restart workers. FPM children, Apache prefork workers, and OPcache/preloaded code must all come from the patched binary. A package upgrade without a worker restart can leave old children serving traffic.
- Roll the whole fleet. Canary one pool, verify, then drain and replace the rest. Check autoscaling groups, cron-run PHP, queue consumers, and admin panels, not only the public web tier.
- Verify image identity by digest, not tag. If you consumed a vendor image in the hours before disclosure, check whether it was rebuilt and whether your deployment actually pulled the new digest.
- Keep the ability to roll back. The patch changes OOM handling from
longjmp()to a delayed soft OOM plus a hard_exit()threshold. Test applications that depend on catching or logging memory exhaustion.
For end-of-life branches, use the PHP security team's minimal source backports for 7.4–8.1 only if you build PHP yourself and can maintain them. Otherwise upgrade. There is no backport for 5.4–7.3.
Interim controls: useful, partial, and easy to misread
PHP is explicit that no configuration change is a complete mitigation. Use these to reduce exposure while the patch rolls out, in this order.
1. Remove the controllable OOM path (CVE-2026-109618)
Set memory_limit=-1 administratively, for example with php_admin_value in the FPM pool or Apache configuration. Then verify that no pool, directory, .htaccess, or application call restores a finite limit, and restart every worker. This removes the predictable per-request failure that the confirmed exploit uses.
It is not complete. PHP's guidance is narrower than many summaries: removing the limit avoids the controllable OOM path, but an underlying allocation can still fail and reach the runtime's error handling. If mmap() or another lower-level allocation fails, a finite limit is not the only way to arrive at the vulnerable path. Pair it with a real memory boundary (see item 4) and do not treat -1 as a security control on its own.
2. Shrink and bound request input
- Set
post_max_sizeto8Mif the application does not need uploads, or32Mif it does; keepupload_max_filesizebelow it. - Lower
max_file_uploads,max_input_vars, andmax_input_nesting_level. - Enforce decoded body size, multipart part count, per-part header count, and nesting depth at the reverse proxy or load balancer. Cover requests without a usable
Content-Length. - If the application does not use multipart form data, block
multipart/form-databefore it reaches PHP. This is one of the highest-value controls formod_php.
3. Break the CVE-2026-109619 chains
- On PHP 8.5, set
fatal_error_backtraces=0administratively. This removes the producer used by the confirmed RCE case, not every variant. - Set
session.upload_progress.enabled=0if you do not need upload progress. Datadog states that community mitigations such as this setting do not fully protect you. It is useful risk reduction, not a complete mitigation. - Consider
pm.max_requests=1for high-risk pools where performance allows. It shortens the window in which a worker can carry stale state into a later request. - Remove deprecated or invalid INI directives that emit diagnostics during startup, including per-directory Apache settings. Prefer startup configuration over per-request overrides.
4. Do not use the wrong limits
Do not use RLIMIT_AS or RLIMIT_DATA as a substitute for memory_limit. They make mmap() fail at the limit, which is exactly the path that triggers CVE-2026-109618. A container or cgroup memory.max is a better availability boundary, but killing a cgroup can take out the FPM master and every worker unless each worker has its own group. Killing one FPM child affects only its current requests and the master replaces it.
5. Treat WAF as defence in depth, not remediation
Cloudflare deployed WAF protections about nine hours before public disclosure and enabled them by default for free and paid plans at 03:02 UTC on 21 October. The rules use full-transaction detection because request-only signatures were insufficient. That is useful time, not a fix: attackers were already mutating payloads against the rules, and the vulnerable code remains reachable from any path the WAF does not model.
Configuration changes that do not mitigate CVE-2026-109619 include error_reporting, the @ operator, display_errors=0, and log_errors=0. The stale state is accessed before PHP decides whether to report the diagnostic.
Detection and hunting
Because exploitation can complete before application code runs, application logs may contain no useful request context. Build the detection around the runtime and the edge.
Signals worth alerting on:
- Spikes in HTTP 500 responses and upstream 502/503 errors, especially followed by worker replacement or
OOMKilledevents. Allowed memory size of ... exhaustedmessages, PHP-FPM pool exhaustion, and unexplained worker churn.- Multipart requests with no usable
Content-Length, very high part-header counts, deeply nested field names, or many empty file controls. - Unusually large or malformed
PHPSESSIDvalues inSet-Cookieresponses. Wiz describes the memory-disclosure primitive as URL-encoding reclaimed heap data into that cookie; this article did not reproduce that behavior. - WAF events for PHP2Shell rules, including blocked requests that were followed by repeated variants from the same source.
- New outbound connections, child processes, or file writes by the web user after a burst of 500s.
Correlate by worker, not only by client IP. The producer and consumer requests must reach the same worker, so a single source IP may show a sequence that looks harmless in isolation. If you have request IDs at the edge but not in PHP, use timing and worker restart events to reconstruct the sequence.
Historical logs are not proof of safety. They may exclude deprecations, may not capture pre-startup failures, and only cover the routes that were exercised. Use logs together with configuration review, version inventory, and host-level telemetry.
If you suspect exploitation
Assume code execution as the web worker account until you can prove otherwise. The worker usually has read access to application code, configuration, session storage, upload directories, and environment variables.
- Contain without destroying evidence. Drain the pool or remove the node from the load balancer. Preserve process memory if your platform allows it, and snapshot disks before rebuilding.
- Preserve edge and runtime logs. WAF events, proxy logs, FPM slow logs, worker restart history, and container events matter more than application request logs here.
- Hunt persistence. Check upload and session directories for unexpected PHP files,
.htaccesschanges, new cron jobs,authorized_keys, systemd units, LD_PRELOAD, and unusual PHP extensions orauto_prepend_filesettings. - Check for credential access. Env files,
.env, database configs, cloud metadata access, SSH keys, and application secrets are all reachable from the worker. Rotate secrets after containment, not before you understand scope. - Rebuild from a patched image. Removing a webshell from a compromised host is not remediation. Rebuild, redeploy, and verify the runtime version inside the new containers.
- Notify. If data was accessible, treat the event as a potential breach and follow your legal and contractual notification process.
Patching alone does not undo access that already happened. The memory-disclosure primitive means secrets may have left the host without any code execution.
Why 9.2, and why "critical" still needs context
The CVSS v4.0 vector is AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H. Network reachable, no privileges, no user interaction, high confidentiality, integrity, and availability impact. Complexity is high and attack requirements are present, which is why the score is 9.2 rather than 10.
In practice:
mod_phpdeployments with default limits are the closest thing to a reliable unauthenticated exploit.- FPM deployments are not automatically reachable through the pre-application path that makes
mod_phpso dangerous, but RCE was confirmed through application-level code paths and CVE-2026-109619 depends on a producer/consumer path.mod_phpremains the emergency; FPM remains urgent. - Exploit portability depends on the PHP build, extensions, frontend behaviour, and allocator layout. Multiple workers increase attempts; they do not remove the condition.
- The memory-disclosure path is application-agnostic and may be easier to weaponise at scale than full RCE.
Do not describe this as a self-propagating worm without evidence. Do treat internet-facing mod_php as an emergency.
Disclosure timeline and the supply-chain lesson
| Date (2026) | Event |
|---|---|
| August (not verified) | GHSA-w8hw-w9fx-j79w credits Wiz- and OpenAI-linked researchers; the August timing, Calif's role, and the multi-month fix and variant hunt are not verified |
| 20 October | CVE identifiers circulate and PHP publishes a pre-announcement |
| 21 October, early hours | Reported, unverified: a proof-of-concept video leaks and vendors report exploitation attempts before the patch |
| 21 October, 03:02 UTC | Cloudflare enables default WAF protections |
| 21 October, time not verified | Reported, unverified: a vendor publishes container images containing the fix before the coordinated release |
| 21 October, 13:30 UTC | CERT-EU 2026-018 states the PHP fixes were released "today" at 13:30 UTC |
| 21 October, ~14:00 UTC | php.net's release archive lists 14:00 UTC; this conflicts with CERT-EU's 13:30 UTC. The 13:30–14:00 UTC window is unresolved |
| 21 October, afternoon | Public write-ups and PoCs spread; mass exploitation attempts reported |
Two lessons for defenders:
- A patch diff is an exploit primitive. When images with the fix appear before the advisory, attackers can reverse the change while defenders are still waiting. Verify image digests and provenance, and do not assume a vendor tag published early is safe to adopt blindly.
- Your exposure window starts at the leak, not the advisory. If you only start inventory when the CVE is public, you have already lost the hours that mattered.
A 24-hour action plan
| Role | Action |
|---|---|
| Platform / SRE | Inventory every PHP runtime and SAPI. Patch internet-facing mod_php first, then FPM and FrankenPHP. Restart workers and verify the binary in each container. |
| Application security | Identify multipart upload endpoints, upload-progress usage, and request paths that can reach OOM. Add edge limits. Check for code paths that depend on memory_limit behaviour. |
| SOC / IR | Enable or tune WAF rules, alert on worker churn and 500 bursts, preserve edge logs, and prepare a credential-rotation and webshell-hunt playbook. |
| Hosting / managed service | Confirm which base images were rebuilt, whether automatic updates are enabled, and which customers run end-of-life PHP. Publish a clear customer action. |
| Leadership | Treat customer-facing mod_php as a sev-1 change window. Approve emergency change control and communicate uncertainty honestly: patched is fixed, mitigated is not. |
Frequently asked questions
Does updating WordPress, Drupal, or my framework fix this?
No. The vulnerable code is in the PHP runtime. Updating the application does not change how PHP handles request startup, OOM, or worker-global state. You must update PHP itself.
Is PHP-FPM safe?
No. FPM is less exposed to the pre-application startup path because it exits a child when request startup fails. That is not a clean bill of health: the PHP Foundation confirms RCE on PHP-FPM through application-level code paths, and the CVE-2026-109619 session and fatal-error chains still apply. Patch FPM with the same urgency, especially if the application performs uploads, session handling, or custom error handling.
Does memory_limit=-1 make me safe?
It removes the controllable OOM trigger used by the confirmed exploit, but it is explicitly not a complete mitigation. Underlying allocation failures can still occur, and the setting removes a useful availability control. Pair it with a cgroup or container memory boundary and do not treat -1 as a security fix.
Is disabling session upload progress enough for CVE-2026-109619?
No. Datadog states that this setting does not fully protect against CVE-2026-109619. It reduces one known producer/consumer route. It does not reset the other worker-global pointers or remove the fatal-error backtrace path.
Why do patched workers sometimes exit with code 137?
The patch adds a hard OOM limit at roughly 200% of memory_limit; crossing it calls _exit(137) to stop before state corruption. That can look like a kernel OOM kill. Check PHP's memory settings, the request that triggered it, and the process exit history before blaming the container runtime.
Can a WAF block this reliably?
A WAF can buy time, and Cloudflare's full-transaction rules are a serious effort. It cannot fix a runtime memory-safety bug. Attackers were already mutating payloads against the rules before the patch, and request-only signatures cannot model every producer/consumer sequence.
What if I cannot patch an end-of-life PHP 5.x or 7.x host?
Isolate it behind an authenticating proxy, block multipart uploads at the edge, remove it from the public internet, and plan replacement. There is no upstream fix for 5.4–7.3. If it has been internet-facing for years, hunt for compromise rather than assuming it was too obscure to target.
Sources and further reading
- PHP security advisory, CVE-2026-109618: GHSA-w8hw-w9fx-j79w
- PHP security advisory, CVE-2026-109619: GHSA-6wmf-4rv5-p3rp
- Patch commit, "Avoid state corruption via bailout": 3584a98bda0f2c0d367255b046798b0d3a221a19
- PHP release archive, 21 October 2026: php.net/archive/2026.php
- PHP 8 changelog, fix entry "Avoid state corruption via bailout" (ChangeLog page date not verified): php.net/ChangeLog-8.php
- Wiz Research, PHP2Shell technical analysis: wiz.io/blog/php2shell-php-rce
- CERT-EU advisory 2026-018: cert.europa.eu
- Google Cloud response and platform guidance: cloud.google.com/blog
- Cloudflare WAF deployment and rule details: blog.cloudflare.com/php-vulnerabilities-waf-protection
- Datadog Security Labs reproduction notes: securitylabs.datadoghq.com
- Tenable FAQ: tenable.com/blog
AI Security Starter Training
Request a practical checklist for prompt injection, RAG data leakage, MCP risks, and model-file security before launch.
Related Research
wp2shell: Unauthenticated WordPress Core RCE via REST API Batch-Route Confusion (CVE-2026-63030 + CVE-2026-60137)
wp2shell chains a WordPress REST API batch-route confusion with a core SQL injection to reach unauthenticated remote code execution. What it is, who is affected, and how to respond.
Threat AnalysisThe April 2026 MCP RCE Wave
Why MCP security depends on architecture, identity, tool isolation, and registration control more than a single CVE.
AppSecThe Limitations of WAF: Why Firewalls Alone Can't Prevent Hacks
Your company relies on a pricey WAF (Web Application Firewall) to block threats. But why is a WAF completely blind to logical flaws? Discover how manual...
Related Services
Scope Estimator
Get a rough engagement size before the scoping call.
Estimated engagement
5–7 days