← Commit list

a2fa1be85b

Joshua Rogers
Joshua Rogers
2026-09-16
SUCCESS Duration: 412s

Subject

lib: reject internal handles in the multi/easy public API

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_multi_remove_handle.md+4 -0
@@ -40,6 +40,10 @@ handles and transfers remain unaffected.
It is fine to remove a handle at any time during a transfer, but not from
within any libcurl callback function.
+An internal handle, such as one passed to a CURLMOPT_NOTIFYFUNCTION(3)
+callback, cannot be removed and makes this function return
+CURLM_BAD_EASY_HANDLE.
+
Removing an easy handle from the multi handle before the corresponding
transfer is complete might cause libcurl to close the connection - if the
state of it and the internal protocol handler deem it necessary. Otherwise
docs/libcurl/opts/CURLMOPT_NOTIFYFUNCTION.md+5 -0
@@ -51,6 +51,11 @@ and curl_multi_cleanup(3) it may call all other methods on the
multi and easy handles. This includes adding and removing easy
handles to/from the multi handle.
+Internal handles are owned by libcurl and the public API rejects them: the
+easy interface functions fail with CURLE_BAD_FUNCTION_ARGUMENT, and
+curl_multi_add_handle(3) and curl_multi_remove_handle(3) return
+CURLM_BAD_EASY_HANDLE.
+
This callback may get invoked at any time when interacting with libcurl.
This may even happen after all transfers are done and *may also*
happen *during* a call to curl_multi_cleanup(3) when cached connections
lib/api.c+1 -1
@@ -196,7 +196,7 @@ bool Curl_eapi_enter(struct Curl_eapi_guard *guard,
guard->depth = 0;
/* Verify that we got an easy handle we can work with. */
- if(!GOOD_EASY_HANDLE(data)) {
+ if(!GOOD_EASY_HANDLE(data) || data->state.internal) {
result = CURLE_BAD_FUNCTION_ARGUMENT;
goto out;
}
lib/multi.c+2 -2
@@ -581,7 +581,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl)
if(CURL_MAPI_ENTER(&guard, m, multi_add_handle, &mresult)) {
struct Curl_easy *data = curl;
/* Verify that we got a somewhat good easy handle too */
- if(!GOOD_EASY_HANDLE(data))
+ if(!GOOD_EASY_HANDLE(data) || data->state.internal)
mresult = CURLM_BAD_EASY_HANDLE;
else
mresult = Curl_multi_add_handle(m, data);
@@ -901,7 +901,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl)
if(CURL_MAPI_ENTER(&guard, m, multi_remove_handle, &mresult)) {
struct Curl_easy *data = curl;
- if(!GOOD_EASY_HANDLE(data))
+ if(!GOOD_EASY_HANDLE(data) || data->state.internal)
mresult = CURLM_BAD_EASY_HANDLE;
else
mresult = Curl_multi_remove_handle(m, data);
tests/data/Makefile.am+1 -0
@@ -2057,6 +2057,7 @@ TESTCASES = \
test3104 \
test3105 \
test3106 \
+ test3107 \
test3109 \
test3200 \
test3201 \
tests/data/test3107+47 -0
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+multi
+curl_multi_remove_handle
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 200 OK
+Content-Length: 6
+
+hello
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<tool>
+lib%TESTNUMBER
+</tool>
+<name>
+reject internal easy handle passed to a notify callback
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/%TESTNUMBER
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<protocol crlf="headers">
+GET /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+</verify>
+</testcase>
tests/libtest/Makefile.inc+1 -0
@@ -329,6 +329,7 @@ TESTS_C = \
lib3103.c \
lib3104.c \
lib3105.c \
+ lib3107.c \
lib3207.c \
lib3208.c \
lib3231.c \
tests/libtest/lib3107.c+105 -0
@@ -0,0 +1,105 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "first.h"
+
+static int t3107_notified;
+static CURLMcode t3107_mres;
+static CURLcode t3107_eres;
+
+static void t3107_notify_cb(CURLM *multi, unsigned int notification,
+ CURL *easy, void *user_data)
+{
+ (void)user_data;
+ if(notification != CURLMNOTIFY_INFO_READ)
+ return;
+ t3107_notified++;
+ t3107_mres = curl_multi_remove_handle(multi, easy);
+ t3107_eres = curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
+ curl_easy_cleanup(easy);
+}
+
+static CURLcode test_lib3107(const char *URL)
+{
+ CURL *curl = NULL;
+ CURLM *multi = NULL;
+ CURLcode result = CURLE_OK;
+ CURLMsg *msg;
+ int running = 1;
+ int queued;
+
+ global_init(CURL_GLOBAL_ALL);
+
+ multi_init(multi);
+ multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, t3107_notify_cb);
+
+ if(curl_multi_notify_enable(multi, CURLMNOTIFY_INFO_READ) != CURLM_OK) {
+ curl_mfprintf(stderr, "curl_multi_notify_enable() failed\n");
+ result = TEST_ERR_MAJOR_BAD;
+ goto test_cleanup;
+ }
+
+ easy_init(curl);
+ easy_setopt(curl, CURLOPT_URL, URL);
+
+ multi_add_handle(multi, curl);
+
+ while(running) {
+ CURLMcode mres = curl_multi_perform(multi, &running);
+ if(!mres && running)
+ mres = curl_multi_poll(multi, NULL, 0, 1000, NULL);
+ if(mres) {
+ curl_mfprintf(stderr, "curl_multi failed, with code %d (%s)\n",
+ (int)mres, curl_multi_strerror(mres));
+ result = TEST_ERR_MULTI;
+ goto test_cleanup;
+ }
+ }
+
+ do {
+ msg = curl_multi_info_read(multi, &queued);
+ } while(msg);
+
+ if(!t3107_notified) {
+ curl_mfprintf(stderr, "no CURLMNOTIFY_INFO_READ notification\n");
+ result = TEST_ERR_MAJOR_BAD;
+ }
+ else if(t3107_mres != CURLM_BAD_EASY_HANDLE) {
+ curl_mfprintf(stderr, "curl_multi_remove_handle() returned %d\n",
+ (int)t3107_mres);
+ result = TEST_ERR_MAJOR_BAD;
+ }
+ else if(t3107_eres != CURLE_BAD_FUNCTION_ARGUMENT) {
+ curl_mfprintf(stderr, "curl_easy_setopt() returned %d\n",
+ (int)t3107_eres);
+ result = TEST_ERR_MAJOR_BAD;
+ }
+
+test_cleanup:
+ curl_multi_remove_handle(multi, curl);
+ curl_multi_cleanup(multi);
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ return result;
+}