Fix bug with brk_cont variable free / free loop vars via temporary liveliness info

This commit is contained in:
Bob Weinand 2015-07-06 04:22:58 +02:00
parent b4223ca627
commit ae5e58b598
5 changed files with 48 additions and 106 deletions

View file

@ -328,8 +328,10 @@ typedef struct _zend_internal_function_info {
zend_bool _is_variadic;
} zend_internal_function_info;
#define ZEND_LIVE_ROPE (1 << 0)
#define ZEND_LIVE_SILENCE (1 << 1)
#define ZEND_LIVE_ROPE 1
#define ZEND_LIVE_SILENCE 2
#define ZEND_LIVE_LOOP 3
#define ZEND_LIVE_MASK 3
struct _zend_op_array {
/* Common elements */

View file

@ -1939,31 +1939,6 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
}
}
static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_offset, const zend_op_array *op_array, const zend_execute_data *execute_data)
{
zend_brk_cont_element *jmp_to;
do {
ZEND_ASSERT(array_offset != -1);
jmp_to = &op_array->brk_cont_array[array_offset];
if (nest_levels > 1 && jmp_to->start >= 0) {
zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
if (brk_opline->opcode == ZEND_FREE) {
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
} else if (brk_opline->opcode == ZEND_FE_FREE) {
zval *var = EX_VAR(brk_opline->op1.var);
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
}
}
array_offset = jmp_to->parent;
} while (--nest_levels > 0);
return jmp_to;
}
#if ZEND_INTENSIVE_DEBUGGING
#define CHECK_SYMBOL_TABLES() \
@ -2389,10 +2364,24 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
if (EX(func)->op_array.T_liveliness && op_num < EX(func)->op_array.last) {
uint32_t *off = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[op_num];
uint32_t *until = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[op_num + 1];
uint32_t *catch_off = NULL;
uint32_t *catch_until = NULL;
if (catch_op_num) {
catch_off = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[catch_op_num];
catch_until = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[catch_op_num + 1];
}
while (off < until) {
uint32_t var = *(off++);
if (var & ZEND_LIVE_ROPE) {
/* we should be safe to assume that all temporaries at catch_op_num will be present at op_num too, in same order */
if (catch_off < catch_until && *catch_off == var) {
catch_off++;
continue;
}
if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_ROPE) {
/* free incomplete rope */
zend_string **rope = (zend_string **) EX_VAR(var & ~ZEND_LIVE_ROPE);
zend_op *last = EX(func)->op_array.opcodes + op_num;
@ -2408,12 +2397,19 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
zend_string_release(rope[j]);
} while (j--);
}
} else if (var & ZEND_LIVE_SILENCE) {
} else if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_SILENCE) {
/* restore previous error_reporting value */
var = var & ~ZEND_LIVE_SILENCE;
if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(var)) != 0) {
EG(error_reporting) = Z_LVAL_P(EX_VAR(var));
}
} else if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_LOOP) {
/* free loop variables */
var = var & ~ZEND_LIVE_LOOP;
if (Z_TYPE_P(EX_VAR(var)) != IS_ARRAY && Z_FE_ITER_P(EX_VAR(var)) != (uint32_t) -1) {
zend_hash_iterator_del(Z_FE_ITER_P(EX_VAR(var)));
}
zval_ptr_dtor_nogc(EX_VAR(var));
} else {
zval_ptr_dtor_nogc(EX_VAR(var));
}
@ -2534,29 +2530,6 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
call = EX(call);
} while (call);
}
for (i = 0; i < EX(func)->op_array.last_brk_cont; i++) {
const zend_brk_cont_element *brk_cont = &EX(func)->op_array.brk_cont_array[i];
if (brk_cont->start < 0) {
continue;
} else if (brk_cont->start > op_num) {
/* further blocks will not be relevant... */
break;
} else if (op_num < brk_cont->brk) {
if (!catch_op_num || catch_op_num >= brk_cont->brk) {
zend_op *brk_opline = &EX(func)->op_array.opcodes[brk_cont->brk];
/* ZEND_FREE opcodes are handled by temporary variable freeing */
if (brk_opline->opcode == ZEND_FE_FREE) {
zval *var = EX_VAR(brk_opline->op1.var);
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
}
}
}
}
}
/* }}} */

View file

@ -895,9 +895,7 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array)
T->start = T->end = -1;
}
if (T->start == -1 /* we need the exceptions *here* and not around the whole branch, else temporary ranges may be accidentally reused. If any of these opcodes reuses a temporary, we must mark the previous range as definitely terminated first */
&& cur_op->opcode != ZEND_CASE /* ??? exception for opcache, is anyway bool */
&& cur_op->opcode != ZEND_FE_RESET_R /* FOREACH TMP is handled using brk_cont_array */
&& cur_op->opcode != ZEND_FE_RESET_RW /* FOREACH TMP is handled using brk_cont_array */
&& cur_op->opcode != ZEND_CASE /* exception for opcache, it might nowhere use the temporary (anyway bool, so no need to free) */
&& cur_op->opcode != ZEND_ROPE_ADD /* the following opcodes reuse TMP created before */
&& cur_op->opcode != ZEND_ADD_ARRAY_ELEMENT
&& cur_op->opcode != ZEND_FAST_CALL /* passes fast_call */
@ -916,9 +914,6 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array)
}
}
if ((cur_op->op1_type & (IS_VAR | IS_TMP_VAR))
&& cur_op->opcode != ZEND_FE_FETCH_R /* FOREACH TMP is handled using brk_cont_array */
&& cur_op->opcode != ZEND_FE_FETCH_RW /* FOREACH TMP is handled using brk_cont_array */
&& cur_op->opcode != ZEND_FE_FREE /* FOREACH TMP is handled using brk_cont_array */
&& cur_op->opcode != ZEND_ROPE_ADD) { /* the following opcodes don't free TMP */
var_live_info *T = Ts[cur_op->op1.var];
if (T->start != (uint32_t) -1) {
@ -952,21 +947,21 @@ ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array)
ZEND_ASSERT(T->end != (uint32_t) -1);
for (j = T->start + 1; j < T->end; j++) {
if (op_array->opcodes[j].opcode != ZEND_THROW) {
op_var_info *opT = opTs[j];
if (opT->var != (uint32_t) -1) {
opTs[j] = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
opTs[j]->next = opT;
opT = opTs[j];
}
opT->var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + i);;
if (op_array->opcodes[T->end].opcode == ZEND_ROPE_END) {
opT->var |= ZEND_LIVE_ROPE;
} else if (op_array->opcodes[T->end].opcode == ZEND_END_SILENCE) {
opT->var |= ZEND_LIVE_SILENCE;
}
op_live_total++;
op_var_info *opT = opTs[j];
if (opT->var != (uint32_t) -1) {
opTs[j] = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
opTs[j]->next = opT;
opT = opTs[j];
}
opT->var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + i);;
if (op_array->opcodes[T->end].opcode == ZEND_ROPE_END) {
opT->var |= ZEND_LIVE_ROPE;
} else if (op_array->opcodes[T->end].opcode == ZEND_END_SILENCE) {
opT->var |= ZEND_LIVE_SILENCE;
} else if (op_array->opcodes[T->end].opcode == ZEND_FE_FREE) {
opT->var |= ZEND_LIVE_LOOP;
}
op_live_total++;
}
} while ((T = T->next));
}

View file

@ -4864,26 +4864,12 @@ ZEND_VM_HANDLER(52, ZEND_BOOL, CONST|TMPVAR|CV, ANY)
ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST)
{
USE_OPLINE
zend_brk_cont_element *el;
zend_op *target = OP_JMP_ADDR(opline, opline->op1);
SAVE_OPLINE();
el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->extended_value,
&EX(func)->op_array, execute_data);
if (el->start >= 0) {
zend_op *brk_opline = EX(func)->op_array.opcodes + el->brk;
if (brk_opline->opcode == ZEND_FREE) {
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
} else if (brk_opline->opcode == ZEND_FE_FREE) {
zval *var = EX_VAR(brk_opline->op1.var);
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
i_cleanup_unfinished_execution(execute_data, opline - EX(func)->op_array.opcodes, target - EX(func)->op_array.opcodes);
ZEND_VM_JMP(target);
}
ZEND_VM_HANDLER(48, ZEND_CASE, CONST|TMPVAR|CV, CONST|TMPVAR|CV)

View file

@ -2227,26 +2227,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(Z
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_brk_cont_element *el;
zend_op *target = OP_JMP_ADDR(opline, opline->op1);
SAVE_OPLINE();
el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->extended_value,
&EX(func)->op_array, execute_data);
if (el->start >= 0) {
zend_op *brk_opline = EX(func)->op_array.opcodes + el->brk;
if (brk_opline->opcode == ZEND_FREE) {
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
} else if (brk_opline->opcode == ZEND_FE_FREE) {
zval *var = EX_VAR(brk_opline->op1.var);
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
i_cleanup_unfinished_execution(execute_data, opline - EX(func)->op_array.opcodes, target - EX(func)->op_array.opcodes);
ZEND_VM_JMP(target);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_INTERFACE_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)