Skip to content

Commit f656344

Browse files
committed
Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1: Fix GH-11245 (In some specific cases SWITCH with one default statement will cause segfault)
2 parents ffae350 + 5cad1a7 commit f656344

File tree

4 files changed

+79
-1
lines changed

4 files changed

+79
-1
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ PHP NEWS
4040
. Fixed bug GH-11134 (Incorrect match default branch optimization). (ilutov)
4141
. Fixed too wide OR and AND range inference. (nielsdos)
4242
. Fixed missing class redeclaration error with OPcache enabled. (ilutov)
43+
. Fixed bug GH-11245 (In some specific cases SWITCH with one default
44+
statement will cause segfault). (nielsdos)
4345

4446
- PCNTL:
4547
. Fixed maximum argument count of pcntl_forkx(). (nielsdos)

Zend/Optimizer/block_pass.c

+9-1
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
264264
break;
265265

266266
case ZEND_FREE:
267+
/* Note: Only remove the source if the source is local to this block.
268+
* If it's not local, then the other blocks successors must also eventually either FREE or consume the temporary,
269+
* hence removing the temporary is not safe in the general case, especially when other consumers are not FREE.
270+
* A FREE may not be removed without also removing the source's result, because otherwise that would cause a memory leak. */
267271
if (opline->op1_type == IS_TMP_VAR) {
268272
src = VAR_SOURCE(opline->op1);
269273
if (src) {
@@ -272,6 +276,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
272276
case ZEND_BOOL_NOT:
273277
/* T = BOOL(X), FREE(T) => T = BOOL(X) */
274278
/* The remaining BOOL is removed by a separate optimization */
279+
/* The source is a bool, no source removals take place, so this may be done non-locally. */
275280
VAR_SOURCE(opline->op1) = NULL;
276281
MAKE_NOP(opline);
277282
++(*opt_count);
@@ -290,6 +295,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
290295
case ZEND_PRE_DEC_OBJ:
291296
case ZEND_PRE_INC_STATIC_PROP:
292297
case ZEND_PRE_DEC_STATIC_PROP:
298+
if (src < op_array->opcodes + block->start) {
299+
break;
300+
}
293301
src->result_type = IS_UNUSED;
294302
VAR_SOURCE(opline->op1) = NULL;
295303
MAKE_NOP(opline);
@@ -302,7 +310,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
302310
} else if (opline->op1_type == IS_VAR) {
303311
src = VAR_SOURCE(opline->op1);
304312
/* V = OP, FREE(V) => OP. NOP */
305-
if (src &&
313+
if (src >= op_array->opcodes + block->start &&
306314
src->opcode != ZEND_FETCH_R &&
307315
src->opcode != ZEND_FETCH_STATIC_PROP_R &&
308316
src->opcode != ZEND_FETCH_DIM_R &&

ext/opcache/tests/opt/gh11245_1.phpt

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
GH-11245: In some specific cases SWITCH with one default statement will cause segfault (VAR variation)
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.optimization_level=0x7FFFBFFF
7+
opcache.opt_debug_level=0x20000
8+
opcache.preload=
9+
--EXTENSIONS--
10+
opcache
11+
--FILE--
12+
<?php
13+
function xx() { return "somegarbage"; }
14+
switch (xx()) {
15+
default:
16+
if (!empty($xx)) {return;}
17+
}
18+
?>
19+
--EXPECTF--
20+
$_main:
21+
; (lines=4, args=0, vars=1, tmps=1)
22+
; (after optimizer)
23+
; %s
24+
0000 T1 = ISSET_ISEMPTY_CV (empty) CV0($xx)
25+
0001 JMPNZ T1 0003
26+
0002 RETURN null
27+
0003 RETURN int(1)
28+
29+
xx:
30+
; (lines=1, args=0, vars=0, tmps=0)
31+
; (after optimizer)
32+
; %s
33+
0000 RETURN string("somegarbage")

ext/opcache/tests/opt/gh11245_2.phpt

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
GH-11245: In some specific cases SWITCH with one default statement will cause segfault (TMP variation)
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.optimization_level=0x7FFFBFFF
7+
opcache.opt_debug_level=0x20000
8+
opcache.preload=
9+
--EXTENSIONS--
10+
opcache
11+
--FILE--
12+
<?php
13+
class X {
14+
// Chosen to test for a memory leak.
15+
static $prop = "aa";
16+
}
17+
switch (++X::$prop) {
18+
default:
19+
if (empty($xx)) {return;}
20+
}
21+
?>
22+
--EXPECTF--
23+
$_main:
24+
; (lines=7, args=0, vars=1, tmps=2)
25+
; (after optimizer)
26+
; %s
27+
0000 T1 = PRE_INC_STATIC_PROP string("prop") string("X")
28+
0001 T2 = ISSET_ISEMPTY_CV (empty) CV0($xx)
29+
0002 JMPZ T2 0005
30+
0003 FREE T1
31+
0004 RETURN null
32+
0005 FREE T1
33+
0006 RETURN int(1)
34+
LIVE RANGES:
35+
1: 0001 - 0005 (tmp/var)

0 commit comments

Comments
 (0)