Skip to content

Commit 482e6a7

Browse files
committed
Avoid throw expression leaks
1 parent d5e51fc commit 482e6a7

File tree

4 files changed

+50
-5
lines changed

4 files changed

+50
-5
lines changed

Zend/tests/throw/leaks.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
throw expression should not leak temporaries
3+
--FILE--
4+
<?php
5+
6+
try {
7+
new stdClass(throw new Exception);
8+
} catch (Exception $e) {
9+
echo "Caught\n";
10+
}
11+
12+
try {
13+
$a = [];
14+
($a + [1]) + throw new Exception;
15+
} catch (Exception $e) {
16+
echo "Caught\n";
17+
}
18+
19+
try {
20+
@throw new Exception;
21+
} catch (Exception $e) {
22+
echo "Caught\n";
23+
}
24+
var_dump(error_reporting());
25+
26+
?>
27+
--EXPECT--
28+
Caught
29+
Caught
30+
Caught
31+
int(32767)

Zend/zend_compile.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4548,10 +4548,13 @@ void zend_compile_throw(znode *result, zend_ast *ast) /* {{{ */
45484548
znode expr_node;
45494549
zend_compile_expr(&expr_node, expr_ast);
45504550

4551-
zend_emit_op(NULL, ZEND_THROW, &expr_node, NULL);
4552-
4553-
result->op_type = IS_CONST;
4554-
ZVAL_BOOL(&result->u.constant, 1);
4551+
zend_op *opline = zend_emit_op(NULL, ZEND_THROW, &expr_node, NULL);
4552+
if (result) {
4553+
/* Mark this as an "expression throw" for opcache. */
4554+
opline->extended_value = ZEND_THROW_IS_EXPR;
4555+
result->op_type = IS_CONST;
4556+
ZVAL_BOOL(&result->u.constant, 1);
4557+
}
45554558
}
45564559
/* }}} */
45574560

@@ -8789,6 +8792,9 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */
87898792
case ZEND_AST_HALT_COMPILER:
87908793
zend_compile_halt_compiler(ast);
87918794
break;
8795+
case ZEND_AST_THROW:
8796+
zend_compile_throw(NULL, ast);
8797+
break;
87928798
default:
87938799
{
87948800
znode result;

Zend/zend_compile.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
927927
#define ZEND_SEND_BY_REF 1u
928928
#define ZEND_SEND_PREFER_REF 2u
929929

930+
#define ZEND_THROW_IS_EXPR 1u
931+
930932
/* The send mode and is_variadic flag are stored as part of zend_type */
931933
#define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT
932934
#define _ZEND_IS_VARIADIC_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 2))

ext/opcache/Optimizer/zend_cfg.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,11 +296,17 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
296296
case ZEND_RETURN_BY_REF:
297297
case ZEND_GENERATOR_RETURN:
298298
case ZEND_EXIT:
299-
case ZEND_THROW:
300299
if (i + 1 < op_array->last) {
301300
BB_START(i + 1);
302301
}
303302
break;
303+
case ZEND_THROW:
304+
/* Don't treat THROW as terminator if it's used in expression context,
305+
* as we may lose live ranges when eliminating unreachable code. */
306+
if (opline->extended_value != ZEND_THROW_IS_EXPR && i + 1 < op_array->last) {
307+
BB_START(i + 1);
308+
}
309+
break;
304310
case ZEND_INCLUDE_OR_EVAL:
305311
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
306312
case ZEND_GENERATOR_CREATE:

0 commit comments

Comments
 (0)