@@ -1465,14 +1465,19 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe
14651465}
14661466
14671467static void zend_get_gc_buffer_release (void );
1468- static void zend_gc_root_tmpvars (void );
1468+ static void zend_gc_check_root_tmpvars (void );
1469+ static void zend_gc_remove_root_tmpvars (void );
14691470
14701471ZEND_API int zend_gc_collect_cycles (void )
14711472{
14721473 int total_count = 0 ;
14731474 bool should_rerun_gc = 0 ;
14741475 bool did_rerun_gc = 0 ;
14751476
1477+ if (GC_G (num_roots ) && GC_G (gc_active )) {
1478+ zend_gc_remove_root_tmpvars ();
1479+ }
1480+
14761481rerun_gc :
14771482 if (GC_G (num_roots )) {
14781483 int count ;
@@ -1669,7 +1674,7 @@ ZEND_API int zend_gc_collect_cycles(void)
16691674
16701675finish :
16711676 zend_get_gc_buffer_release ();
1672- zend_gc_root_tmpvars ();
1677+ zend_gc_check_root_tmpvars ();
16731678 return total_count ;
16741679}
16751680
@@ -1707,7 +1712,7 @@ static void zend_get_gc_buffer_release(void) {
17071712 * cycles. However, there are some rare exceptions where this is possible, in which case we rely
17081713 * on the producing code to root the value. If a GC run occurs between the rooting and consumption
17091714 * of the value, we would end up leaking it. To avoid this, root all live TMPVAR values here. */
1710- static void zend_gc_root_tmpvars (void ) {
1715+ static void zend_gc_check_root_tmpvars (void ) {
17111716 zend_execute_data * ex = EG (current_execute_data );
17121717 for (; ex ; ex = ex -> prev_execute_data ) {
17131718 zend_function * func = ex -> func ;
@@ -1737,6 +1742,36 @@ static void zend_gc_root_tmpvars(void) {
17371742 }
17381743}
17391744
1745+ static void zend_gc_remove_root_tmpvars (void ) {
1746+ zend_execute_data * ex = EG (current_execute_data );
1747+ for (; ex ; ex = ex -> prev_execute_data ) {
1748+ zend_function * func = ex -> func ;
1749+ if (!func || !ZEND_USER_CODE (func -> type )) {
1750+ continue ;
1751+ }
1752+
1753+ uint32_t op_num = ex -> opline - ex -> func -> op_array .opcodes ;
1754+ for (uint32_t i = 0 ; i < func -> op_array .last_live_range ; i ++ ) {
1755+ const zend_live_range * range = & func -> op_array .live_range [i ];
1756+ if (range -> start > op_num ) {
1757+ break ;
1758+ }
1759+ if (range -> end <= op_num ) {
1760+ continue ;
1761+ }
1762+
1763+ uint32_t kind = range -> var & ZEND_LIVE_MASK ;
1764+ if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP ) {
1765+ uint32_t var_num = range -> var & ~ZEND_LIVE_MASK ;
1766+ zval * var = ZEND_CALL_VAR (ex , var_num );
1767+ if (Z_REFCOUNTED_P (var )) {
1768+ GC_REMOVE_FROM_BUFFER (Z_COUNTED_P (var ));
1769+ }
1770+ }
1771+ }
1772+ }
1773+ }
1774+
17401775#ifdef ZTS
17411776size_t zend_gc_globals_size (void )
17421777{
0 commit comments