@@ -264,6 +264,10 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
264
264
break ;
265
265
266
266
case ZEND_FREE :
267
+ /* Note: Only remove the source if the source is local to this block.
268
+ * If it's not local, then the other blocks successors must also eventually either FREE or consume the temporary,
269
+ * hence removing the temporary is not safe in the general case, especially when other consumers are not FREE.
270
+ * A FREE may not be removed without also removing the source's result, because otherwise that would cause a memory leak. */
267
271
if (opline -> op1_type == IS_TMP_VAR ) {
268
272
src = VAR_SOURCE (opline -> op1 );
269
273
if (src ) {
@@ -272,6 +276,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
272
276
case ZEND_BOOL_NOT :
273
277
/* T = BOOL(X), FREE(T) => T = BOOL(X) */
274
278
/* The remaining BOOL is removed by a separate optimization */
279
+ /* The source is a bool, no source removals take place, so this may be done non-locally. */
275
280
VAR_SOURCE (opline -> op1 ) = NULL ;
276
281
MAKE_NOP (opline );
277
282
++ (* opt_count );
@@ -290,6 +295,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
290
295
case ZEND_PRE_DEC_OBJ :
291
296
case ZEND_PRE_INC_STATIC_PROP :
292
297
case ZEND_PRE_DEC_STATIC_PROP :
298
+ if (src < op_array -> opcodes + block -> start ) {
299
+ break ;
300
+ }
293
301
src -> result_type = IS_UNUSED ;
294
302
VAR_SOURCE (opline -> op1 ) = NULL ;
295
303
MAKE_NOP (opline );
@@ -302,7 +310,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
302
310
} else if (opline -> op1_type == IS_VAR ) {
303
311
src = VAR_SOURCE (opline -> op1 );
304
312
/* V = OP, FREE(V) => OP. NOP */
305
- if (src &&
313
+ if (src >= op_array -> opcodes + block -> start &&
306
314
src -> opcode != ZEND_FETCH_R &&
307
315
src -> opcode != ZEND_FETCH_STATIC_PROP_R &&
308
316
src -> opcode != ZEND_FETCH_DIM_R &&
0 commit comments