@@ -1582,6 +1582,149 @@ static zend_never_inline void zend_assign_to_object_dim(zend_object *obj, zval *
15821582 }
15831583}
15841584
1585+ struct _zend_frameless_reentry_copies {
1586+ struct _zend_frameless_reentry_copies * prev ;
1587+ zend_execute_data * execute_data ;
1588+ const zend_op * opline ;
1589+ uint8_t copied_args ;
1590+ zval args [3 ];
1591+ };
1592+
1593+ static zend_always_inline bool zend_frameless_arg_needs_reentry_copy (zval * zv )
1594+ {
1595+ ZVAL_DEREF (zv );
1596+ return Z_TYPE_P (zv ) == IS_ARRAY || Z_TYPE_P (zv ) == IS_STRING ;
1597+ }
1598+
1599+ static void zend_frameless_reentry_copy_arg (zend_frameless_reentry_copies * copies , uint32_t arg , zval * zv )
1600+ {
1601+ if (!zend_frameless_arg_needs_reentry_copy (zv )) {
1602+ return ;
1603+ }
1604+
1605+ ZVAL_COPY_DEREF (& copies -> args [arg ], zv );
1606+ copies -> copied_args |= (1u << arg );
1607+ }
1608+
1609+ static bool zend_frameless_reentry_has_copies (zend_execute_data * execute_data , const zend_op * opline )
1610+ {
1611+ for (zend_frameless_reentry_copies * copies = EG (frameless_reentry_copies );
1612+ copies ;
1613+ copies = copies -> prev ) {
1614+ if (copies -> execute_data == execute_data && copies -> opline == opline ) {
1615+ return true;
1616+ }
1617+ }
1618+
1619+ return false;
1620+ }
1621+
1622+ ZEND_API bool zend_frameless_protect_args_for_reentry (void )
1623+ {
1624+ zend_execute_data * execute_data = EG (current_execute_data );
1625+ if (!execute_data ) {
1626+ return false;
1627+ }
1628+
1629+ const zend_op * opline = EX (opline );
1630+ if (!opline || !ZEND_OP_IS_FRAMELESS_ICALL (opline -> opcode )) {
1631+ return false;
1632+ }
1633+
1634+ if (zend_frameless_reentry_has_copies (execute_data , opline )) {
1635+ return true;
1636+ }
1637+
1638+ uint8_t num_args = ZEND_FLF_NUM_ARGS (opline -> opcode );
1639+ if (num_args == 0 ) {
1640+ return false;
1641+ }
1642+
1643+ zend_frameless_reentry_copies * copies = emalloc (sizeof (zend_frameless_reentry_copies ));
1644+ copies -> execute_data = execute_data ;
1645+ copies -> opline = opline ;
1646+ copies -> copied_args = 0 ;
1647+
1648+ if (opline -> op1_type == IS_CV ) {
1649+ zend_frameless_reentry_copy_arg (copies , 0 ,
1650+ zend_get_zval_ptr (opline , opline -> op1_type , & opline -> op1 , execute_data ));
1651+ }
1652+ if (num_args >= 2 && opline -> op2_type == IS_CV ) {
1653+ zend_frameless_reentry_copy_arg (copies , 1 ,
1654+ zend_get_zval_ptr (opline , opline -> op2_type , & opline -> op2 , execute_data ));
1655+ }
1656+ if (num_args >= 3 && (opline + 1 )-> op1_type == IS_CV ) {
1657+ zend_frameless_reentry_copy_arg (copies , 2 ,
1658+ zend_get_zval_ptr (opline + 1 , (opline + 1 )-> op1_type , & (opline + 1 )-> op1 , execute_data ));
1659+ }
1660+
1661+ if (copies -> copied_args == 0 ) {
1662+ efree (copies );
1663+ return false;
1664+ }
1665+
1666+ copies -> prev = EG (frameless_reentry_copies );
1667+ EG (frameless_reentry_copies ) = copies ;
1668+
1669+ return true;
1670+ }
1671+
1672+ ZEND_API void zend_frameless_schedule_reentry_cleanup (void )
1673+ {
1674+ zend_atomic_bool_store_ex (& EG (vm_interrupt ), true);
1675+ }
1676+
1677+ static bool zend_frameless_reentry_copies_in_use (zend_frameless_reentry_copies * copies )
1678+ {
1679+ for (zend_execute_data * execute_data = EG (current_execute_data );
1680+ execute_data ;
1681+ execute_data = execute_data -> prev_execute_data ) {
1682+ if (execute_data == copies -> execute_data && execute_data -> opline == copies -> opline ) {
1683+ return true;
1684+ }
1685+ }
1686+
1687+ return false;
1688+ }
1689+
1690+ static void zend_frameless_cleanup_reentry_copies_ex (bool force )
1691+ {
1692+ zend_frameless_reentry_copies * * next = & EG (frameless_reentry_copies );
1693+
1694+ while (* next ) {
1695+ zend_frameless_reentry_copies * copies = * next ;
1696+
1697+ if (!force && zend_frameless_reentry_copies_in_use (copies )) {
1698+ next = & copies -> prev ;
1699+ continue ;
1700+ }
1701+
1702+ * next = copies -> prev ;
1703+
1704+ for (uint32_t i = 0 ; i < 3 ; i ++ ) {
1705+ if (copies -> copied_args & (1u << i )) {
1706+ zval_ptr_dtor (& copies -> args [i ]);
1707+ }
1708+ }
1709+
1710+ efree (copies );
1711+ }
1712+
1713+ if (EG (frameless_reentry_copies )) {
1714+ zend_atomic_bool_store_ex (& EG (vm_interrupt ), true);
1715+ }
1716+ }
1717+
1718+ ZEND_API void zend_frameless_cleanup_reentry_copies (void )
1719+ {
1720+ zend_frameless_cleanup_reentry_copies_ex (false);
1721+ }
1722+
1723+ ZEND_API void zend_frameless_cleanup_reentry_copies_force (void )
1724+ {
1725+ zend_frameless_cleanup_reentry_copies_ex (true);
1726+ }
1727+
15851728static void frameless_observed_call_copy (zend_execute_data * call , uint32_t arg , zval * zv )
15861729{
15871730 if (Z_ISUNDEF_P (zv )) {
@@ -4081,6 +4224,7 @@ ZEND_API void ZEND_FASTCALL zend_free_compiled_variables(zend_execute_data *exec
40814224ZEND_API ZEND_COLD void ZEND_FASTCALL zend_fcall_interrupt (zend_execute_data * call )
40824225{
40834226 zend_atomic_bool_store_ex (& EG (vm_interrupt ), false);
4227+ zend_frameless_cleanup_reentry_copies ();
40844228 if (zend_atomic_bool_load_ex (& EG (timed_out ))) {
40854229 zend_timeout ();
40864230 } else if (zend_interrupt_function ) {
0 commit comments