← Commit list

a6af50c484

Daniel Stenberg
Daniel Stenberg
2026-06-26
SUCCESS Duration: 170s

Subject

multi: forbid curl_easy_pause from within multi socket callback

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

docs/libcurl/curl_easy_pause.md+2 -2
@@ -28,8 +28,8 @@ CURLcode curl_easy_pause(CURL *handle, int action);
Using this function, you can explicitly mark a running connection to get
paused, and you can unpause a connection that was previously paused. Unlike
-most other libcurl functions, curl_easy_pause(3) can be used from within
-callbacks.
+most other libcurl functions, curl_easy_pause(3) can be used from within all
+callbacks except the socket callback set with CURLMOPT_SOCKETFUNCTION(3).
A connection can be paused by using this function or by letting the read or
the write callbacks return the proper return code (*CURL_READFUNC_PAUSE* and
lib/easy.c+6 -5
@@ -1138,18 +1138,19 @@ void curl_easy_reset(CURL *curl)
CURLcode curl_easy_pause(CURL *curl, int action)
{
CURLcode result = CURLE_OK;
- bool recursive = FALSE;
bool changed = FALSE;
struct Curl_easy *data = curl;
bool recv_paused, recv_paused_new;
bool send_paused, send_paused_new;
+ uint8_t in_c;
if(!GOOD_EASY_HANDLE(data) || !data->conn)
/* crazy input, do not continue */
return CURLE_BAD_FUNCTION_ARGUMENT;
- if(Curl_is_in_callback(data))
- recursive = TRUE;
+ in_c = Curl_is_in_callback(data);
+ if(in_c == IN_CALLBACK_FORBID_EASY_PAUSE)
+ return CURLE_RECURSIVE_API_CALL;
recv_paused = Curl_xfer_recv_is_paused(data);
recv_paused_new = (action & CURLPAUSE_RECV);
@@ -1188,10 +1189,10 @@ CURLcode curl_easy_pause(CURL *curl, int action)
if(Curl_multi_ev_assess_xfer(data->multi, data) && !result)
result = CURLE_ABORTED_BY_CALLBACK;
- if(recursive)
+ if(in_c)
/* this might have called a callback recursively which might have set this
to false again on exit */
- Curl_set_in_callback(data, TRUE);
+ Curl_set_in_callback(data, in_c);
return result;
}
lib/multi.c+4 -3
@@ -3906,15 +3906,16 @@ static void process_pending_handles(struct Curl_multi *multi)
}
}
-void Curl_set_in_callback(struct Curl_easy *data, bool value)
+/* 'value' used to be a boolean but can now also contain more info */
+void Curl_set_in_callback(struct Curl_easy *data, uint8_t value)
{
if(data && data->multi)
data->multi->in_callback = value;
}
-bool Curl_is_in_callback(struct Curl_easy *data)
+uint8_t Curl_is_in_callback(struct Curl_easy *data)
{
- return data && data->multi && data->multi->in_callback;
+ return (data && data->multi) ? data->multi->in_callback : IN_CALLBACK_NO;
}
unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
lib/multi_ev.c+5 -8
@@ -34,8 +34,7 @@
#include "uint-spbset.h"
#include "multihandle.h"
-
-static void mev_in_callback(struct Curl_multi *multi, bool value)
+static void mev_in_callback(struct Curl_multi *multi, uint8_t value)
{
multi->in_callback = value;
}
@@ -209,12 +208,10 @@ static CURLMcode mev_forget_socket(struct Curl_multi *multi,
if(entry->announced && multi->socket_cb) {
NOVERBOSE((void)cause);
CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", cause, s);
- mev_in_callback(multi, TRUE);
+ mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE);
rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
multi->socket_userp, entry->user_data);
- mev_in_callback(multi, FALSE);
- /* curl_easy_pause() is documented as callable from any callback; it
- * re-enters mev_assess() which may free this 'entry'. Re-fetch. */
+ mev_in_callback(multi, IN_CALLBACK_NO);
entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
if(entry)
entry->announced = FALSE;
@@ -285,10 +282,10 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi,
CURL_TRC_M(data, "ev update call(fd=%" FMT_SOCKET_T ", ev=%s%s)",
s, (comboaction & CURL_POLL_IN) ? "IN" : "",
(comboaction & CURL_POLL_OUT) ? "OUT" : "");
- mev_in_callback(multi, TRUE);
+ mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE);
rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
entry->user_data);
- mev_in_callback(multi, FALSE);
+ mev_in_callback(multi, IN_CALLBACK_NO);
if(rc == -1) {
multi->dead = TRUE;
return CURLM_ABORTED_BY_CALLBACK;
lib/multihandle.h+5 -1
@@ -45,6 +45,10 @@ struct Curl_message {
struct CURLMsg extmsg;
};
+#define IN_CALLBACK_NO 0
+#define IN_CALLBACK_YES 1
+#define IN_CALLBACK_FORBID_EASY_PAUSE 2
+
/* NOTE: if you add a state here, add the name to the statenames[] array
* in curl_trc.c as well!
*/
@@ -179,10 +183,10 @@ struct Curl_multi {
#endif
uint32_t last_pending_mid; /* mid of last pending transfer rescheduled */
uint32_t last_resolv_id; /* id of the last DNS resolve operation */
+ uint8_t in_callback; /* conditions for executing in callbacks */
BIT(ipv6_works);
BIT(multiplexing); /* multiplexing wanted */
BIT(recheckstate); /* see Curl_multi_connchanged */
- BIT(in_callback); /* true while executing a callback */
BIT(in_ntfy_callback); /* true while dispatching notifications */
#ifdef USE_OPENSSL
BIT(ssl_seeded);
lib/multiif.h+2 -2
@@ -37,8 +37,8 @@ void Curl_attach_connection(struct Curl_easy *data,
struct connectdata *conn);
void Curl_detach_connection(struct Curl_easy *data);
bool Curl_multiplex_wanted(const struct Curl_multi *multi);
-void Curl_set_in_callback(struct Curl_easy *data, bool value);
-bool Curl_is_in_callback(struct Curl_easy *data);
+void Curl_set_in_callback(struct Curl_easy *data, uint8_t value);
+uint8_t Curl_is_in_callback(struct Curl_easy *data);
CURLcode Curl_preconnect(struct Curl_easy *data);
bool Curl_is_connecting(struct Curl_easy *data);
tests/libtest/lib758.c+9 -0
@@ -147,6 +147,7 @@ static int t758_curlSocketCallback(CURL *curl, curl_socket_t s, int action,
void *userp, void *socketp)
{
struct t758_ReadWriteSockets *sockets = userp;
+ CURLcode result;
(void)curl;
(void)socketp;
@@ -158,6 +159,14 @@ static int t758_curlSocketCallback(CURL *curl, curl_socket_t s, int action,
return -1;
}
+ /* Pause is forbidden in this callback. This also returns
+ CURLE_BAD_FUNCTION_ARGUMENT before the connection has been setup. */
+ result = curl_easy_pause(curl, CURLPAUSE_ALL);
+ if(!result) {
+ t758_msg("<- curl_easy_pause should return error!");
+ return -1;
+ }
+
if(action == CURL_POLL_IN || action == CURL_POLL_INOUT)
if(t758_addFd(&sockets->read, s, "read"))
return -1; /* bail out */