Merge branch 'PHP-8.2'

* PHP-8.2:
  Fix GH-11245 (In some specific cases SWITCH with one default statement will cause segfault)
This commit is contained in:
nielsdos 2023-05-23 00:36:55 +02:00
commit 24ff7eee3f
3 changed files with 77 additions and 1 deletions

View File

@ -264,6 +264,10 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
break; break;
case ZEND_FREE: case ZEND_FREE:
/* Note: Only remove the source if the source is local to this block.
* If it's not local, then the other blocks successors must also eventually either FREE or consume the temporary,
* hence removing the temporary is not safe in the general case, especially when other consumers are not FREE.
* A FREE may not be removed without also removing the source's result, because otherwise that would cause a memory leak. */
if (opline->op1_type == IS_TMP_VAR) { if (opline->op1_type == IS_TMP_VAR) {
src = VAR_SOURCE(opline->op1); src = VAR_SOURCE(opline->op1);
if (src) { if (src) {
@ -272,6 +276,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
case ZEND_BOOL_NOT: case ZEND_BOOL_NOT:
/* T = BOOL(X), FREE(T) => T = BOOL(X) */ /* T = BOOL(X), FREE(T) => T = BOOL(X) */
/* The remaining BOOL is removed by a separate optimization */ /* The remaining BOOL is removed by a separate optimization */
/* The source is a bool, no source removals take place, so this may be done non-locally. */
VAR_SOURCE(opline->op1) = NULL; VAR_SOURCE(opline->op1) = NULL;
MAKE_NOP(opline); MAKE_NOP(opline);
++(*opt_count); ++(*opt_count);
@ -290,6 +295,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
case ZEND_PRE_DEC_OBJ: case ZEND_PRE_DEC_OBJ:
case ZEND_PRE_INC_STATIC_PROP: case ZEND_PRE_INC_STATIC_PROP:
case ZEND_PRE_DEC_STATIC_PROP: case ZEND_PRE_DEC_STATIC_PROP:
if (src < op_array->opcodes + block->start) {
break;
}
src->result_type = IS_UNUSED; src->result_type = IS_UNUSED;
VAR_SOURCE(opline->op1) = NULL; VAR_SOURCE(opline->op1) = NULL;
MAKE_NOP(opline); MAKE_NOP(opline);
@ -302,7 +310,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
} else if (opline->op1_type == IS_VAR) { } else if (opline->op1_type == IS_VAR) {
src = VAR_SOURCE(opline->op1); src = VAR_SOURCE(opline->op1);
/* V = OP, FREE(V) => OP. NOP */ /* V = OP, FREE(V) => OP. NOP */
if (src && if (src >= op_array->opcodes + block->start &&
src->opcode != ZEND_FETCH_R && src->opcode != ZEND_FETCH_R &&
src->opcode != ZEND_FETCH_STATIC_PROP_R && src->opcode != ZEND_FETCH_STATIC_PROP_R &&
src->opcode != ZEND_FETCH_DIM_R && src->opcode != ZEND_FETCH_DIM_R &&

View File

@ -0,0 +1,33 @@
--TEST--
GH-11245: In some specific cases SWITCH with one default statement will cause segfault (VAR variation)
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=0x7FFFBFFF
opcache.opt_debug_level=0x20000
opcache.preload=
--EXTENSIONS--
opcache
--FILE--
<?php
function xx() { return "somegarbage"; }
switch (xx()) {
default:
if (!empty($xx)) {return;}
}
?>
--EXPECTF--
$_main:
; (lines=4, args=0, vars=1, tmps=1)
; (after optimizer)
; %s
0000 T1 = ISSET_ISEMPTY_CV (empty) CV0($xx)
0001 JMPNZ T1 0003
0002 RETURN null
0003 RETURN int(1)
xx:
; (lines=1, args=0, vars=0, tmps=0)
; (after optimizer)
; %s
0000 RETURN string("somegarbage")

View File

@ -0,0 +1,35 @@
--TEST--
GH-11245: In some specific cases SWITCH with one default statement will cause segfault (TMP variation)
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=0x7FFFBFFF
opcache.opt_debug_level=0x20000
opcache.preload=
--EXTENSIONS--
opcache
--FILE--
<?php
class X {
// Chosen to test for a memory leak.
static $prop = "aa";
}
switch (++X::$prop) {
default:
if (empty($xx)) {return;}
}
?>
--EXPECTF--
$_main:
; (lines=7, args=0, vars=1, tmps=2)
; (after optimizer)
; %s
0000 T1 = PRE_INC_STATIC_PROP string("prop") string("X")
0001 T2 = ISSET_ISEMPTY_CV (empty) CV0($xx)
0002 JMPZ T2 0005
0003 FREE T1
0004 RETURN null
0005 FREE T1
0006 RETURN int(1)
LIVE RANGES:
1: 0001 - 0005 (tmp/var)