Commit b2e02ccba0b for php
commit b2e02ccba0b6a87fadd99830b13440897de7e7e4
Merge: dfe6d76ac5c a2011dbfc17
Author: Jakub Zelenka <bukka@php.net>
Date: Sat Nov 7 12:30:40 2026 +0100
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
Fix use-after-free when pclose() closes a stream from its user filter
# Conflicts:
# ext/standard/file.c
diff --cc NEWS
index 4f3a6607e76,26abaf2f03a..053bd8f7169
--- a/NEWS
+++ b/NEWS
@@@ -30,8 -28,10 +30,10 @@@ PH
callback). (Ilia Alshanetsky)
. Fixed leaks and data loss when user filter leaves buckets on input
brigade. (ndossche, Matthias Goergens, Jakub Zelenka)
+ . Fixed bug GH-24175 (Use-after-free when pclose() closes a stream from its
+ user filter). (Edmond)
-19 Nov 2026, PHP 8.4.28
+19 Nov 2026, PHP 8.5.13
- Core:
. Fixed bug GH-24184 (System ID not being reinitialized after an Apache
diff --cc ext/standard/file.c
index 381618d4f5f,412af159f19..51258be9c73
--- a/ext/standard/file.c
+++ b/ext/standard/file.c
@@@ -837,9 -845,17 +837,14 @@@ PHP_FUNCTION(pclose
php_stream *stream;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_RESOURCE(res)
+ PHP_Z_PARAM_STREAM(stream)
ZEND_PARSE_PARAMETERS_END();
- PHP_STREAM_FROM_ZVAL(stream, res);
-
+ if ((stream->flags & PHP_STREAM_FLAG_NO_FCLOSE) != 0) {
- php_error_docref(NULL, E_WARNING, ZEND_LONG_FMT " is not a valid stream resource", stream->res->handle);
++ php_error_docref(NULL, E_WARNING, "cannot close the provided stream, as it must not be manually closed");
+ RETURN_LONG(-1);
+ }
+
FG(pclose_wait) = 1;
zend_list_close(stream->res);
FG(pclose_wait) = 0;
diff --cc ext/standard/tests/filters/pclose_in_filter.phpt
index 00000000000,32c6183db3c..0d13eaada0c
mode 000000,100644..100644
--- a/ext/standard/tests/filters/pclose_in_filter.phpt
+++ b/ext/standard/tests/filters/pclose_in_filter.phpt
@@@ -1,0 -1,28 +1,28 @@@
+ --TEST--
+ pclose() of the stream from its user filter's callback
+ --FILE--
+ <?php
+ class Closer extends php_user_filter {
+ public function filter($in, $out, &$consumed, bool $closing): int {
+ global $fp;
+ if (!$closing) {
+ var_dump(pclose($fp));
+ }
+ while ($bucket = stream_bucket_make_writeable($in)) {
+ $consumed += $bucket->datalen;
+ stream_bucket_append($out, $bucket);
+ }
+ return PSFS_PASS_ON;
+ }
+ }
+
+ stream_filter_register('closer', 'Closer');
+ $fp = fopen('php://memory', 'w+');
+ stream_filter_append($fp, 'closer', STREAM_FILTER_WRITE);
+ fwrite($fp, 'abc');
+ var_dump(pclose($fp));
+ ?>
+ --EXPECTF--
-Warning: pclose(): %d is not a valid stream resource in %s on line %d
++Warning: pclose(): cannot close the provided stream, as it must not be manually closed in %s on line %d
+ int(-1)
+ int(0)