@@ -1465,14 +1465,19 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe
1465
1465
}
1466
1466
1467
1467
static 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 );
1469
1470
1470
1471
ZEND_API int zend_gc_collect_cycles (void )
1471
1472
{
1472
1473
int total_count = 0 ;
1473
1474
bool should_rerun_gc = 0 ;
1474
1475
bool did_rerun_gc = 0 ;
1475
1476
1477
+ if (GC_G (num_roots ) && GC_G (gc_active )) {
1478
+ zend_gc_remove_root_tmpvars ();
1479
+ }
1480
+
1476
1481
rerun_gc :
1477
1482
if (GC_G (num_roots )) {
1478
1483
int count ;
@@ -1669,7 +1674,7 @@ ZEND_API int zend_gc_collect_cycles(void)
1669
1674
1670
1675
finish :
1671
1676
zend_get_gc_buffer_release ();
1672
- zend_gc_root_tmpvars ();
1677
+ zend_gc_check_root_tmpvars ();
1673
1678
return total_count ;
1674
1679
}
1675
1680
@@ -1707,7 +1712,7 @@ static void zend_get_gc_buffer_release(void) {
1707
1712
* cycles. However, there are some rare exceptions where this is possible, in which case we rely
1708
1713
* on the producing code to root the value. If a GC run occurs between the rooting and consumption
1709
1714
* 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 ) {
1711
1716
zend_execute_data * ex = EG (current_execute_data );
1712
1717
for (; ex ; ex = ex -> prev_execute_data ) {
1713
1718
zend_function * func = ex -> func ;
@@ -1737,6 +1742,36 @@ static void zend_gc_root_tmpvars(void) {
1737
1742
}
1738
1743
}
1739
1744
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
+
1740
1775
#ifdef ZTS
1741
1776
size_t zend_gc_globals_size (void )
1742
1777
{
0 commit comments