-
Notifications
You must be signed in to change notification settings - Fork 7.7k
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
Fix volatile INDICECT with .= #15961
base: master
Are you sure you want to change the base?
Conversation
class C {} | ||
|
||
$obj = new C(); | ||
// $obj->a = str_repeat('a', 10); |
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.
Note: This line isn't even necessary to trigger the leak. $this->a .=
prepares the index in the property table and stores the pointer in the VAR
passed to ASSIGN_OP
. unset($obj->a)
will then remove the key and NULL value that were just created. The actual store will then occur in a dead zval.
Neither STATIC_PROP nor VAR are susceptible. Static vars cannot be unset. Unsetting vars only sets them to IS_UNDEF, including dynamic properties (`zend_hash_del_ind()` in the `ZEND_UNSET_VAR` handler). Fixes phpGH-15938
Hmm, I just realized that this changes behavior due to |
This would have been a nice solution. The optimizer is able to optimize-out the cast sometimes, and I see no perf regression on the symfony-demo benchmark. An other case it doesn't handle is this: <?php
class C {
public string $a;
}
$obj = new C();
$v = new class {
function __toString() {
global $obj;
$obj = null;
return 'str';
}
};
$obj->a = &$v; |
This is a neat idea. I'm not sure though if we should be doing this, we're playing increasingly complex games to fix these kinds of issues, a generic "zval locking" mechanism (for the lack of a better term) that would cover as much cases as possible would be nice ig. EDIT: To be clear, I like this idea and perhaps we should commit this; but I'm starting to doubt adding all these tiny things all over the place for specific cases is the way to go. One one hand we should fix as many as possible of these, on the other hand it's all subtle stuff and complexity increases. |
@nielsdos Yeah I agree it would be nice to have a global solution. Some spitballing: Essentially,
Number 1. is relatively easy to fix. We can lock the current instructions container in As for what happens when modifying the locked object, we have the two aforementioned approaches:
I haven't done any testing, but it seems like this should work. It's late, so it might also not make sense. 😄 Edit: And what I forgot. For the undefined offset warnings, we would need to terminate when unlocking if the container goes away. |
It also just occurred to me that references are potentially also subject to this problem. And of course they are. :( class C {
public mixed $prop1;
public ?string $prop2;
public function __toString() {
unset($this->prop1);
unset($this->prop2);
return 'bar';
}
}
function test() {
$c = new C();
$c->prop1 = 'foo';
$c->prop1 = &$c->prop2;
$c->prop1 = $c;
var_dump($c);
}
test(); That said, this can probably work the same way. Increase the refcount before the type coercion, and release after the assignment. |
This is similar in some ways to the solutions I briefly suggested in #15938. The second approach seems simpler indeed, at least for arrays and maybe references. For objects, since implementations of
|
Neither STATIC_PROP nor VAR are susceptible. Static vars cannot be unset.
Unsetting vars only sets them to IS_UNDEF, including dynamic properties
(
zend_hash_del_ind()
in theZEND_UNSET_VAR
handler).Fixes GH-15938