Skip to content

Commit 17c5315

Browse files
committed
Fixed exception habdling on "return" statement.
Such exceptions shouldn't be caught in the same function.
1 parent d91aad5 commit 17c5315

File tree

7 files changed

+150
-6
lines changed

7 files changed

+150
-6
lines changed

Zend/tests/return_types/029.phpt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ function foo(): string {
88
try {
99
return $class; // invalid return type
1010
} catch (TypeError $e) {
11-
return "no leak or segfault";
11+
return "BAG!";
1212
}
1313
}
1414
}
15-
print foo();
16-
15+
try {
16+
print foo();
17+
} catch (TypeError $e) {
18+
print "no leak or segfault";
19+
}
1720
?>
1821
--EXPECT--
1922
no leak or segfault

Zend/tests/return_types/030.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Return types must not double free loop variables
3+
--FILE--
4+
<?php
5+
6+
function foo(): string {
7+
$a = [];
8+
try {
9+
return $a; // invalid return type
10+
} catch (TypeError $e) {
11+
echo "BAG!\n";
12+
return "ops!\n";
13+
}
14+
}
15+
try {
16+
echo foo();
17+
} catch (TypeError $e) {
18+
echo "OK\n";
19+
}
20+
21+
?>
22+
--EXPECT--
23+
OK
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
Exception inside a foreach loop with on an object with destructor
3+
--FILE--
4+
<?php
5+
class bar {
6+
public $foo = 1;
7+
public $bar = 2;
8+
function __destruct() {
9+
throw new Exception("test");
10+
}
11+
}
12+
function foo(): string {
13+
foreach (new bar() as $foo) {
14+
try {
15+
$foo = new Exception;
16+
return;
17+
} catch (Exception $e) {
18+
echo "Exception1: " . $e->getMessage() . "\n";
19+
} catch (Error $e) {
20+
echo "Error1: " . $e->getMessage() . "\n";
21+
}
22+
}
23+
echo "bag!\n";
24+
}
25+
try {
26+
foo();
27+
} catch (Throwable $e) {
28+
echo (($e instanceof Exception) ? "Exception2: " : "Error2: ") .
29+
$e->getMessage() . "\n";
30+
$e = $e->getPrevious();
31+
while ($e instanceof Throwable) {
32+
echo "\tPrev " . (($e instanceof Exception) ? "Exception2: " : "Error2: ") .
33+
$e->getMessage() . "\n";
34+
$e = $e->getPrevious();
35+
}
36+
}
37+
echo "ok\n";
38+
?>
39+
--EXPECTF--
40+
Exception2: test
41+
Prev Error2: Return value of foo() must be of the type string, none returned in %stemporary_cleaning_008.php on line %d
42+
ok
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
Exception inside a foreach loop with on an object with destructor
3+
--FILE--
4+
<?php
5+
class bar {
6+
public $foo = 1;
7+
function __destruct() {
8+
throw new Exception;
9+
}
10+
}
11+
12+
function foo() {
13+
foreach (new bar() as &$foo) {
14+
try {
15+
$foo = new Exception;
16+
return;
17+
} catch (Exception $e) {
18+
echo "Exception1\n";
19+
}
20+
}
21+
}
22+
try {
23+
foo();
24+
} catch (Exception $e) {
25+
echo "Exception2\n";
26+
}
27+
?>
28+
--EXPECT--
29+
Exception2

Zend/zend_opcode.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,8 @@ static zend_always_inline uint32_t *generate_var_liveliness_info_ex(zend_op_arra
992992
&& opline->opcode != ZEND_CASE
993993
&& opline->opcode != ZEND_FE_FETCH_R
994994
&& opline->opcode != ZEND_FE_FETCH_RW
995-
/* the following opcodes are not the "final" */
995+
/* the following opcodes are parts of "return" statement */
996+
&& opline->opcode != ZEND_VERIFY_RETURN_TYPE
996997
&& (opline->opcode != ZEND_FREE || !(opline->extended_value & ZEND_FREE_ON_RETURN))
997998
&& (opline->opcode != ZEND_FE_FREE || !(opline->extended_value & ZEND_FREE_ON_RETURN))
998999
) {

Zend/zend_vm_def.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2610,10 +2610,13 @@ ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, ANY)
26102610

26112611
ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
26122612
{
2613+
zval *var;
26132614
USE_OPLINE
26142615

26152616
SAVE_OPLINE();
2616-
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
2617+
var = EX_VAR(opline->op1.var);
2618+
zval_ptr_dtor_nogc(var);
2619+
ZVAL_NULL(var);
26172620
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
26182621
}
26192622

@@ -2626,8 +2629,10 @@ ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY)
26262629
var = EX_VAR(opline->op1.var);
26272630
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
26282631
zend_hash_iterator_del(Z_FE_ITER_P(var));
2632+
Z_FE_ITER_P(var) = (uint32_t)-1;
26292633
}
26302634
zval_ptr_dtor_nogc(var);
2635+
ZVAL_NULL(var);
26312636
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
26322637
}
26332638

@@ -3883,6 +3888,9 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
38833888

38843889
if (UNEXPECTED(EG(exception) != NULL)) {
38853890
FREE_OP1();
3891+
if (OP1_TYPE == IS_TMP_VAR || OP1_TYPE == IS_VAR) {
3892+
ZVAL_NULL(retval_ref);
3893+
}
38863894
}
38873895
#endif
38883896
}
@@ -7074,6 +7082,15 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
70747082
}
70757083
}
70767084

7085+
if (catch_op_num || finally_op_num) {
7086+
if (EX(func)->op_array.opcodes[op_num].opcode == ZEND_VERIFY_RETURN_TYPE
7087+
|| (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
7088+
|| (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FE_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
7089+
) {
7090+
catch_op_num = finally_op_num = 0;
7091+
}
7092+
}
7093+
70777094
i_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
70787095

70797096
if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {

Zend/zend_vm_execute.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
15001500
}
15011501
}
15021502

1503+
if (catch_op_num || finally_op_num) {
1504+
if (EX(func)->op_array.opcodes[op_num].opcode == ZEND_VERIFY_RETURN_TYPE
1505+
|| (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
1506+
|| (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FE_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
1507+
) {
1508+
catch_op_num = finally_op_num = 0;
1509+
}
1510+
}
1511+
15031512
i_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
15041513

15051514
if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
@@ -7740,6 +7749,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_
77407749

77417750
if (UNEXPECTED(EG(exception) != NULL)) {
77427751

7752+
if (IS_CONST == IS_TMP_VAR || IS_CONST == IS_VAR) {
7753+
ZVAL_NULL(retval_ref);
7754+
}
77437755
}
77447756
#endif
77457757
}
@@ -13536,6 +13548,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
1353613548

1353713549
if (UNEXPECTED(EG(exception) != NULL)) {
1353813550
zval_ptr_dtor_nogc(free_op1);
13551+
if (IS_TMP_VAR == IS_TMP_VAR || IS_TMP_VAR == IS_VAR) {
13552+
ZVAL_NULL(retval_ref);
13553+
}
1353913554
}
1354013555
#endif
1354113556
}
@@ -19225,6 +19240,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
1922519240

1922619241
if (UNEXPECTED(EG(exception) != NULL)) {
1922719242
zval_ptr_dtor_nogc(free_op1);
19243+
if (IS_VAR == IS_TMP_VAR || IS_VAR == IS_VAR) {
19244+
ZVAL_NULL(retval_ref);
19245+
}
1922819246
}
1922919247
#endif
1923019248
}
@@ -24910,6 +24928,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
2491024928

2491124929
if (UNEXPECTED(EG(exception) != NULL)) {
2491224930

24931+
if (IS_UNUSED == IS_TMP_VAR || IS_UNUSED == IS_VAR) {
24932+
ZVAL_NULL(retval_ref);
24933+
}
2491324934
}
2491424935
#endif
2491524936
}
@@ -34346,6 +34367,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
3434634367

3434734368
if (UNEXPECTED(EG(exception) != NULL)) {
3434834369

34370+
if (IS_CV == IS_TMP_VAR || IS_CV == IS_VAR) {
34371+
ZVAL_NULL(retval_ref);
34372+
}
3434934373
}
3435034374
#endif
3435134375
}
@@ -40164,10 +40188,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER(Z
4016440188

4016540189
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
4016640190
{
40191+
zval *var;
4016740192
USE_OPLINE
4016840193

4016940194
SAVE_OPLINE();
40170-
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
40195+
var = EX_VAR(opline->op1.var);
40196+
zval_ptr_dtor_nogc(var);
40197+
ZVAL_NULL(var);
4017140198
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
4017240199
}
4017340200

@@ -40180,8 +40207,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZE
4018040207
var = EX_VAR(opline->op1.var);
4018140208
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
4018240209
zend_hash_iterator_del(Z_FE_ITER_P(var));
40210+
Z_FE_ITER_P(var) = (uint32_t)-1;
4018340211
}
4018440212
zval_ptr_dtor_nogc(var);
40213+
ZVAL_NULL(var);
4018540214
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
4018640215
}
4018740216

0 commit comments

Comments
 (0)