-
Notifications
You must be signed in to change notification settings - Fork 7.8k
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 GH-17577: JIT packed type guard crash #17584
Conversation
ab89b9b
to
b477345
Compare
When a guard check is created for a variable to check if it's a packed array, it is possible that there was no prior type check for that variable. This happens in the global scope for example when the variable aliases. In the test, this causes a dereference of address 8 because the integer element in `$a` is interpreted as an array address. This patch adds a check to see if the guard is handled. If we were not able to determine or guard the type then we also cannot know the array is packed.
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.
The patch fixes the problem, but I think it might be better not to add MAY_BE_PACKED_GUARD
in the first place. This may be done by checking op1_info & (MAY_BE_ANY|MAY_BE_UNDEF) == MY_BE_ARRAY
.
What do you think? may be I miss something.
The particular code responsible for setting diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index c138a5bcb0a..8fd0d1323d0 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -1935,7 +1935,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
- if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) {
+ if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)
+ && (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
info->type |= MAY_BE_PACKED_GUARD;
if (orig_op1_type & IS_TRACE_PACKED) {
info->type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
@@ -4162,7 +4163,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
}
if ((info & MAY_BE_PACKED_GUARD) != 0
- && (info & MAY_BE_GUARD) == 0
&& (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET)
If I do this, and keep the assertion for the stack type to be |
Actually, the check for PACKED_GUARD is useless in if ((info & MAY_BE_PACKED_GUARD) != 0
&& (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
- || trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET)
+ || (trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET)
+ && EX_VAR_TO_NUM((opline-1)->result.var) == i))
&& (ssa->vars[i].use_chain != -1
|| (ssa->vars[i].phi_use_chain
&& !(ssa->var_info[ssa->vars[i].phi_use_chain->ssa_var].type & MAY_BE_PACKED_GUARD)))) { |
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.
Looks fine, except the IS_VAR
condition.
I would prefer to keep the conditions for type guard and packed guard the same.
@@ -4164,10 +4169,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par | |||
if ((info & MAY_BE_PACKED_GUARD) != 0 | |||
&& (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP | |||
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL | |||
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET) | |||
|| (trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET | |||
&& (opline-1)->result_type == IS_VAR |
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.
It's better to keep the conditions exactly the same. We don't have IS_VAR
check above (when we generate type guard). In general, Optimizer may fuse DO_FCALL with the following ASSIGN to IS_CV (I'm not sure if this is enabled or not).
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.
Maybe I miss something, but I got this from here:
php-src/ext/opcache/jit/zend_jit_trace.c
Lines 4148 to 4150 in a99025c
|| (trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET | |
&& (opline-1)->result_type == IS_VAR | |
&& EX_VAR_TO_NUM((opline-1)->result.var) == i)) |
So I'm not sure I understand why the check for IS_VAR is fine there for the type guard, but not for the packed type guard.
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 see. I looked into the master branch code.
So keep IS_VAR
in PHP-8.3 and remove it in PHP-8.4 and master.
When a guard check is created for a variable to check if it's a packed array, it is possible that there was no prior type check for that variable. This happens in the global scope for example when the variable aliases. In the test, this causes a dereference of address 8 because the integer element in
$a
is interpreted as an array address and zend_array.u.flags is at offset 8.This patch adds a check to see if the guard is handled.
If we were not able to determine or guard the type is array, then we also cannot know the array is packed.
An alternative I thought about was emitting a type check in this branch, but that can cause incorrect guard motion in case of aliasing. In such case we also shouldn't clear the
MAY_BE_PACKED_GUARD
like it does now.