@@ -563,6 +563,34 @@ PHP_MSHUTDOWN_FUNCTION(curl)
563563}
564564/* }}} */
565565
566+ static void php_curl_call_callback (
567+ php_curl * ch , zend_fcall_info_cache * fcc , zval * retval , uint32_t argc , zval * argv )
568+ {
569+ /* The callback may replace or clear its own FCC. Keep its objects alive until
570+ * the call returns, without taking ownership of the FCC's trampoline. */
571+ zend_object * object = fcc -> object ;
572+ zend_object * closure = fcc -> closure ;
573+ if (object ) {
574+ GC_ADDREF (object );
575+ }
576+ if (closure ) {
577+ GC_ADDREF (closure );
578+ }
579+
580+ bool was_in_callback = ch -> in_callback ;
581+ ch -> in_callback = true;
582+ zend_call_known_fcc (fcc , retval , argc , argv , NULL );
583+
584+ /* Destructors may also call back into curl, so keep the callback guard set. */
585+ if (object ) {
586+ OBJ_RELEASE (object );
587+ }
588+ if (closure ) {
589+ OBJ_RELEASE (closure );
590+ }
591+ ch -> in_callback = was_in_callback ;
592+ }
593+
566594/* {{{ curl_write */
567595static size_t curl_write (char * data , size_t size , size_t nmemb , void * ctx )
568596{
@@ -594,9 +622,7 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
594622 ZVAL_OBJ (& argv [0 ], & ch -> std );
595623 ZVAL_STRINGL (& argv [1 ], data , length );
596624
597- ch -> in_callback = true;
598- zend_call_known_fcc (& write_handler -> fcc , & retval , /* param_count */ 2 , argv , /* named_params */ NULL );
599- ch -> in_callback = false;
625+ php_curl_call_callback (ch , & write_handler -> fcc , & retval , /* argc */ 2 , argv );
600626 if (!Z_ISUNDEF (retval )) {
601627 _php_curl_verify_handlers (ch , /* reporterror */ true);
602628 /* TODO Check callback returns an int or something castable to int */
@@ -630,9 +656,7 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
630656 ZVAL_STRING (& argv [1 ], pattern );
631657 ZVAL_STRING (& argv [2 ], string );
632658
633- ch -> in_callback = true;
634- zend_call_known_fcc (& ch -> handlers .fnmatch , & retval , /* param_count */ 3 , argv , /* named_params */ NULL );
635- ch -> in_callback = false;
659+ php_curl_call_callback (ch , & ch -> handlers .fnmatch , & retval , /* argc */ 3 , argv );
636660
637661 if (!Z_ISUNDEF (retval )) {
638662 _php_curl_verify_handlers (ch , /* reporterror */ true);
@@ -670,9 +694,7 @@ static int curl_progress(void *clientp, double dltotal, double dlnow, double ult
670694 ZVAL_LONG (& args [3 ], (zend_long )ultotal );
671695 ZVAL_LONG (& args [4 ], (zend_long )ulnow );
672696
673- ch -> in_callback = true;
674- zend_call_known_fcc (& ch -> handlers .progress , & retval , /* param_count */ 5 , args , /* named_params */ NULL );
675- ch -> in_callback = false;
697+ php_curl_call_callback (ch , & ch -> handlers .progress , & retval , /* argc */ 5 , args );
676698
677699 if (!Z_ISUNDEF (retval )) {
678700 _php_curl_verify_handlers (ch , /* reporterror */ true);
@@ -711,9 +733,7 @@ static int curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, cu
711733 ZVAL_LONG (& argv [3 ], ultotal );
712734 ZVAL_LONG (& argv [4 ], ulnow );
713735
714- ch -> in_callback = true;
715- zend_call_known_fcc (& ch -> handlers .xferinfo , & retval , /* param_count */ 5 , argv , /* named_params */ NULL );
716- ch -> in_callback = false;
736+ php_curl_call_callback (ch , & ch -> handlers .xferinfo , & retval , /* argc */ 5 , argv );
717737
718738 if (!Z_ISUNDEF (retval )) {
719739 _php_curl_verify_handlers (ch , /* reporterror */ true);
@@ -756,9 +776,7 @@ static int curl_prereqfunction(void *clientp, char *conn_primary_ip, char *conn_
756776 ZVAL_LONG (& args [3 ], conn_primary_port );
757777 ZVAL_LONG (& args [4 ], conn_local_port );
758778
759- ch -> in_callback = true;
760- zend_call_known_fcc (& ch -> handlers .prereq , & retval , /* param_count */ 5 , args , /* named_params */ NULL );
761- ch -> in_callback = false;
779+ php_curl_call_callback (ch , & ch -> handlers .prereq , & retval , /* argc */ 5 , args );
762780
763781 if (!Z_ISUNDEF (retval )) {
764782 _php_curl_verify_handlers (ch , /* reporterror */ true);
@@ -803,9 +821,7 @@ static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key,
803821 ZVAL_STRINGL (& args [2 ], key , keylen );
804822 ZVAL_LONG (& args [3 ], keylen );
805823
806- ch -> in_callback = true;
807- zend_call_known_fcc (& ch -> handlers .sshhostkey , & retval , /* param_count */ 4 , args , /* named_params */ NULL );
808- ch -> in_callback = false;
824+ php_curl_call_callback (ch , & ch -> handlers .sshhostkey , & retval , /* argc */ 4 , args );
809825
810826 if (!Z_ISUNDEF (retval )) {
811827 _php_curl_verify_handlers (ch , /* reporterror */ true);
@@ -855,9 +871,7 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
855871 }
856872 ZVAL_LONG (& argv [2 ], (int )size * nmemb );
857873
858- ch -> in_callback = true;
859- zend_call_known_fcc (& read_handler -> fcc , & retval , /* param_count */ 3 , argv , /* named_params */ NULL );
860- ch -> in_callback = false;
874+ php_curl_call_callback (ch , & read_handler -> fcc , & retval , /* argc */ 3 , argv );
861875 if (!Z_ISUNDEF (retval )) {
862876 _php_curl_verify_handlers (ch , /* reporterror */ true);
863877 if (Z_TYPE (retval ) == IS_STRING ) {
@@ -907,9 +921,7 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx
907921 ZVAL_OBJ (& argv [0 ], & ch -> std );
908922 ZVAL_STRINGL (& argv [1 ], data , length );
909923
910- ch -> in_callback = true;
911- zend_call_known_fcc (& write_handler -> fcc , & retval , /* param_count */ 2 , argv , /* named_params */ NULL );
912- ch -> in_callback = false;
924+ php_curl_call_callback (ch , & write_handler -> fcc , & retval , /* argc */ 2 , argv );
913925 if (!Z_ISUNDEF (retval )) {
914926 // TODO: Check for valid int type for return value
915927 _php_curl_verify_handlers (ch , /* reporterror */ true);
@@ -963,9 +975,7 @@ static int curl_debug(CURL *handle, curl_infotype type, char *data, size_t size,
963975 ZVAL_LONG (& args [1 ], type );
964976 ZVAL_STRINGL (& args [2 ], data , size );
965977
966- ch -> in_callback = true;
967- zend_call_known_fcc (& ch -> handlers .debug , NULL , /* param_count */ 3 , args , /* named_params */ NULL );
968- ch -> in_callback = false;
978+ php_curl_call_callback (ch , & ch -> handlers .debug , NULL , /* argc */ 3 , args );
969979
970980 zval_ptr_dtor (& args [0 ]);
971981 zval_ptr_dtor (& args [2 ]);
@@ -1635,14 +1645,8 @@ PHP_FUNCTION(curl_copy_handle)
16351645}
16361646/* }}} */
16371647
1638- static bool php_curl_set_callable_handler (php_curl * ch , zend_fcall_info_cache * const handler_fcc , zval * callable , bool is_array_config , const char * option_name )
1648+ static bool php_curl_set_callable_handler (zend_fcall_info_cache * const handler_fcc , zval * callable , bool is_array_config , const char * option_name )
16391649{
1640- /* Replacing a callback would free the fcc that is still executing on the stack. */
1641- if (ch -> in_callback ) {
1642- zend_throw_error (NULL , "%s(): Attempt to set the %s option from a callback" , get_active_function_name (), option_name );
1643- return false;
1644- }
1645-
16461650 if (ZEND_FCC_INITIALIZED (* handler_fcc )) {
16471651 zend_fcc_dtor (handler_fcc );
16481652 }
@@ -1666,7 +1670,7 @@ static bool php_curl_set_callable_handler(php_curl *ch, zend_fcall_info_cache *c
16661670
16671671#define HANDLE_CURL_OPTION_CALLABLE_PHP_CURL_USER (curl_ptr , constant_no_function , handler_type , default_method ) \
16681672 case constant_no_function##FUNCTION: { \
1669- bool result = php_curl_set_callable_handler(curl_ptr, &curl_ptr->handlers.handler_type->fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \
1673+ bool result = php_curl_set_callable_handler(&curl_ptr->handlers.handler_type->fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \
16701674 if (!result) { \
16711675 curl_ptr->handlers.handler_type->method = default_method; \
16721676 return FAILURE; \
@@ -1681,7 +1685,7 @@ static bool php_curl_set_callable_handler(php_curl *ch, zend_fcall_info_cache *c
16811685
16821686#define HANDLE_CURL_OPTION_CALLABLE (curl_ptr , constant_no_function , handler_fcc , c_callback ) \
16831687 case constant_no_function##FUNCTION: { \
1684- bool result = php_curl_set_callable_handler(curl_ptr, &curl_ptr->handler_fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \
1688+ bool result = php_curl_set_callable_handler(&curl_ptr->handler_fcc, zvalue, is_array_config, #constant_no_function "FUNCTION"); \
16851689 if (!result) { \
16861690 return FAILURE; \
16871691 } \
0 commit comments