-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix some increment/decrement bugs #11759
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
44b1ac3
to
2ba8668
Compare
Zend/zend_execute.c
Outdated
/* Error handler can mess up properties and unset it */ | ||
if (UNEXPECTED(Z_TYPE_P(prop) == IS_UNDEF)) { | ||
ZVAL_NULL(prop); | ||
} |
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.
I think this is wrong. We shouldn't change the property. In worst case this may be a typed property that can't be NULL. There are few similar places in the patch.
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.
@arnaud-lb gave me a solution that I am going to try out which should hopefully fix those in a sensible manner.
2ba8668
to
d542152
Compare
@dstogov I'm having a weird bug with the function JIT with test The output is:
Where it seems neither the exceptions are being thrown, but also one of the operations just disappears, @nielsdos had a quick look, and they could also reproduce it and seem to think there is an existing bug in the JIT. |
Function JIT does exactly the same that VM does. The output of Zend/tests/in-de-crement/incdec_undefined_vars_exception.phpt in php master is the same with and without JIT. So if you fixed a bug in VM, this should be reflected in JIT (may be some of JIT helpers or JIT code generator itself). Also, I can't follow your trick in error handler |
The trick is to be able to |
Using |
Okay, so doing this now actually produces all the operands and keeps the value undef for the Seems using a static variable in an error handler confuses the JIT, but that's an unrelated issue. |
d542152
to
c72f194
Compare
PHP master branch.
|
c72f194
to
f1f77e3
Compare
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.
I think we would semantically be more consistent to assign to assign to result
before emitting the warning, similar to what we do for destructors. This way, nothing can change the result mid-operation. Unfortunately, passing the result pointer to the increment/decrement function could mean changing quite a few handlers in the VM. I'm not sure if that is acceptable.
Another option would be to fetch the result pointer through current_execute_data
similar to this code here:
php-src/Zend/zend_object_handlers.c
Lines 852 to 860 in 5da08df
if (execute_data | |
&& EX(func) | |
&& ZEND_USER_CODE(EX(func)->common.type) | |
&& EX(opline) | |
&& EX(opline)->opcode == ZEND_ASSIGN_OBJ | |
&& EX(opline)->result_type) { | |
ZVAL_COPY_DEREF(EX_VAR(EX(opline)->result.var), variable_ptr); | |
variable_ptr = NULL; | |
} |
Given that this warning is temporary and shouldn't add an additional overhead to the happy path, that might be an acceptable option.
It turns out not just NULL is affected nor -- but also on booleans and this also affects properties
f1f77e3
to
20d6c44
Compare
/* Smart branch expects result to be set with exceptions */ | ||
if (UNEXPECTED(RETURN_VALUE_USED(opline))) { | ||
ZVAL_NULL(EX_VAR(opline->result.var)); | ||
} | ||
HANDLE_EXCEPTION(); |
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.
@Girgias This HANDLE_EXCEPTION()
was introduced by your previous patch. Note that it returns from the handler without execution FREE_OP1_VAR_PTR()
. This may lead to memory leak.
CC @iluuu1994
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.
This is the PoC. It doesn't leak in PHP-8.2.
<?php
set_error_handler(function($severity, $m) {
if (strpos($m, 'Indirect modification of overloaded element') === 0) return;
throw new Exception($m, $severity);
});
class Foo implements ArrayAccess {
function offsetGet($index): mixed {
return range(1, 5);
}
function offsetSet($index, $newval): void {
}
function offsetExists($index): bool {
return true;
}
function offsetUnset($index): void {
}
}
$foo = new Foo;
try {
$foo[0]++;
} catch (Throwable $ex) {
echo $ex->getMessage() . "\n";
}
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.
Right >_> thanks for catching this.
So the first commit probably should be backported as that's a pre-existing issue. (Also this issue exists for nearly every other case where we haveZVAL_UNDEFINED_OP1()
/ZVAL_UNDEFINED_OP2()
without a check forEG(exception)
).Spun-out into #11850
Thanks to @arnaud-lb for helping me pinpoint the issues as they were kinda tricky.