Skip to content

[pull] master from php:master - #1180

Merged
pull[bot] merged 15 commits into
turkdevops:masterfrom
php:master
Aug 11, 2026
Merged

[pull] master from php:master#1180
pull[bot] merged 15 commits into
turkdevops:masterfrom
php:master

Conversation

@pull

@pull pull Bot commented Aug 11, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

lazerg and others added 15 commits August 11, 2026 19:01
* PHP-8.4:
  Add a stack limit check in php_count_recursive() (#23197)
* PHP-8.5:
  Add a stack limit check in php_count_recursive() (#23197)
…ment

implode() walks the array with ZEND_HASH_FOREACH_VAL while holding no
reference on it. Converting a Stringable element runs user code, and if
that code drops the last remaining reference to the array (`$a = null;`
from __toString()), arData is freed and the next iteration reads freed
memory. strtr() and str_replace() read their array arguments the same
way and crash the same way, so they are fixed here too.

Taking a reference on the table for the duration of the read keeps it
alive and turns an in-place mutation into a separation instead, same as
zend_compare_symbol_tables() does around zend_hash_compare(). In
implode() the reference is released after the pieces have been
concatenated, since the collected zend_strings are still owned by the
array until then.

Close GH-23207
* PHP-8.4:
  Fix GH-23204: use-after-free when __toString() destroys an array argument
* PHP-8.5:
  Fix GH-23204: use-after-free when __toString() destroys an array argument
With PDO::ATTR_PREFETCH => 0 a statement streams its result set, and the
cleanup reads the rest of it by calling PQgetResult() until it returns
NULL. That never happens while the connection is copying: PQgetResult()
hands out a fresh COPY result every time. A COPY run through a lazy fetch
has therefore spun at 100% CPU since 8.5.0, as soon as another lazy fetch
takes the connection over. The copy has to be ended first: a copy in with
PQputCopyEnd(), a copy out by draining PQgetCopyData().

The drain was skipped as well, because is_running_unbuffered was cleared
first, both in the cleanup's own abort path and in pgsql_stmt_fetch()
before it calls the cleanup. With PDO::ATTR_EMULATE_PREPARES or
Pdo\Pgsql::ATTR_DISABLE_PREPARES the connection then stayed busy and the
next lazy fetch failed with "another command is already in progress".

The connection's pointer to the statement streaming on it was only cleared
while closing a server-side prepared statement, which those two modes do
not create, so destroying one left the pointer dangling for the next lazy
fetch to read. And a statement whose stream was taken over kept its row
counters after its result had been freed, so fetch() returned a row of
NULLs rather than false.

Close GH-23065
* PHP-8.5:
  ext/pdo_pgsql: Fix several lazy fetch defects
* PHP-8.4:
  PHP 8.4 is now for PHP 8.4.26-dev
* PHP-8.5:
  PHP 8.4 is now for PHP 8.4.26-dev
Co-authored-by: DanielEScherzer <daniel.e.scherzer@gmail.com>
@pull pull Bot locked and limited conversation to collaborators Aug 11, 2026
@pull pull Bot added the ⤵️ pull label Aug 11, 2026
@pull
pull Bot merged commit a2640ae into turkdevops:master Aug 11, 2026
0 of 2 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants