[php-src] PHP-8.4: Fix use-after-free when removing a failing zlib.inflate filter (#24176)

From: Date: Fri, 09 Oct 2026 19:59:12 +0000
Subject: [php-src] PHP-8.4: Fix use-after-free when removing a failing zlib.inflate filter (#24176)
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-142131@lists.php.net to get a copy of this message
Author: Edmond (EdmondDantes) Committer: Weilin Du (LamentXU123) Date: 2026-10-10T03:45:59+08:00 Commit: https://github.com/php/php-src/commit/d5f84c738b984b67079fddf8f4d77201a2bf01d6 Raw diff: https://github.com/php/php-src/commit/d5f84c738b984b67079fddf8f4d77201a2bf01d6.diff Fix use-after-free when removing a failing zlib.inflate filter (#24176) Corrupt input makes zlib.inflate raise an E_NOTICE. An error handler can remove the filter and free its state, so resetting the input pointers after the notice writes to freed memory. Release the input bucket and reset the filter state before raising the notice, then return without accessing the filter again. Add regression coverage for error handlers that remove both write and read filters. Closing the stream itself from an error handler is outside this fix and requires protection around the filter chain walk. Closes #24176 Changed paths: A ext/zlib/tests/zlib_filter_inflate_error_handler_removes_filter.phpt M NEWS M ext/zlib/zlib_filter.c Diff: diff --git a/NEWS b/NEWS index 5c01150cabe4..05dc5a7c733d 100644 --- a/NEWS +++ b/NEWS @@ -49,6 +49,10 @@ PHP NEWS is called from a progress or cancel callback during close(). (Ilia Alshanetsky) +- Zlib: + . Fixed use-after-free when an error handler removes a failing zlib.inflate + filter (GH-24176). (Edmond) + 22 Oct 2026, PHP 8.4.27 - BCMath: diff --git a/ext/zlib/tests/zlib_filter_inflate_error_handler_removes_filter.phpt b/ext/zlib/tests/zlib_filter_inflate_error_handler_removes_filter.phpt new file mode 100644 index 000000000000..ee5bab1052fc --- /dev/null +++ b/ext/zlib/tests/zlib_filter_inflate_error_handler_removes_filter.phpt @@ -0,0 +1,30 @@ +--TEST-- +zlib.inflate filter removed by the error handler of its own notice +--EXTENSIONS-- +zlib +--FILE-- +<?php +$fp = fopen('php://memory', 'w+'); +$filter = stream_filter_append($fp, 'zlib.inflate', STREAM_FILTER_WRITE); +set_error_handler(function (int $errno, string $errstr) use (&$filter) { + echo $errstr, "\n"; + var_dump(stream_filter_remove($filter)); + return true; +}); +var_dump(fwrite($fp, "\xff\xff\xff\xff")); + +$fp = fopen('php://memory', 'w+'); +fwrite($fp, "\xff\xff\xff\xff"); +rewind($fp); +$filter = stream_filter_append($fp, 'zlib.inflate', STREAM_FILTER_READ); +var_dump(fread($fp, 10)); +echo "Done\n"; +?> +--EXPECT-- +fwrite(): zlib: data error +bool(true) +bool(false) +fread(): zlib: data error +bool(true) +bool(false) +Done diff --git a/ext/zlib/zlib_filter.c b/ext/zlib/zlib_filter.c index e5491afec391..85711be8caa1 100644 --- a/ext/zlib/zlib_filter.c +++ b/ext/zlib/zlib_filter.c @@ -90,11 +90,12 @@ static php_stream_filter_status_t php_zlib_inflate_filter( exit_status = PSFS_PASS_ON; } else if (status != Z_OK && status != Z_BUF_ERROR) { /* Something bad happened */ - php_error_docref(NULL, E_NOTICE, "zlib: %s", zError(status)); php_stream_bucket_delref(bucket); /* reset these because despite the error the filter may be used again */ data->strm.next_in = data->inbuf; data->strm.avail_in = 0; + /* Last: an error handler may remove this filter and free data. */ + php_error_docref(NULL, E_NOTICE, "zlib: %s", zError(status)); return PSFS_ERR_FATAL; } desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */

« previous php.cvs (#142131) next »