]> granicus.if.org Git - php/commitdiff
Fixed bug #80046
authorNikita Popov <nikita.ppv@gmail.com>
Thu, 3 Sep 2020 09:16:50 +0000 (11:16 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 3 Sep 2020 09:18:01 +0000 (11:18 +0200)
We already protect against optimizing away loop frees in DFA pass,
but not in block pass.

NEWS
Zend/tests/bug80046.phpt [new file with mode: 0644]
ext/opcache/Optimizer/block_pass.c
ext/opcache/Optimizer/zend_cfg.c

diff --git a/NEWS b/NEWS
index e2cb05e949509caa0ee02ab8b165a611fad44d0f..9eaa1b1c58d2fe08b8d92cca2a0b7a4457792a80 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,7 @@ PHP                                                                        NEWS
 - OPcache:
   . Fixed bug #80002 (calc free space for new interned string is wrong).
     (t-matsuno)
+  . Fixed bug #80046 (FREE for SWITCH_STRING optimized away). (Nikita)
 
 - PDO:
   . Fixed bug #80027 (Terrible performance using $query->fetch on queries with
diff --git a/Zend/tests/bug80046.phpt b/Zend/tests/bug80046.phpt
new file mode 100644 (file)
index 0000000..87a493c
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+Bug #80046: FREE for SWITCH_STRING optimized away
+--FILE--
+<?php
+
+function test($foo) {
+    switch ($foo . 'Bar') {
+        case 'A':
+            throw new Exception('A');
+        default:
+            throw new Exception('Default');
+    }
+}
+try {
+    test('Foo');
+} catch (Exception $e) {
+    echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+Default
index 3327ec86df4b0c30df0abff3ae3a8d3877e02025..17b89c4d53eff020a5d2163cb909dc8c248afbec 100644 (file)
@@ -921,7 +921,15 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
                if (b->len == 0) {
                        continue;
                }
-               if (b->flags & ZEND_BB_REACHABLE) {
+               if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
+                       if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
+                               /* Only keep the FREE for the loop var */
+                               ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
+                                               || op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
+                               len += b->len = 1;
+                               continue;
+                       }
+
                        opline = op_array->opcodes + b->start + b->len - 1;
                        if (opline->opcode == ZEND_JMP) {
                                zend_basic_block *next = b + 1;
@@ -959,7 +967,7 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
 
        /* Copy code of reachable blocks into a single buffer */
        for (b = blocks; b < end; b++) {
-               if (b->flags & ZEND_BB_REACHABLE) {
+               if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
                        memcpy(opline, op_array->opcodes + b->start, b->len * sizeof(zend_op));
                        b->start = opline - new_opcodes;
                        opline += b->len;
@@ -1083,7 +1091,7 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
        /* rebuild map (just for printing) */
        memset(cfg->map, -1, sizeof(int) * op_array->last);
        for (n = 0; n < cfg->blocks_count; n++) {
-               if (cfg->blocks[n].flags & ZEND_BB_REACHABLE) {
+               if (cfg->blocks[n].flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
                        cfg->map[cfg->blocks[n].start] = n;
                }
        }
index 66c15be3113ac1300f6827c48bbe0cddae75ac67..76c829bb3e2f9261febfe742dc5b3536c62af939 100644 (file)
@@ -575,9 +575,8 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
        }
 
        /* Build CFG, Step 4, Mark Reachable Basic Blocks */
-       zend_mark_reachable_blocks(op_array, cfg, 0);
-
        cfg->flags |= flags;
+       zend_mark_reachable_blocks(op_array, cfg, 0);
 
        return SUCCESS;
 }