Skip to content

curl multi busyloop bug with smaller socket buffers #21671

@piru

Description

@piru

I did this

/*
  Curl busyloop PoC written by Harry Sintonen <sintonen@iki.fi>
  Public Domain

  My current understanding is that the socket will report as having
  data to receive and thus curl_multi_poll() will always return
  immediately. The socket will be added to the pollset by the
  Curl_ssl_adjust_pollset() since OpenSSL reports SSL_ERROR_WANT_READ.
  curl_multi_perform() sees the connection paused and does nothing
  -> Busyloop

  Largely based on https://curl.se/libcurl/c/multi-app.html
*/
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>

#define HANDLECOUNT 1

size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
  return CURL_WRITEFUNC_PAUSE;
}

int sockopt_callback(void *clientp,
                     curl_socket_t curlfd,
                     curlsocktype purpose)
{
  const int newsize = 32768;
  int rc = 0;
  rc |= setsockopt(curlfd, SOL_SOCKET, SO_RCVBUF, &newsize, sizeof(newsize));
  rc |= setsockopt(curlfd, SOL_SOCKET, SO_SNDBUF, &newsize, sizeof(newsize));

  return rc ? CURL_SOCKOPT_ERROR : CURL_SOCKOPT_OK;
}

int main(void)
{
  CURL *curl[HANDLECOUNT];
  CURLM *multi;
  int i;

  curl_global_init(CURL_GLOBAL_ALL);

  for(i = 0; i < HANDLECOUNT; i++) {
    curl[i] = curl_easy_init();
    //curl_easy_setopt(curl[i], CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_easy_setopt(curl[i], CURLOPT_BUFFERSIZE, 32 * 1024);
    curl_easy_setopt(curl[i], CURLOPT_SOCKOPTFUNCTION, sockopt_callback);
    curl_easy_setopt(curl[0], CURLOPT_URL, "https://curl.se/download/curl-8.20.0.tar.xz");
    curl_easy_setopt(curl[0], CURLOPT_WRITEFUNCTION, write_callback);
  }

  multi = curl_multi_init();
  if(multi) {

    int still_running = 1; /* keep number of running handles */

    for(i = 0; i < HANDLECOUNT; i++)
      curl_multi_add_handle(multi, curl[i]);

    while(still_running) {
      CURLMcode mresult = curl_multi_perform(multi, &still_running);
      //printf("mresult %d still_running %d\n", mresult, still_running);
      if (mresult)
        break;

      if(still_running) {
        mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL);
        //printf("curl_multi_poll mresult %d\n", mresult);
      }
      if(mresult)
        break;
    }

    for(i = 0; i < HANDLECOUNT; i++)
      curl_multi_remove_handle(multi, curl[i]);

    curl_multi_cleanup(multi);
  }

  for(i = 0; i < HANDLECOUNT; i++)
    curl_easy_cleanup(curl[i]);

  curl_global_cleanup();

  return 0;
}

I expected the following

Application not hanging with 100% single core CPU load.

curl/libcurl version

curl 8.20.0
and curl git master

curl 8.21.0-DEV (x86_64-pc-linux-gnu) libcurl/8.21.0-DEV OpenSSL/3.6.2 zlib/1.3.2 brotli/1.2.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.5 nghttp2/1.69.0 OpenLDAP/2.6.10

operating system

Any

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions