Skip to content

Commit 0c3cf1f

Browse files
committed
Fix GH-17577: JIT packed type guard crash
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. Closes GH-17584.
1 parent f88445b commit 0c3cf1f

File tree

3 files changed

+43
-6
lines changed

3 files changed

+43
-6
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ PHP NEWS
1414
- Opcache:
1515
. Fixed bug GH-17654 (Multiple classes using same trait causes function
1616
JIT crash). (nielsdos)
17+
. Fixed bug GH-17577 (JIT packed type guard crash). (nielsdos, Dmitry)
1718

1819
- PHPDBG:
1920
. Partially fixed bug GH-17387 (Trivial crash in phpdbg lexer). (nielsdos)

ext/opcache/jit/zend_jit_trace.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,7 +1745,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
17451745
if (!(orig_op1_type & IS_TRACE_PACKED)) {
17461746
zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
17471747

1748-
if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) {
1748+
if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)
1749+
&& (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
17491750
info->type |= MAY_BE_PACKED_GUARD;
17501751
info->type &= ~MAY_BE_ARRAY_PACKED;
17511752
}
@@ -1754,7 +1755,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
17541755
&& val_type != IS_UNDEF) {
17551756
zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
17561757

1757-
if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) {
1758+
if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)
1759+
&& (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
17581760
info->type |= MAY_BE_PACKED_GUARD;
17591761
info->type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
17601762
}
@@ -1833,7 +1835,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
18331835

18341836
zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
18351837

1836-
if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) {
1838+
if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)
1839+
&& (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
18371840
info->type |= MAY_BE_PACKED_GUARD;
18381841
if (orig_op1_type & IS_TRACE_PACKED) {
18391842
info->type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
@@ -1935,7 +1938,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
19351938

19361939
zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
19371940

1938-
if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) {
1941+
if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)
1942+
&& (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
19391943
info->type |= MAY_BE_PACKED_GUARD;
19401944
if (orig_op1_type & IS_TRACE_PACKED) {
19411945
info->type &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
@@ -1965,7 +1969,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
19651969

19661970
zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
19671971

1968-
if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)) {
1972+
if (MAY_BE_PACKED(info->type) && MAY_BE_HASH(info->type)
1973+
&& (info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
19691974
info->type |= MAY_BE_PACKED_GUARD;
19701975
info->type &= ~MAY_BE_ARRAY_PACKED;
19711976
}
@@ -4164,10 +4169,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
41644169
if ((info & MAY_BE_PACKED_GUARD) != 0
41654170
&& (trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
41664171
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_CALL
4167-
|| trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET)
4172+
|| (trace_buffer->stop == ZEND_JIT_TRACE_STOP_RECURSIVE_RET
4173+
&& (opline-1)->result_type == IS_VAR
4174+
&& EX_VAR_TO_NUM((opline-1)->result.var) == i))
41684175
&& (ssa->vars[i].use_chain != -1
41694176
|| (ssa->vars[i].phi_use_chain
41704177
&& !(ssa->var_info[ssa->vars[i].phi_use_chain->ssa_var].type & MAY_BE_PACKED_GUARD)))) {
4178+
ZEND_ASSERT(STACK_TYPE(stack, i) == IS_ARRAY);
4179+
41714180
if (!zend_jit_packed_guard(&dasm_state, opline, EX_NUM_TO_VAR(i), info)) {
41724181
goto jit_failure;
41734182
}

ext/opcache/tests/jit/gh17577.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
GH-17577 (JIT packed type guard crash)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.jit_buffer_size=16M
7+
opcache.jit_hot_func=1
8+
--FILE--
9+
<?php
10+
$a = array(
11+
array(1,2,3),
12+
0,
13+
);
14+
function my_dump($var) {
15+
}
16+
foreach($a as $b) {
17+
for ($i = 0; $i < 3; $i++) {
18+
my_dump($b[$i]);
19+
}
20+
}
21+
?>
22+
--EXPECTF--
23+
Warning: Trying to access array offset on int in %s on line %d
24+
25+
Warning: Trying to access array offset on int in %s on line %d
26+
27+
Warning: Trying to access array offset on int in %s on line %d

0 commit comments

Comments
 (0)