Skip to content

Commit e2da92b

Browse files
committed
Fix ZipArchive callback being called after executor has shut down
free_obj() for objects referenced in the main symbol table may be called only once the executor has already shut down. php_zip_cancel_callback() may attempt to invoke a user callback, which will terminate the process because user code is not expected to be executed at this point. We solve this by calling the callback in dtor_obj(), which is called earlier in the shutdown sequence. dtor_obj() will actually attempt to call it again if the object was reinitialized in the destructor, so we also avoid calling the callback when the executor has shut down in the first place. This should never matter in practice. Closes GH-19602
1 parent f9a782d commit e2da92b

File tree

4 files changed

+84
-0
lines changed

4 files changed

+84
-0
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,8 @@ PHP NEWS
2929
. Fixed bug GH-19926 (reset internal pointer earlier while splicing array
3030
while COW violation flag is still set). (alexandre-daubois)
3131

32+
- Zip:
33+
. Fixed ZipArchive callback being called after executor has shut down.
34+
(ilutov)
35+
3236
<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>

ext/zip/php_zip.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,21 @@ static void php_zip_cancel_callback_free(void *ptr)
10021002
}
10031003
#endif
10041004

1005+
static void php_zip_object_dtor(zend_object *object)
1006+
{
1007+
zend_objects_destroy_object(object);
1008+
1009+
ze_zip_object *intern = php_zip_fetch_object(object);
1010+
1011+
if (intern->za) {
1012+
if (zip_close(intern->za) != 0) {
1013+
php_error_docref(NULL, E_WARNING, "Cannot destroy the zip context: %s", zip_strerror(intern->za));
1014+
zip_discard(intern->za);
1015+
}
1016+
intern->za = NULL;
1017+
}
1018+
}
1019+
10051020
static void php_zip_object_free_storage(zend_object *object) /* {{{ */
10061021
{
10071022
ze_zip_object * intern = php_zip_fetch_object(object);
@@ -2995,6 +3010,10 @@ PHP_METHOD(ZipArchive, getStream)
29953010
#ifdef HAVE_PROGRESS_CALLBACK
29963011
static void php_zip_progress_callback(zip_t *arch, double state, void *ptr)
29973012
{
3013+
if (!EG(active)) {
3014+
return;
3015+
}
3016+
29983017
zval cb_args[1];
29993018
ze_zip_object *obj = ptr;
30003019

@@ -3041,6 +3060,10 @@ static int php_zip_cancel_callback(zip_t *arch, void *ptr)
30413060
zval cb_retval;
30423061
ze_zip_object *obj = ptr;
30433062

3063+
if (!EG(active)) {
3064+
return 0;
3065+
}
3066+
30443067
zend_call_known_fcc(&obj->cancel_callback, &cb_retval, 0, NULL, NULL);
30453068
if (Z_ISUNDEF(cb_retval)) {
30463069
/* Cancel if an exception has been thrown */
@@ -3128,6 +3151,7 @@ static PHP_MINIT_FUNCTION(zip)
31283151
memcpy(&zip_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
31293152
zip_object_handlers.offset = XtOffsetOf(ze_zip_object, zo);
31303153
zip_object_handlers.free_obj = php_zip_object_free_storage;
3154+
zip_object_handlers.dtor_obj = php_zip_object_dtor;
31313155
zip_object_handlers.clone_obj = NULL;
31323156
zip_object_handlers.get_property_ptr_ptr = php_zip_get_property_ptr_ptr;
31333157

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
ZipArchive destructor should be called on bailout
3+
--EXTENSIONS--
4+
zip
5+
--FILE--
6+
<?php
7+
8+
function &cb() {}
9+
10+
$file = __DIR__ . '/gh18907.zip';
11+
$zip = new ZipArchive;
12+
$zip->open($file, ZIPARCHIVE::CREATE);
13+
$zip->registerCancelCallback(cb(...));
14+
$zip->addFromString('test', 'test');
15+
$fusion = $zip;
16+
17+
?>
18+
--CLEAN--
19+
<?php
20+
$file = __DIR__ . '/gh18907.zip';
21+
@unlink($file);
22+
?>
23+
--EXPECTF--
24+
Notice: Only variable references should be returned by reference in %s on line %d
25+
26+
Notice: Only variable references should be returned by reference in %s on line %d
27+
28+
Notice: Only variable references should be returned by reference in %s on line %d
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Leaking ZipArchive destructor
3+
--EXTENSIONS--
4+
zip
5+
--FILE--
6+
<?php
7+
8+
class MyZipArchive extends ZipArchive {
9+
public function __destruct() {
10+
global $leak;
11+
$leak = $this;
12+
}
13+
}
14+
15+
new MyZipArchive;
16+
$file = __DIR__ . '/ZipArchive_destruct.zip';
17+
$leak->open($file, ZIPARCHIVE::CREATE);
18+
$leak->addFromString('test', 'test');
19+
20+
?>
21+
===DONE===
22+
--CLEAN--
23+
<?php
24+
$file = __DIR__ . '/ZipArchive_destruct.zip';
25+
@unlink($file);
26+
?>
27+
--EXPECT--
28+
===DONE===

0 commit comments

Comments
 (0)