Description
I did this
Compiled and ran the following program:
#include <curl/curl.h>
#include <boost/asio.hpp>
using namespace boost::asio;
int progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
static int progress_callback_times = 0;
++progress_callback_times;
// Close the socket when mstate == MSTATE_CONNECTING
if (progress_callback_times == 2)
{
auto &a = *(ip::tcp::acceptor *)clientp;
auto sock = a.accept();
sock.close();
}
return 0;
}
int main()
{
io_context ctx(1);
ip::tcp::acceptor a(ctx.get_executor());
a.open(ip::tcp::v4());
socket_base::reuse_address ra(true);
a.set_option(ra);
ip::tcp::endpoint e{ip::address_v4::loopback(), 8080};
a.bind(e);
a.listen();
auto easy = curl_easy_init();
curl_easy_setopt(easy, CURLOPT_URL, "http://127.0.0.1:8080");
curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(easy, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(easy, CURLOPT_XFERINFOFUNCTION, progress_callback);
curl_easy_setopt(easy, CURLOPT_XFERINFODATA, &a);
curl_easy_perform(easy);
return 0;
}
I expected the following
The program should exit almost immediately, since it is attempting to communicate with a closed socket
What actually happened
The program consumed an entire CPU core for 5 minutes before the connection timed out.
This only happens on Mac; Linux works fine.
The issue appears to be that when waiting for the socket to be ready, multi_wait
calls poll
with events
set to POLLOUT
, and the OS sets revents
to POLLOUT
and returns immediately. However, when multi_runsingle
calls poll
it sets events
to POLLOUT|POLLPRI
and the OS sets revents
to POLLHUP|POLLPRI
. Curl interprets the POLLHUP
as an error, but then when verifyconnect
checks SO_ERROR
it gets back 0 so it assumes that there wasn't actually an error. Because revents
didn't contain POLLOUT
curl thinks that it needs to wait for the socket to be writable, so it calls multi_wait
and the cycle repeats until the connect timeout elapses.
curl/libcurl version
curl 7.78.0-DEV (Darwin) libcurl/7.78.0-DEV SecureTransport zlib/1.2.11
Release-Date: [unreleased]
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS Debug HSTS IPv6 Largefile libz NTLM SSL TrackMemory UnixSockets
I built curl off of commit bfbde88
operating system
Darwin NYC-L5169-MAC.local 19.5.0 Darwin Kernel Version 19.5.0: Tue May 26 20:41:44 PDT 2020; root:xnu-6153.121.2~2/RELEASE_X86_64 x86_64 i386 MacBookPro16,1 Darwin