-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Prevent resumption of generator suspended in yield from #19315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Our PRs clashed. #19313 But mine fails, so I trust your solution is better. 😄 |
Oops :) I did assign the bug to myself before starting working on the fix, but I know it's easy to miss as I did a few times in the past. |
I found a way to make this still crash: 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";
} |
This PR seems right to me. @iluuu1994 Your crash is unrelated, e.g. |
@bwoebi Don't think it's unrelated. It results in the same uaf. It happens for the same reason ( Details
|
I'm saying that the problem here is that throw() is being permitted to be called regardless of ZEND_GENERATOR_CURRENTLY_RUNNING (as long as they're not actually resumed), which is a much broader bug, unlike send() and next() which fail early. Specifically: zend_generator_throw_exception needs to check for it. |
Ah ok. Yes, preventing |
I will send a separate PR for the throw issue then. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, then let's merge this one.
Normally we prevent generators from being resumed while they are already running, but we failed to do so for generators delegating to non-Generators. As a result such generator can be resumed, terminated, which causes unexpected results (crashes) later.
In GH-19306 in particular, the generator delegate
It::getIterator()
suspends while being called by generatorg()
. We then resumeg()
, which throws while trying to resumeIt::getIterator()
. This causesg()
andIt::getIterator()
to be released. We then crash while resuming the Fiber inIt::getIterator()
.Fix this by ensuring that generators are marked as running while they fetch the next value from the delegate.
Fixes GH-19306