]> granicus.if.org Git - php/commitdiff
Avoid throw expression leaks
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 24 Apr 2020 14:12:06 +0000 (16:12 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Mon, 27 Apr 2020 13:22:05 +0000 (15:22 +0200)
Mark "throw" used in expression context with a flag, and don't
treat it as a BB terminator in that case. This prevents us from
optimizing away the following opcodes as unreachable, which may
result in live ranges being dropped incorrectly.

Close GH-5450.

Zend/tests/throw/leaks.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_compile.h
ext/opcache/Optimizer/zend_cfg.c

diff --git a/Zend/tests/throw/leaks.phpt b/Zend/tests/throw/leaks.phpt
new file mode 100644 (file)
index 0000000..072f65f
--- /dev/null
@@ -0,0 +1,31 @@
+--TEST--
+throw expression should not leak temporaries
+--FILE--
+<?php
+
+try {
+    new stdClass(throw new Exception);
+} catch (Exception $e) {
+    echo "Caught\n";
+}
+
+try {
+    $a = [];
+    ($a + [1]) + throw new Exception;
+} catch (Exception $e) {
+    echo "Caught\n";
+}
+
+try {
+    @throw new Exception;
+} catch (Exception $e) {
+    echo "Caught\n";
+}
+var_dump(error_reporting());
+
+?>
+--EXPECT--
+Caught
+Caught
+Caught
+int(32767)
index a83d4d3d180c11b53c850843efe970e3d8d4fb22..bb279d5570b8e99b317275ac51e561969fdfdefa 100644 (file)
@@ -4548,10 +4548,13 @@ void zend_compile_throw(znode *result, zend_ast *ast) /* {{{ */
        znode expr_node;
        zend_compile_expr(&expr_node, expr_ast);
 
-       zend_emit_op(NULL, ZEND_THROW, &expr_node, NULL);
-
-       result->op_type = IS_CONST;
-       ZVAL_BOOL(&result->u.constant, 1);
+       zend_op *opline = zend_emit_op(NULL, ZEND_THROW, &expr_node, NULL);
+       if (result) {
+               /* Mark this as an "expression throw" for opcache. */
+               opline->extended_value = ZEND_THROW_IS_EXPR;
+               result->op_type = IS_CONST;
+               ZVAL_BOOL(&result->u.constant, 1);
+       }
 }
 /* }}} */
 
@@ -8791,6 +8794,9 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */
                case ZEND_AST_HALT_COMPILER:
                        zend_compile_halt_compiler(ast);
                        break;
+               case ZEND_AST_THROW:
+                       zend_compile_throw(NULL, ast);
+                       break;
                default:
                {
                        znode result;
index 8631747c063001e531ad2c15b40efd4de997bd47..a90219b1b4453b561f88c2c85a9c2ee46ae3b269 100644 (file)
@@ -927,6 +927,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
 #define ZEND_SEND_BY_REF     1u
 #define ZEND_SEND_PREFER_REF 2u
 
+#define ZEND_THROW_IS_EXPR 1u
+
 /* The send mode and is_variadic flag are stored as part of zend_type */
 #define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT
 #define _ZEND_IS_VARIADIC_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 2))
index c5b1b53a0ec63f88ed4834c2cc0ace9906893a50..6ac5781e09dcab713e6ac9a3d6bd05124c98231c 100644 (file)
@@ -296,11 +296,17 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
                        case ZEND_RETURN_BY_REF:
                        case ZEND_GENERATOR_RETURN:
                        case ZEND_EXIT:
-                       case ZEND_THROW:
                                if (i + 1 < op_array->last) {
                                        BB_START(i + 1);
                                }
                                break;
+                       case ZEND_THROW:
+                               /* Don't treat THROW as terminator if it's used in expression context,
+                                * as we may lose live ranges when eliminating unreachable code. */
+                               if (opline->extended_value != ZEND_THROW_IS_EXPR && i + 1 < op_array->last) {
+                                       BB_START(i + 1);
+                               }
+                               break;
                        case ZEND_INCLUDE_OR_EVAL:
                                flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
                        case ZEND_GENERATOR_CREATE: