]> granicus.if.org Git - php/commitdiff
Fixed bug #71196 (Memory leak with out-of-order live ranges)
authorDmitry Stogov <dmitry@zend.com>
Tue, 22 Dec 2015 22:47:38 +0000 (01:47 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 22 Dec 2015 22:47:38 +0000 (01:47 +0300)
Zend/tests/bug71196.phpt [new file with mode: 0644]
Zend/zend_compile.c

diff --git a/Zend/tests/bug71196.phpt b/Zend/tests/bug71196.phpt
new file mode 100644 (file)
index 0000000..ca25f9f
--- /dev/null
@@ -0,0 +1,13 @@
+--TEST--
+Bug #71196 (Memory leak with out-of-order live ranges)
+--FILE--
+<?php
+try  {
+        $a = "1";
+        [1, (y().$a.$a) . ($a.$a)];
+} catch (Error $e) {
+        var_dump($e->getMessage());
+}
+?>
+--EXPECT--
+string(30) "Call to undefined function y()"
index acf312cfa624502cc8262bf4d65a277aae7a7fa5..82601d9e3b7f0b299b10e20801b14f59ece04d70 100644 (file)
@@ -598,6 +598,68 @@ static uint32_t zend_start_live_range(zend_op_array *op_array, uint32_t start) /
 }
 /* }}} */
 
+static uint32_t zend_start_live_range_ex(zend_op_array *op_array, uint32_t start) /* {{{ */
+{
+       if (op_array->last_live_range == 0 ||
+           op_array->live_range[op_array->last_live_range - 1].start <= start) {
+               return zend_start_live_range(op_array, start);
+       } else {
+               /* Live ranges have to be sorted by "start" field */
+               uint32_t n = op_array->last_live_range;
+
+               /* move early ranges to make a room */
+               op_array->last_live_range = n + 1;
+               op_array->live_range = erealloc(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
+               do {
+                       op_array->live_range[n] = op_array->live_range[n-1];
+                       n--;
+               } while (n != 0 && op_array->live_range[n-1].start > start);
+
+           /* initialize new range */
+               op_array->live_range[n].start = start;
+
+               /* update referens to live-ranges from stack */
+               if (!zend_stack_is_empty(&CG(loop_var_stack))) {
+                       zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
+                       zend_loop_var *base = zend_stack_base(&CG(loop_var_stack));
+                       int check_opcodes = 0;
+
+                       for (; loop_var >= base; loop_var--) {
+                               if (loop_var->opcode == ZEND_RETURN) {
+                                       /* Stack separator */
+                                       break;
+                               } else if (loop_var->opcode == ZEND_FREE ||
+                                  loop_var->opcode == ZEND_FE_FREE) {
+                                       if (loop_var->u.live_range_offset >= n) {
+                                               loop_var->u.live_range_offset++;
+                                               check_opcodes = 1;
+                                       } else {
+                                               break;
+                                       }
+                               }
+                       }
+
+                       /* update previously generated FREE/FE_FREE opcodes */
+                       if (check_opcodes) {
+                               zend_op *opline = op_array->opcodes + op_array->live_range[n+1].start;
+                               zend_op *end = op_array->opcodes + op_array->last;
+
+                               while (opline < end) {
+                                       if ((opline->opcode == ZEND_FREE ||
+                                            opline->opcode == ZEND_FE_FREE) &&
+                                           (opline->extended_value & ZEND_FREE_ON_RETURN) &&
+                                           opline->op2.num >= n) {
+                                               opline->op2.num++;
+                                       }
+                                       opline++;
+                               }
+                       }
+               }
+               return n;
+       }
+}
+/* }}} */
+
 static void zend_end_live_range(zend_op_array *op_array, uint32_t offset, uint32_t end, uint32_t kind, uint32_t var) /* {{{ */
 {
        zend_live_range *range = op_array->live_range + offset;
@@ -1932,7 +1994,7 @@ static void zend_find_live_range(zend_op *opline, zend_uchar type, uint32_t var)
                                }
                        }
                zend_end_live_range(CG(active_op_array),
-                               zend_start_live_range(CG(active_op_array),
+                               zend_start_live_range_ex(CG(active_op_array),
                                        def + 1 - CG(active_op_array)->opcodes),
                                opline - CG(active_op_array)->opcodes,
                                ZEND_LIVE_TMPVAR, def->result.var);