@@ -1582,6 +1582,153 @@ 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+ if (!EX (func ) || !ZEND_USER_CODE (EX (func )-> type )) {
1630+ return false;
1631+ }
1632+
1633+ const zend_op * opline = EX (opline );
1634+ if (!opline || !ZEND_OP_IS_FRAMELESS_ICALL (opline -> opcode )) {
1635+ return false;
1636+ }
1637+
1638+ if (zend_frameless_reentry_has_copies (execute_data , opline )) {
1639+ return true;
1640+ }
1641+
1642+ uint8_t num_args = ZEND_FLF_NUM_ARGS (opline -> opcode );
1643+ if (num_args == 0 ) {
1644+ return false;
1645+ }
1646+
1647+ zend_frameless_reentry_copies * copies = emalloc (sizeof (zend_frameless_reentry_copies ));
1648+ copies -> execute_data = execute_data ;
1649+ copies -> opline = opline ;
1650+ copies -> copied_args = 0 ;
1651+
1652+ if (opline -> op1_type == IS_CV ) {
1653+ zend_frameless_reentry_copy_arg (copies , 0 ,
1654+ zend_get_zval_ptr (opline , opline -> op1_type , & opline -> op1 , execute_data ));
1655+ }
1656+ if (num_args >= 2 && opline -> op2_type == IS_CV ) {
1657+ zend_frameless_reentry_copy_arg (copies , 1 ,
1658+ zend_get_zval_ptr (opline , opline -> op2_type , & opline -> op2 , execute_data ));
1659+ }
1660+ if (num_args >= 3 && (opline + 1 )-> op1_type == IS_CV ) {
1661+ zend_frameless_reentry_copy_arg (copies , 2 ,
1662+ zend_get_zval_ptr (opline + 1 , (opline + 1 )-> op1_type , & (opline + 1 )-> op1 , execute_data ));
1663+ }
1664+
1665+ if (copies -> copied_args == 0 ) {
1666+ efree (copies );
1667+ return false;
1668+ }
1669+
1670+ copies -> prev = EG (frameless_reentry_copies );
1671+ EG (frameless_reentry_copies ) = copies ;
1672+
1673+ return true;
1674+ }
1675+
1676+ ZEND_API void zend_frameless_schedule_reentry_cleanup (void )
1677+ {
1678+ zend_atomic_bool_store_ex (& EG (vm_interrupt ), true);
1679+ }
1680+
1681+ static bool zend_frameless_reentry_copies_in_use (zend_frameless_reentry_copies * copies )
1682+ {
1683+ for (zend_execute_data * execute_data = EG (current_execute_data );
1684+ execute_data ;
1685+ execute_data = execute_data -> prev_execute_data ) {
1686+ if (execute_data == copies -> execute_data && execute_data -> opline == copies -> opline ) {
1687+ return true;
1688+ }
1689+ }
1690+
1691+ return false;
1692+ }
1693+
1694+ static void zend_frameless_cleanup_reentry_copies_ex (bool force )
1695+ {
1696+ zend_frameless_reentry_copies * * next = & EG (frameless_reentry_copies );
1697+
1698+ while (* next ) {
1699+ zend_frameless_reentry_copies * copies = * next ;
1700+
1701+ if (!force && zend_frameless_reentry_copies_in_use (copies )) {
1702+ next = & copies -> prev ;
1703+ continue ;
1704+ }
1705+
1706+ * next = copies -> prev ;
1707+
1708+ for (uint32_t i = 0 ; i < 3 ; i ++ ) {
1709+ if (copies -> copied_args & (1u << i )) {
1710+ zval_ptr_dtor (& copies -> args [i ]);
1711+ }
1712+ }
1713+
1714+ efree (copies );
1715+ }
1716+
1717+ if (EG (frameless_reentry_copies )) {
1718+ zend_atomic_bool_store_ex (& EG (vm_interrupt ), true);
1719+ }
1720+ }
1721+
1722+ ZEND_API void zend_frameless_cleanup_reentry_copies (void )
1723+ {
1724+ zend_frameless_cleanup_reentry_copies_ex (false);
1725+ }
1726+
1727+ ZEND_API void zend_frameless_cleanup_reentry_copies_force (void )
1728+ {
1729+ zend_frameless_cleanup_reentry_copies_ex (true);
1730+ }
1731+
15851732static void frameless_observed_call_copy (zend_execute_data * call , uint32_t arg , zval * zv )
15861733{
15871734 if (Z_ISUNDEF_P (zv )) {
@@ -4081,6 +4228,7 @@ ZEND_API void ZEND_FASTCALL zend_free_compiled_variables(zend_execute_data *exec
40814228ZEND_API ZEND_COLD void ZEND_FASTCALL zend_fcall_interrupt (zend_execute_data * call )
40824229{
40834230 zend_atomic_bool_store_ex (& EG (vm_interrupt ), false);
4231+ zend_frameless_cleanup_reentry_copies ();
40844232 if (zend_atomic_bool_load_ex (& EG (timed_out ))) {
40854233 zend_timeout ();
40864234 } else if (zend_interrupt_function ) {
0 commit comments