The PHP team shipped PHP 8.5.10 on August 27, 2026, and this one carries no CVEs. It's a bug fix release, and the theme running through it is crashes. Seven separate stack overflows on deeply nested data now throw an Error instead of taking the process down, a set of use-after-frees in Streams, XSL, and PDO_PGSQL are closed, and the JIT picked up four fixes. PHP 8.4.25 went out the same day with most of the same fixes.
Here's what's in it:
- Stack overflows in
==,===,array_walk_recursive(),array_replace_recursive(),compact(), and three DOM methods now throw a catchableError \Cin a regex pattern with theumodifier is now a compile error instead of a crash- Four PDO_PGSQL lazy fetch defects fixed, including a 100% CPU infinite loop present since 8.5.0
- Four JIT fixes, one of them wrong code for property hook getters passed as function arguments
DOMElement::setAttribute()no longer fails silently when the DTD declares a default for the attribute- Memory safety fixes in Session, Streams, XSL, SimpleXML, Sockets, and Reflection
What's New
Stack Overflows Become Catchable Errors
Take two arrays, nest them twenty thousand levels deep, and compare them with ==. On 8.5.9 you get a segfault, because zend_hash_compare() recurses once per level until it runs out of stack. The same shape of input crashed array_walk_recursive(), array_replace_recursive(), and compact(), along with DOMDocument::normalize(), Dom\XMLDocument normalization, and DOMNode::isEqualNode().
Lazizbek Ergashev fixed most of them the same way: check the stack limit that zend.max_allowed_stack_size already tracks before recursing, and throw if it's been hit. So this:
$a = []; $b = []; for ($i = 0; $i < 20000; $i++) { $a = [$a]; $b = [$b]; } var_dump($a == $b);
now throws Error: Maximum call stack size reached during comparison, which you can catch. This matters more than it might sound. Anything that decodes untrusted JSON or XML and then compares, walks, or normalizes the result was one crafted payload away from a crash, and a crash in PHP-FPM takes the worker with it.
Issues: GH-23088, GH-23111, GH-23113, GH-23115, GH-23116, GH-23117, GH-23120 PRs: GH-23090, GH-23124, GH-23125, GH-23126, GH-23127, GH-23140
\C Is Forbidden in UTF-8 Patterns
PCRE's \C escape matches a single byte, and in UTF-8 mode that can land in the middle of a multibyte character. PCRE2 documents the behavior as undefined, and in PHP it produced a negative-length match that preg_match_all() tried to copy. Arnaud Le Blanc's fix compiles every u pattern with PCRE2_NEVER_BACKSLASH_C, so a pattern that uses both now fails to compile:
preg_match_all("/(.*)\\C/u", "Ã ", $m); // Warning: preg_match_all(): Compilation failed: using \C is // incompatible with the 'u' modifier at offset 6
The call returns false and $m is null, the same as any other pattern that fails to compile. Without the u modifier \C works as before.
PDO_PGSQL Lazy Fetch
Setting PDO::ATTR_PREFETCH => 0 on a PostgreSQL connection streams the result set instead of buffering it, and Kentarou Takeda tracked down four bugs in how that stream gets cleaned up. The worst is an infinite loop: if a lazy fetch was left sitting in a COPY and another lazy fetch took the connection over, cleanup called PQgetResult() forever waiting for a NULL that a copy never returns, and the process sat at 100% CPU. That's been there since 8.5.0.
The other three are a use-after-free when a statement with emulated or disabled prepares is destroyed, a connection left busy so the next lazy fetch failed with "another command is already in progress", and a statement whose stream had been taken over returning a row of NULLs from fetch() instead of false.
PR: GH-23065
JIT Fixes
Zhao Hao fixed a function JIT bug where reading a virtual property hook as the first argument to an unqualified builtin call from inside a namespace handed the callee a value from the wrong memory. The reporter hit it as file_get_contents() complaining about null bytes in a path that came straight from a get => hook (GH-22857). Arnaud Le Blanc fixed a register-tracking bug (GH-22763) and a deoptimizer bug that didn't preserve parent registers (GH-22915), and David Carlier fixed a DT_TEXTREL in JIT-generated TLS access on x86_64 (GH-22693). Arnaud also fixed a crash when a first-class callable like strlen(...) appears in a constant expression under preloading (GH-22782).
Other Fixes Worth Noting
DOMElement::setAttribute() on an attribute the DTD declares with a #FIXED default hit an assertion on debug builds and silently did nothing on release builds. It creates the attribute again, as it did on 8.1 through 8.3 (GH-22825).
Session got two memory fixes from Niels Dossche: corruption in the mm save handler, and a heap corruption when a custom handler's create_sid() returns an invalid ID (GH-23043). Streams closed a use-after-free when a user stream filter touches $this->stream during the close flush, one that's been open since 2024 (GH-15836). XSL fixed a use-after-free when a DOMDocument subclass's __clone() holds onto the stylesheet copy XSLTProcessor::importStylesheet() made.
MBString fixed mb_strrpos() returning the wrong position for a negative offset in non-UTF-8 encodings (GH-22779) and a crash in mb_ereg_search_getregs() after mb_eregi() invalidates the regex cache (GH-21036). Reflection exception messages no longer truncate on null bytes (GH-22905), which follows the __toString() fixes in PHP 8.5.9. PDO_ODBC stopped returning garbage binary strings for NULL values in long columns (GH-23016), SQLite fixed a leak when closing a database with a blob stream still open, and Date fixed a leak on a double DatePeriod::__construct() call. Sockets picked up memory fixes and better socket_set_option() validation messages, and Intl stopped IntlListFormatter::__construct() leaving stale global error state behind after successful calls.
Upgrade Notes
No new features and no CVEs, this is a patch release. One thing can break: a regex that combines \C with the u modifier will stop compiling. Grep for \C in your patterns before you upgrade, since the fix is to drop the u flag or rewrite the pattern without \C. Everything else is a fix for something that already crashed or returned wrong data.
If you run PostgreSQL through PDO with ATTR_PREFETCH => 0, or if your app decodes untrusted nested input, schedule this one. Otherwise it can go out with your next batch. PHP 8.4.25 carries most of the same fixes, including the stack overflow work and the \C change. The PDO_PGSQL lazy fetch fix is 8.5 only, since the bug arrived in 8.5.0. PHP 8.3 and 8.2 are in security-only support and got nothing this round. Most people will pick this up through their distribution's packages or a Docker image rather than building from source, so check what your base image ships before you plan anything.