[php-src] PHP-8.5: Merge branch 'PHP-8.4' into PHP-8.5

From: Date: Tue, 06 Oct 2026 14:35:36 +0000
Subject: [php-src] PHP-8.5: Merge branch 'PHP-8.4' into PHP-8.5
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-142007@lists.php.net to get a copy of this message
Author: Jakub Zelenka (bukka) Date: 2026-10-06T16:35:26+02:00 Commit: https://github.com/php/php-src/commit/f0d48c8991acbfe02d5eae6146895f7370929a12 Raw diff: https://github.com/php/php-src/commit/f0d48c8991acbfe02d5eae6146895f7370929a12.diff Merge branch 'PHP-8.4' into PHP-8.5 * PHP-8.4: Fix bzopen() ownership of the stream it wraps Changed paths: A ext/bz2/tests/bzopen_resource_fclose.phpt M NEWS M ext/bz2/bz2.c Diff: diff --git a/NEWS b/NEWS index a42642d601e8..0f6bb34b368c 100644 --- a/NEWS +++ b/NEWS @@ -6,7 +6,21 @@ PHP NEWS . Fixed BcMath\Number results that truncate to zero keeping a negative sign and comparing less than zero. (Ilia Alshanetsky) -- Core: +- BZ2: + . Fixed double close of the descriptor and use-after-free of the inner + stream when bzopen() is given a stream resource. (Jakub Zelenka) + +- CLI + . Fix GH-22567 (Windows ZTS CLI SAPI should refresh its TSRMLS cache during + request activation). (matyhtf) + . Fixed bug GH-23764 (Built-in server leaks a file descriptor on every HEAD + request for a static file). (jakubskopal) + . Fixed crash in the built-in server when a client is reset before being + accepted. (David Carlier) + . Fixed the built-in server truncating an error page and leaking its + unsent part after a partial socket write. (Ilia Alshanetsky) + +- Core . Fixed bug GH-23644 (Optimizer leaves a constant-vs-constant comparison unfolded, crashing the VM in zval_undefined_cv). (ndossche) . Fixed OSS-Fuzz 532353396 (assertion failure with static type). (Girgias) @@ -25,14 +39,6 @@ PHP NEWS . Fixed exception thrown by destructor during GC in a Fiber not being rethrown into the frame that triggered the GC. (Nicolas Grekas) -- CLI - . Fix GH-22567 (Windows ZTS CLI SAPI should refresh its TSRMLS cache during - request activation). (matyhtf) - . Fixed bug GH-23764 (Built-in server leaks a file descriptor on every HEAD - request for a static file). (jakubskopal) - . Fixed the built-in server truncating an error page and leaking its - unsent part after a partial socket write. (Ilia Alshanetsky) - - DOM: . Fixed use-after-free when re-constructing a DOMXPath whose php:function registrations are freed while still reachable from the cycle collector. diff --git a/ext/bz2/bz2.c b/ext/bz2/bz2.c index a7af4b1a03a8..319518f2ace8 100644 --- a/ext/bz2/bz2.c +++ b/ext/bz2/bz2.c @@ -59,7 +59,7 @@ ZEND_GET_MODULE(bz2) struct php_bz2_stream_data_t { BZFILE *bz_file; - php_stream *stream; + zend_resource *stream_res; }; /* {{{ BZip2 stream implementation */ @@ -131,8 +131,15 @@ static int php_bz2iop_close(php_stream *stream, int close_handle) BZ2_bzclose(self->bz_file); } - if (self->stream) { - php_stream_free(self->stream, PHP_STREAM_FREE_CLOSE | (close_handle == 0 ? PHP_STREAM_FREE_PRESERVE_HANDLE : 0)); + /* The inner stream may have been closed by the user, only its resource is held */ + if (self->stream_res) { + php_stream *inner = zend_fetch_resource2( + self->stream_res, NULL, php_file_le_stream(), php_file_le_pstream()); + if (inner) { + php_stream_free(inner, PHP_STREAM_FREE_CLOSE | (close_handle == 0 ? PHP_STREAM_FREE_PRESERVE_HANDLE : 0)); + } else { + zend_list_delete(self->stream_res); + } } efree(self); @@ -158,6 +165,23 @@ const php_stream_ops php_stream_bz2io_ops = { }; /* {{{ Bzip2 stream openers */ + +/* bzlib closes the descriptor it is given, so it gets a duplicate */ +static BZFILE *php_bz2_bzdopen(php_socket_t fd, const char *mode) +{ + int dup_fd = dup((int) fd); + if (dup_fd == -1) { + return NULL; + } + + BZFILE *bz = BZ2_bzdopen(dup_fd, mode); + if (!bz) { + close(dup_fd); + } + + return bz; +} + PHP_BZ2_API php_stream *_php_stream_bz2open_from_BZFILE(BZFILE *bz, const char *mode, php_stream *innerstream STREAMS_DC) { @@ -165,8 +189,9 @@ PHP_BZ2_API php_stream *_php_stream_bz2open_from_BZFILE(BZFILE *bz, self = emalloc(sizeof(*self)); - self->stream = innerstream; + self->stream_res = NULL; if (innerstream) { + self->stream_res = innerstream->res; GC_ADDREF(innerstream->res); } self->bz_file = bz; @@ -223,7 +248,7 @@ PHP_BZ2_API php_stream *_php_stream_bz2open(php_stream_wrapper *wrapper, if (stream) { php_socket_t fd; if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS)) { - bz_file = BZ2_bzdopen((int)fd, mode); + bz_file = php_bz2_bzdopen(fd, mode); } } @@ -409,7 +434,10 @@ PHP_FUNCTION(bzopen) RETURN_FALSE; } - bz = BZ2_bzdopen((int)fd, mode); + bz = php_bz2_bzdopen(fd, mode); + if (!bz) { + RETURN_FALSE; + } stream = php_stream_bz2open_from_BZFILE(bz, mode, stream); } else { diff --git a/ext/bz2/tests/bzopen_resource_fclose.phpt b/ext/bz2/tests/bzopen_resource_fclose.phpt new file mode 100644 index 000000000000..d194ba406979 --- /dev/null +++ b/ext/bz2/tests/bzopen_resource_fclose.phpt @@ -0,0 +1,41 @@ +--TEST-- +bzopen() on a stream resource: the inner stream may be closed first +--EXTENSIONS-- +bz2 +--FILE-- +<?php +$file = tempnam(sys_get_temp_dir(), 'bz2'); +$victim = tempnam(sys_get_temp_dir(), 'bz2'); +$payload = str_repeat("payload", 100); + +$fp = fopen($file, 'w'); +$bz = bzopen($fp, 'w'); +var_dump(bzwrite($bz, $payload)); + +// The bz2 stream owns its own descriptor, so closing the inner stream neither +// invalidates it nor makes it write into the next descriptor opened +var_dump(fclose($fp)); +$other = fopen($victim, 'w'); +bzclose($bz); +var_dump(fclose($other)); + +var_dump(bzdecompress(file_get_contents($file)) === $payload); +var_dump(filesize($victim)); + +$fp = fopen($file, 'r'); +$bz = bzopen($fp, 'r'); +var_dump(fclose($fp)); +var_dump(bzread($bz, 8192) === $payload); +bzclose($bz); + +unlink($file); +unlink($victim); +?> +--EXPECT-- +int(700) +bool(true) +bool(true) +bool(true) +int(0) +bool(true) +bool(true)

« previous php.cvs (#142007) next »