public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Jan Macku <jamacku@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/curl] rawhide: fix multi_fdset must not report only the wakeup socket
Date: Thu, 18 Jun 2026 10:54:18 GMT	[thread overview]
Message-ID: <178178005848.1.15778690615029135378.rpms-curl-b4df4148f935@fedoraproject.org> (raw)

            A new commit has been pushed.

            Repo   : rpms/curl
            Branch : rawhide
            Commit : b4df4148f935bcae13ed56bcd87142439a7b736e
            Author : Jan Macku <jamacku@redhat.com>
            Date   : 2026-06-18T12:29:27+02:00
            Stats  : +373/-2 in 2 file(s)
            URL    : https://src.fedoraproject.org/rpms/curl/c/b4df4148f935bcae13ed56bcd87142439a7b736e?branch=rawhide

            Log:
            fix multi_fdset must not report only the wakeup socket

Resolves: #2460719

---
diff --git a/0001-curl-8.21.0~rc3-multi-xfers_really_alive.patch b/0001-curl-8.21.0~rc3-multi-xfers_really_alive.patch
new file mode 100644
index 0000000..35682da
--- /dev/null
+++ b/0001-curl-8.21.0~rc3-multi-xfers_really_alive.patch
@@ -0,0 +1,365 @@
+From 6a92e7cf03e819b84b3d96ae7934cccbb1a6563b Mon Sep 17 00:00:00 2001
+From: Stefan Eissing <stefan@eissing.org>
+Date: Wed, 17 Jun 2026 14:20:02 +0200
+Subject: [PATCH] multi: xfers_really_alive
+
+Yes, we were counting the "live" transfers before, but were they
+*really* alive?
+
+When determining to add the wakeup socket to fdset/waitfds etc, we
+should only do that when the multi handle is actually processing
+transfers. Other wise, the application could wait on the wakeup socket
+forever.
+
+For this, we counted `multi->xfers_alive` (e.g. the "running" number
+returned by `curl_multi_perform()`). This was almost correct.
+
+The problem is that added easy handles are counted as "alive" right away
+on the addition. But the processing has not started yet. They did not
+trigger any DNS resolves or opened any sockets yet.
+
+Add two fields in multi and easy handle:
+
+* `multi->xfers_really_alive`: counts the "alive" transfers that have
+  passed `MSTATE_INIT` (at least once)
+* `data->state.really_alive`: to track if the transfer has been counted
+
+Add test 2412 to check that adding transfers without perform will not
+trigger the wakeup socket to be added.
+
+Fixes #22050
+Reported-by: Bryan Henderson
+Closes #22066
+
+(cherry picked from commit f0be41763542f68dce344beee8a5c5e5b858e6d1)
+---
+ lib/multi.c                | 36 ++++++++++++---
+ lib/multihandle.h          |  2 +
+ lib/urldata.h              |  6 +--
+ tests/data/Makefile.am     |  2 +-
+ tests/data/test2412        | 50 ++++++++++++++++++++
+ tests/libtest/Makefile.inc |  1 +
+ tests/libtest/lib2412.c    | 95 ++++++++++++++++++++++++++++++++++++++
+ tests/libtest/lib530.c     |  3 ++
+ 8 files changed, 182 insertions(+), 13 deletions(-)
+ create mode 100644 tests/data/test2412
+ create mode 100644 tests/libtest/lib2412.c
+
+diff --git a/lib/multi.c b/lib/multi.c
+index d0fa68ab4c..d6ae111d8e 100644
+--- a/lib/multi.c
++++ b/lib/multi.c
+@@ -531,6 +531,8 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl)
+ 
+   /* set the easy handle */
+   multistate(data, MSTATE_INIT);
++  /* not yet passed INIT state */
++  data->state.really_alive = FALSE;
+ 
+ #ifdef USE_LIBPSL
+   /* Do the same for PSL. */
+@@ -570,12 +572,6 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl)
+     data->set.server_response_timeout;
+   multi->admin->set.no_signal = data->set.no_signal;
+ 
+-  mresult = multi_assess_wakeup(multi);
+-  if(mresult) {
+-    failf(data, "error enabling wakeup listening: %d", mresult);
+-    return mresult;
+-  }
+-
+   CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u",
+              data->mid, Curl_multi_xfers_running(multi),
+              Curl_uint32_tbl_count(&multi->xfers));
+@@ -851,6 +847,12 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl)
+   /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */
+   if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid))
+     --multi->xfers_alive;
++  if(data->state.really_alive) {
++    data->state.really_alive = FALSE;
++    --multi->xfers_really_alive;
++    if(!multi->xfers_really_alive)
++      (void)multi_assess_wakeup(multi);
++  }
+ 
+   Curl_wildcard_dtor(&data->wildcard);
+ 
+@@ -1151,7 +1153,9 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data,
+   /* The admin handle always listens on the wakeup socket when there
+    * are transfers alive. */
+   if(data->multi && (data == data->multi->admin) &&
+-     data->multi->xfers_alive) {
++     data->multi->xfers_really_alive) {
++    CURL_TRC_M(data, "adding wakeup, %u xfers really alive",
++               data->multi->xfers_really_alive);
+     result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]);
+   }
+ #endif
+@@ -2459,6 +2463,12 @@ static void handle_completed(struct Curl_multi *multi,
+   Curl_uint32_bset_remove(&multi->dirty, data->mid);
+   Curl_uint32_bset_remove(&multi->pending, data->mid);
+   Curl_uint32_bset_add(&multi->msgsent, data->mid);
++  if(data->state.really_alive) {
++    data->state.really_alive = FALSE;
++    --multi->xfers_really_alive;
++    if(!multi->xfers_really_alive)
++      (void)multi_assess_wakeup(multi);
++  }
+   --multi->xfers_alive;
+   if(!multi->xfers_alive)
+     multi_assess_wakeup(multi);
+@@ -2466,6 +2476,18 @@ static void handle_completed(struct Curl_multi *multi,
+ 
+ static CURLMcode multistate_init(struct Curl_easy *data, CURLcode *result)
+ {
++  if(!data->state.really_alive) {
++    data->state.really_alive = TRUE;
++    ++data->multi->xfers_really_alive;
++    if(data->multi->xfers_really_alive == 1) {
++      CURLMcode mresult = multi_assess_wakeup(data->multi);
++      if(mresult) {
++        failf(data, "error enabling wakeup listening: %d", mresult);
++        return mresult;
++      }
++    }
++  }
++
+   *result = Curl_pretransfer(data);
+   if(*result)
+     return CURLM_OK;
+diff --git a/lib/multihandle.h b/lib/multihandle.h
+index c5cdfbe82e..19dd2ffcdf 100644
+--- a/lib/multihandle.h
++++ b/lib/multihandle.h
+@@ -85,6 +85,8 @@ struct Curl_multi {
+ 
+   unsigned int xfers_alive; /* amount of added transfers that have
+                                not yet reached COMPLETE state */
++  unsigned int xfers_really_alive; /* amount of added transfers that have
++                               passed INIT state but are not COMPLETE yet */
+   curl_off_t xfers_total_ever; /* total of added transfers, ever. */
+   struct uint32_tbl xfers; /* transfers added to this multi */
+   /* Each transfer's mid may be present in at most one of these */
+diff --git a/lib/urldata.h b/lib/urldata.h
+index 232364fcf3..d4d336d8db 100644
+--- a/lib/urldata.h
++++ b/lib/urldata.h
+@@ -704,11 +704,7 @@ struct UrlState {
+   uint8_t httpreq; /* Curl_HttpReq; what kind of HTTP request (if any)
+                             is this */
+ 
+-  /* when curl_easy_perform() is called, the multi handle is "owned" by
+-     the easy handle so curl_easy_cleanup() on such an easy handle will
+-     also close the multi handle! */
+-  BIT(multi_owned_by_easy);
+-
++  BIT(really_alive); /* transfer is really alive in multi, passed INIT */
+   BIT(this_is_a_follow); /* this is a followed Location: request */
+   BIT(refused_stream); /* this was refused, try again */
+   BIT(errorbuf); /* Set to TRUE if the error buffer is already filled in.
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index 0d5277d55b..676d49f4fd 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -261,7 +261,7 @@ test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \
+ test2309 test2310 \
+ \
+ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \
+-test2408 test2409 test2410 test2411 \
++test2408 test2409 test2410 test2411 test2412 \
+ \
+ test2500 test2501 test2502 test2503 test2504 test2505 test2506 \
+ \
+diff --git a/tests/data/test2412 b/tests/data/test2412
+new file mode 100644
+index 0000000000..e0320e2ce4
+--- /dev/null
++++ b/tests/data/test2412
+@@ -0,0 +1,50 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++multi
++</keywords>
++</info>
++
++# Server-side
++<reply>
++<data nocheck="yes">
++HTTP/1.1 200 OK
++Date: Tue, 09 Nov 2010 14:49:00 GMT
++Server: test-server/fake
++Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
++ETag: "21025-dc7-39462498"
++Accept-Ranges: bytes
++Content-Length: 6007
++Connection: close
++Content-Type: text/html
++Funny-head: yesyes
++
++-foo-
++%repeat[1000 x foobar]%
++</data>
++</reply>
++
++# Client-side
++<client>
++<features>
++wakeup
++</features>
++<server>
++http
++</server>
++<tool>
++lib%TESTNUMBER
++</tool>
++<name>
++checking curl_multi_fdset on nothing to do
++</name>
++<command>
++http://%HOSTIP:%HTTPPORT/%TESTNUMBER
++</command>
++</client>
++
++# Verify data after the test has been "shot"
++<verify>
++</verify>
++</testcase>
+diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
+index 98c9939994..bec648542b 100644
+--- a/tests/libtest/Makefile.inc
++++ b/tests/libtest/Makefile.inc
+@@ -115,6 +115,7 @@ TESTS_C = \
+   lib2023.c lib2032.c lib2082.c \
+   lib2301.c lib2302.c lib2304.c           lib2306.c lib2308.c lib2309.c \
+   lib2402.c           lib2404.c lib2405.c \
++  lib2412.c \
+   lib2502.c lib2504.c lib2505.c lib2506.c \
+   lib2700.c \
+   lib3010.c lib3025.c lib3026.c lib3027.c lib3033.c lib3034.c \
+diff --git a/tests/libtest/lib2412.c b/tests/libtest/lib2412.c
+new file mode 100644
+index 0000000000..79d49a2d76
+--- /dev/null
++++ b/tests/libtest/lib2412.c
+@@ -0,0 +1,95 @@
++/***************************************************************************
++ *                                  _   _ ____  _
++ *  Project                     ___| | | |  _ \| |
++ *                             / __| | | | |_) | |
++ *                            | (__| |_| |  _ <| |___
++ *                             \___|\___/|_| \_\_____|
++ *
++ * Copyright (C) Dmitry Karpov <dkarpov1970@gmail.com>
++ *
++ * 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"
++#include "testtrace.h"
++
++static CURLcode test_lib2412(const char *URL)
++{
++  CURLcode result = CURLE_OK;
++  CURLM *multi = NULL;
++  CURL *easy = NULL;
++  CURLMcode rc;
++  fd_set readFdSet, writeFdSet, exceptFdSet;
++  int maxFd;
++
++  (void)URL;
++  global_init(CURL_GLOBAL_ALL);
++
++  multi = curl_multi_init();
++  if(!multi) {
++    curl_mfprintf(stderr, "curl_multi_init() failed\n");
++    result = TEST_ERR_MAJOR_BAD;
++    goto test_cleanup;
++  }
++
++  easy = curl_easy_init();
++  if(!easy) {
++    curl_mfprintf(stderr, "curl_easy_init() failed\n");
++    result = TEST_ERR_MAJOR_BAD;
++    goto test_cleanup;
++  }
++  debug_config.nohex = TRUE;
++  debug_config.tracetime = TRUE;
++  easy_setopt(easy, CURLOPT_DEBUGDATA, &debug_config);
++  easy_setopt(easy, CURLOPT_DEBUGFUNCTION, libtest_debug_cb);
++  easy_setopt(easy, CURLOPT_VERBOSE, 1L);
++
++  rc = curl_multi_add_handle(multi, easy);
++  if(rc) {
++    curl_mfprintf(stderr, "curl_multi_add_handle() failed: %d\n", rc);
++    result = TEST_ERR_MAJOR_BAD;
++    goto test_cleanup;
++  }
++
++  FD_ZERO(&readFdSet);
++  FD_ZERO(&writeFdSet);
++  FD_ZERO(&exceptFdSet);
++  maxFd = -1;
++  rc = curl_multi_fdset(multi, &readFdSet, &writeFdSet, &exceptFdSet,
++                        &maxFd);
++  if(rc) {
++    curl_mfprintf(stderr, "curl_multi_fdset() failed: %d\n", rc);
++    result = TEST_ERR_MAJOR_BAD;
++    goto test_cleanup;
++  }
++
++  if(maxFd == -1)
++    curl_mfprintf(stderr, "There are no file descriptors to wait for\n");
++  else {
++    curl_mfprintf(stderr, "libcurl supplied a file descriptor to "
++           "wait for (maxFd=%d).  Waiting now ...\n", maxFd);
++    result = TEST_ERR_FAILURE;
++  }
++
++test_cleanup:
++  if(easy) {
++    curl_multi_remove_handle(multi, easy);
++    curl_easy_cleanup(easy);
++  }
++  if(multi)
++    curl_multi_cleanup(multi);
++  curl_global_cleanup();
++  return result;
++}
+diff --git a/tests/libtest/lib530.c b/tests/libtest/lib530.c
+index d4c894d1d0..bddb857be0 100644
+--- a/tests/libtest/lib530.c
++++ b/tests/libtest/lib530.c
+@@ -29,6 +29,7 @@
+  */
+ 
+ #include "first.h"
++#include "testtrace.h"
+ 
+ static struct t530_ctx {
+   int socket_calls;
+@@ -300,6 +301,8 @@ static CURLcode testone(const char *URL, int timer_fail_at, int socket_fail_at)
+   easy_setopt(curl, CURLOPT_URL, URL);
+ 
+   /* go verbose */
++  easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config);
++  easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb);
+   easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+ 
+   multi_init(multi);
+-- 
+2.54.0
+

diff --git a/curl.spec b/curl.spec
index 1be531f..0492417 100644
--- a/curl.spec
+++ b/curl.spec
@@ -13,7 +13,7 @@
 Summary: A utility for getting files from remote servers (FTP, HTTP, and others)
 Name: curl
 Version: 8.21.0~rc3
-Release: 1%{?dist}
+Release: 2%{?dist}
 License: curl
 Source0: https://curl.se/download/%{name}-%{version_no_tilde}.tar.xz
 Source1: https://curl.se/download/%{name}-%{version_no_tilde}.tar.xz.asc
@@ -22,6 +22,9 @@ Source1: https://curl.se/download/%{name}-%{version_no_tilde}.tar.xz.asc
 # which points to the GPG key as of April 7th 2016 of https://daniel.haxx.se/mykey.asc
 Source2: mykey.asc
 
+# fix multi_fdset must not report only the wakeup socket (#2460719)
+Patch001: 0001-curl-8.21.0~rc3-multi-xfers_really_alive.patch
+
 # patch making libcurl multilib ready
 Patch101: 0101-curl-7.32.0-multilib.patch
 
@@ -256,7 +259,7 @@ printf "609\n610\n611\n612\n613\n614\n615\n616\n617\n618\n" >>tests/data/DISABLE
 printf "619\n620\n621\n622\n623\n624\n625\n626\n627\n628\n" >>tests/data/DISABLED
 printf "629\n630\n631\n633\n634\n635\n636\n637\n638\n639\n" >>tests/data/DISABLED
 printf "640\n641\n642\n656\n664\n665\n" >>tests/data/DISABLED
-printf "1446\n1459\n1583\n2004\n2007\n" >>tests/data/DISABLED
+printf "1446\n1459\n1583\n1725\n2004\n2007\n" >>tests/data/DISABLED
 printf "3021\n3022\n" >>tests/data/DISABLED
 %endif
 
@@ -465,6 +468,9 @@ rm -f ${RPM_BUILD_ROOT}%{_mandir}/man1/wcurl.1*
 %{_libdir}/libcurl.so.4.[0-9].[0-9].minimal
 
 %changelog
+* Thu Jun 18 2026 Jan Macku <jamacku@redhat.com> - 8.21.0~rc3-2
+- fix multi_fdset must not report only the wakeup socket (#2460719)
+
 * Wed Jun 17 2026 Jan Macku <jamacku@redhat.com> - 8.21.0~rc3-1
 - new upstream release candidate
 

                 reply	other threads:[~2026-06-18 10:54 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178178005848.1.15778690615029135378.rpms-curl-b4df4148f935@fedoraproject.org \
    --to=jamacku@redhat.com \
    --cc=git-commits@fedoraproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox