diff --git a/NEWS b/NEWS index 5dadee43b6ce..24cbe0a83a5a 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ PHP NEWS - PDO_PGSQL: . Fixed crash when a persistent connection fails. (KentarouTakeda) +- Zip: + . Fixed bug GH-23899 (Assertion failure when a cancel callback returns an + invalid type during shutdown). (Weilin Du) + 24 Sep 2026, PHP 8.6.0RC2 - Core: diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 88fdcaa9b03e..a095f4fcca32 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -3241,15 +3241,21 @@ static int php_zip_cancel_callback(zip_t *arch, void *ptr) /* Cancel if an exception has been thrown */ return -1; } - bool failed; - zend_long retval = zval_try_get_long(&cb_retval, &failed); - if (failed) { - zend_type_error("Return value of callback provided to ZipArchive::registerCancelCallback()" - " must be of type int, %s returned", zend_zval_value_name(&cb_retval)); + zend_long retval; + /* Conversion and reporting an invalid return type can both bail out during shutdown. */ + zend_try { + bool failed; + retval = zval_try_get_long(&cb_retval, &failed); + if (failed) { + retval = -1; + zend_type_error("Return value of callback provided to ZipArchive::registerCancelCallback()" + " must be of type int, %s returned", zend_zval_value_name(&cb_retval)); + } zval_ptr_dtor(&cb_retval); + } zend_catch { + archive->bailout_callback = true; return -1; - } - zval_ptr_dtor(&cb_retval); + } zend_end_try(); return (int) retval; } diff --git a/ext/zip/tests/gh23899.phpt b/ext/zip/tests/gh23899.phpt new file mode 100644 index 000000000000..233a5a2666f5 --- /dev/null +++ b/ext/zip/tests/gh23899.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-23899 (Invalid cancel callback return type during shutdown causes an assertion failure) +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +open(__DIR__ . '/gh23899.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE); +$zip->registerCancelCallback(function () { + return [new stdClass]; +}); +$zip->addFromString('test', 'test'); +echo "Done\n"; +?> +--CLEAN-- + +--EXPECTF-- +Done + +Fatal error: Uncaught TypeError: Return value of callback provided to ZipArchive::registerCancelCallback() must be of type int, array returned in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/ext/zip/tests/gh23899_conversion.phpt b/ext/zip/tests/gh23899_conversion.phpt new file mode 100644 index 000000000000..1e86ce5be82d --- /dev/null +++ b/ext/zip/tests/gh23899_conversion.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-23899 (Bailout during cancel callback return value conversion at shutdown) +--EXTENSIONS-- +zip +--SKIPIF-- + +--INI-- +zend.exception_ignore_args=1 +--FILE-- +open(__DIR__ . '/gh23899_conversion.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE); +$zip->registerCancelCallback(function () { + return '123abc'; +}); +$zip->addFromString('test', 'test'); +echo "Done\n"; +?> +--CLEAN-- + +--EXPECTF-- +Done + +Fatal error: Uncaught Exception: A non-numeric value encountered in %s:%d +Stack trace: +#0 [internal function]: {closure:%s:%d}() +#1 {main} + thrown in %s on line %d diff --git a/ext/zip/tests/gh23899_destructor.phpt b/ext/zip/tests/gh23899_destructor.phpt new file mode 100644 index 000000000000..fad10717e592 --- /dev/null +++ b/ext/zip/tests/gh23899_destructor.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-23899 (Cancel callback return value whose destructor throws during shutdown) +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +open(__DIR__ . '/gh23899_destructor.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE); +$zip->registerCancelCallback(function () { + return new ThrowingDestructor; +}); +$zip->addFromString('test', 'test'); +echo "Done\n"; +?> +--CLEAN-- + +--EXPECT-- +Done