Skip to content

Commit a2011db

Browse files
EdmondDantesbukka
authored andcommitted
Fix use-after-free when pclose() closes a stream from its user filter
fclose() refuses a stream marked PHP_STREAM_FLAG_NO_FCLOSE, which a user filter's callback sets on its stream; pclose() closed it anyway, freeing the stream and the filter under the running callback. Closes GH-24175
1 parent 662bc31 commit a2011db

3 files changed

Lines changed: 35 additions & 0 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ PHP NEWS
2828
callback). (Ilia Alshanetsky)
2929
. Fixed leaks and data loss when user filter leaves buckets on input
3030
brigade. (ndossche, Matthias Goergens, Jakub Zelenka)
31+
. Fixed bug GH-24175 (Use-after-free when pclose() closes a stream from its
32+
user filter). (Edmond)
3133

3234
19 Nov 2026, PHP 8.4.28
3335

‎ext/standard/file.c‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,11 @@ PHP_FUNCTION(pclose)
851851

852852
PHP_STREAM_FROM_ZVAL(stream, res);
853853

854+
if ((stream->flags & PHP_STREAM_FLAG_NO_FCLOSE) != 0) {
855+
php_error_docref(NULL, E_WARNING, ZEND_LONG_FMT " is not a valid stream resource", stream->res->handle);
856+
RETURN_LONG(-1);
857+
}
858+
854859
FG(pclose_wait) = 1;
855860
zend_list_close(stream->res);
856861
FG(pclose_wait) = 0;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
pclose() of the stream from its user filter's callback
3+
--FILE--
4+
<?php
5+
class Closer extends php_user_filter {
6+
public function filter($in, $out, &$consumed, bool $closing): int {
7+
global $fp;
8+
if (!$closing) {
9+
var_dump(pclose($fp));
10+
}
11+
while ($bucket = stream_bucket_make_writeable($in)) {
12+
$consumed += $bucket->datalen;
13+
stream_bucket_append($out, $bucket);
14+
}
15+
return PSFS_PASS_ON;
16+
}
17+
}
18+
19+
stream_filter_register('closer', 'Closer');
20+
$fp = fopen('php://memory', 'w+');
21+
stream_filter_append($fp, 'closer', STREAM_FILTER_WRITE);
22+
fwrite($fp, 'abc');
23+
var_dump(pclose($fp));
24+
?>
25+
--EXPECTF--
26+
Warning: pclose(): %d is not a valid stream resource in %s on line %d
27+
int(-1)
28+
int(0)

0 commit comments

Comments
 (0)