Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
20 changes: 13 additions & 7 deletions ext/zip/php_zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()"
Comment thread
LamentXU123 marked this conversation as resolved.
" 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;
}
Expand Down
31 changes: 31 additions & 0 deletions ext/zip/tests/gh23899.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
GH-23899 (Invalid cancel callback return type during shutdown causes an assertion failure)
--EXTENSIONS--
zip
--SKIPIF--
<?php
if (!method_exists(ZipArchive::class, 'registerCancelCallback')) {
die('skip cancel callbacks are not supported');
}
?>
--FILE--
<?php
$zip = new ZipArchive;
$zip->open(__DIR__ . '/gh23899.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
$zip->registerCancelCallback(function () {
return [new stdClass];
});
$zip->addFromString('test', 'test');
echo "Done\n";
?>
--CLEAN--
<?php
@unlink(__DIR__ . '/gh23899.zip');
?>
--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
38 changes: 38 additions & 0 deletions ext/zip/tests/gh23899_conversion.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
GH-23899 (Bailout during cancel callback return value conversion at shutdown)
--EXTENSIONS--
zip
--SKIPIF--
<?php
if (!method_exists(ZipArchive::class, 'registerCancelCallback')) {
die('skip cancel callbacks are not supported');
}
?>
--INI--
zend.exception_ignore_args=1
--FILE--
<?php
set_error_handler(function ($errno, $message) {
throw new Exception($message);
});

$zip = new ZipArchive;
$zip->open(__DIR__ . '/gh23899_conversion.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
$zip->registerCancelCallback(function () {
return '123abc';
});
$zip->addFromString('test', 'test');
echo "Done\n";
?>
--CLEAN--
<?php
@unlink(__DIR__ . '/gh23899_conversion.zip');
?>
--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
32 changes: 32 additions & 0 deletions ext/zip/tests/gh23899_destructor.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--TEST--
GH-23899 (Cancel callback return value whose destructor throws during shutdown)
--EXTENSIONS--
zip
--SKIPIF--
<?php
if (!method_exists(ZipArchive::class, 'registerCancelCallback')) {
die('skip cancel callbacks are not supported');
}
?>
--FILE--
<?php
class ThrowingDestructor {
public function __destruct() {
throw new Exception('destructor');
}
}

$zip = new ZipArchive;
$zip->open(__DIR__ . '/gh23899_destructor.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
$zip->registerCancelCallback(function () {
return new ThrowingDestructor;
});
$zip->addFromString('test', 'test');
echo "Done\n";
?>
--CLEAN--
<?php
@unlink(__DIR__ . '/gh23899_destructor.zip');
?>
--EXPECT--
Done
Loading