@@ -264,6 +264,10 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
264264 break ;
265265
266266 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. */
267271 if (opline -> op1_type == IS_TMP_VAR ) {
268272 src = VAR_SOURCE (opline -> op1 );
269273 if (src ) {
@@ -272,6 +276,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
272276 case ZEND_BOOL_NOT :
273277 /* T = BOOL(X), FREE(T) => T = BOOL(X) */
274278 /* 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. */
275280 VAR_SOURCE (opline -> op1 ) = NULL ;
276281 MAKE_NOP (opline );
277282 ++ (* opt_count );
@@ -290,6 +295,9 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
290295 case ZEND_PRE_DEC_OBJ :
291296 case ZEND_PRE_INC_STATIC_PROP :
292297 case ZEND_PRE_DEC_STATIC_PROP :
298+ if (src < op_array -> opcodes + block -> start ) {
299+ break ;
300+ }
293301 src -> result_type = IS_UNUSED ;
294302 VAR_SOURCE (opline -> op1 ) = NULL ;
295303 MAKE_NOP (opline );
@@ -302,7 +310,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
302310 } else if (opline -> op1_type == IS_VAR ) {
303311 src = VAR_SOURCE (opline -> op1 );
304312 /* V = OP, FREE(V) => OP. NOP */
305- if (src &&
313+ if (src >= op_array -> opcodes + block -> start &&
306314 src -> opcode != ZEND_FETCH_R &&
307315 src -> opcode != ZEND_FETCH_STATIC_PROP_R &&
308316 src -> opcode != ZEND_FETCH_DIM_R &&
0 commit comments