Close generator already in dtor phase

In the added test case, the Closure ends up being freed before
the generator during GC.

This patch closes the generator (and thus releases the held
closure / execute_data) already during dtor_obj, which will avoid
ordering issues in free_obj. dtor_obj is not always called, but
if it isn't, then we also won't run GC and will free_obj in
reverse construction order.

Fixes oss-fuzz #33947.
This commit is contained in:
Nikita Popov committed 2021-05-04 16:59:59 +02:00
1 parent 2aac616071
commit 4411026217
2 files changed
+21 -1

No files matched your search

+17
View File
@@ -0,0 +1,17 @@
--TEST--
Close generator in dtor to avoid freeing order issues
--FILE--
<?php
$gen = function() {
yield;
throw new Exception; // Just to create a live range
};
$a = new stdclass;
$a->a = $a;
$a->gen = $gen();
?>
===DONE===
--EXPECT--
===DONE===
+4 -1
View File
@@ -226,6 +226,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
if (EXPECTED(!ex) || EXPECTED(!(ex->func->op_array.fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK))
|| CG(unclean_shutdown)) {
zend_generator_close(generator, 0);
return;
}
@@ -265,7 +266,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
/* TODO: If we hit another yield inside try/finally,
* should we also jump to the next finally block? */
return;
break;
} else if (op_num < try_catch->finally_end) {
zval *fast_call =
ZEND_CALL_VAR(ex, ex->func->op_array.opcodes[try_catch->finally_end].op1.var);
@@ -284,6 +285,8 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
try_catch_offset--;
}
zend_generator_close(generator, 0);
}
/* }}} */