mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Fix bug with brk_cont variable free / free loop vars via temporary liveliness info
This commit is contained in:
parent
b4223ca627
commit
ae5e58b598
5 changed files with 48 additions and 106 deletions
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue