mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Prevent throwing in running generator
Generator::throw() on a running generator is not allowed. It throws "Cannot resume an already running generator" when trying to resume the generator to handle the provided exception. However, when calling Generator::throw() on a generator with a non-Generator delegate, we release the delegate regardless. If a Fiber was suspended in the delegate, this causes use after frees when the Fiber is resumed. Fix this by throwing "Cannot resume an already running generator" earlier. Fixes GH-19326 Closes GH-19327
This commit is contained in:
2
NEWS
2
NEWS
@@ -15,6 +15,8 @@ PHP NEWS
|
|||||||
causes assertion failure). (nielsdos)
|
causes assertion failure). (nielsdos)
|
||||||
. Fixed bug GH-19306 (Generator can be resumed while fetching next value from
|
. Fixed bug GH-19306 (Generator can be resumed while fetching next value from
|
||||||
delegated Generator). (Arnaud)
|
delegated Generator). (Arnaud)
|
||||||
|
. Fixed bug GH-19326 (Calling Generator::throw() on a running generator with
|
||||||
|
a non-Generator delegate crashes). (Arnaud)
|
||||||
|
|
||||||
- FTP:
|
- FTP:
|
||||||
. Fix theoretical issues with hrtime() not being available. (nielsdos)
|
. Fix theoretical issues with hrtime() not being available. (nielsdos)
|
||||||
|
|||||||
36
Zend/tests/gh19326.phpt
Normal file
36
Zend/tests/gh19326.phpt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-19326: Calling Generator::throw() on a running generator with a non-Generator delegate crashes
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class It implements IteratorAggregate {
|
||||||
|
public function getIterator(): Generator {
|
||||||
|
yield "";
|
||||||
|
Fiber::suspend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() {
|
||||||
|
yield from new It();
|
||||||
|
}
|
||||||
|
|
||||||
|
$b = g();
|
||||||
|
$b->rewind();
|
||||||
|
|
||||||
|
$fiber = new Fiber(function () use ($b) {
|
||||||
|
$b->next();
|
||||||
|
});
|
||||||
|
|
||||||
|
$fiber->start();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$b->throw(new Exception('test'));
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$fiber->resume();
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot resume an already running generator
|
||||||
@@ -492,8 +492,14 @@ ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zend_generator_throw_exception(zend_generator *generator, zval *exception)
|
static zend_result zend_generator_throw_exception(zend_generator *generator, zval *exception)
|
||||||
{
|
{
|
||||||
|
if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
|
||||||
|
zval_ptr_dtor(exception);
|
||||||
|
zend_throw_error(NULL, "Cannot resume an already running generator");
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
zend_execute_data *original_execute_data = EG(current_execute_data);
|
zend_execute_data *original_execute_data = EG(current_execute_data);
|
||||||
|
|
||||||
/* Throw the exception in the context of the generator. Decrementing the opline
|
/* Throw the exception in the context of the generator. Decrementing the opline
|
||||||
@@ -519,6 +525,8 @@ static void zend_generator_throw_exception(zend_generator *generator, zval *exce
|
|||||||
}
|
}
|
||||||
|
|
||||||
EG(current_execute_data) = original_execute_data;
|
EG(current_execute_data) = original_execute_data;
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zend_generator_add_child(zend_generator *generator, zend_generator *child)
|
static void zend_generator_add_child(zend_generator *generator, zend_generator *child)
|
||||||
@@ -1026,7 +1034,9 @@ ZEND_METHOD(Generator, throw)
|
|||||||
if (generator->execute_data) {
|
if (generator->execute_data) {
|
||||||
zend_generator *root = zend_generator_get_current(generator);
|
zend_generator *root = zend_generator_get_current(generator);
|
||||||
|
|
||||||
zend_generator_throw_exception(root, exception);
|
if (zend_generator_throw_exception(root, exception) == FAILURE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
zend_generator_resume(generator);
|
zend_generator_resume(generator);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user