Hi Stefan, I was surprised that your PR didn't get into 8.19.0 release. Are there any plans to get it into the master upstream soon and maybe get it in some 8.19.1 follow-up? Or it is too risky to be in 8.19 and it needs more time to settle down?
I am asking because I need to fix the crash with set share in order to migrate our application to 8.19. It crashes in some 3rd party library code, which I can't change. If your change is not ready yet and a bit risky to take into production, then I will probably need to apply a patch with my simple fix in order to use 8.19. Ideally, it would be very helpful to have the fix for set share crash in 8.19, as it eventually may bite some other folks as well. Thanks! Dmitry -----Original Message----- From: curl-library <[email protected]> On Behalf Of Dmitry Karpov via curl-library Sent: Monday, March 9, 2026 12:56 PM To: Stefan Eissing <[email protected]> Cc: Dmitry Karpov <[email protected]>; libcurl development <[email protected]> Subject: [EXTERNAL] Re: Crash in some use case using shared handle with connection sharing in 8.18 and above Hi Stefan, I tried your PR and it works fine. Thanks a lot! Dmitry -----Original Message----- From: Stefan Eissing <[email protected]> Sent: Monday, March 9, 2026 8:14 AM To: Dmitry Karpov <[email protected]> Cc: libcurl development <[email protected]> Subject: [EXTERNAL] Re: Crash in some use case using shared handle with connection sharing in 8.18 and above Dmitry, I took your suggested fix for the connection among some other improvements to shares in https://github.com/curl/curl/pull/20870 Let me know how that work for you. Thanks! - Stefan > Am 03.03.2026 um 20:56 schrieb Dmitry Karpov <[email protected]>: > >> An application can get into lots of trouble when removing a share again from >> an easy handle. I'd recommend not to do that. > > But removing a share may be a part of a more complex code or framework, which > is invoked on certain conditions. > So, it may be not always possible to avoid removing the share when some kind > of cleanup for an easy handle wrapped into some complex code is done. > > And I think that libcurl should handle it gracefully and not crash in such > cases, especially if it worked just fine in the previous libcurl versions. > > Besides, the same crash occurs if a share is not removed from an easy > handle, but replaced by some other share (i.e. client wants to use > connections from some other pool stored in a different share), which is like > using connections from a different multi-handle. > > And the problem is only for the connection sharing option, because the > cleanup code when a new share is set doesn't clear the connection data in > easy handle as it does for the other share options. > Could you please have a look at the fix that I suggested which adds a cleanup > for connection data before a new share is set and let me know if it is OK? > > Thanks, > Dmitry > > > -----Original Message----- > From: Stefan Eissing <[email protected]> > Sent: Monday, March 2, 2026 11:32 PM > To: libcurl development <[email protected]> > Cc: Dmitry Karpov <[email protected]> > Subject: [EXTERNAL] Re: Crash in some use case using shared handle > with connection sharing in 8.18 and above > > An application can get into lots of trouble when removing a share again from > an easy handle. I'd recommend not to do that. > > Cheers, > Stefan > >> Am 03.03.2026 um 07:51 schrieb Dmitry Karpov via curl-library >> <[email protected]>: >> >> Hi All, I discovered a crash case scenario with shared handle with >> connection sharing when trying libcurl 8.18.0 in my application. >> The crash case can be described as follows: >> - client create a share handle to share connections. >> - client creates a multi handle. >> - client creates an easy handle. >> - client sets a share handle in the easy handle. >> - client adds the easy handle to the multi handle. >> - client performs the transfer via the multi-handle until it gets some data. >> NOTE: The download must be long enough, so we break the transfer before it >> is fully done. >> - client sets a null share to the easy handle. >> - client removes the easy handle from the multi handle (via >> curl_multi_remove_handle) - CRASH! >> >> In the debug build the crash happens because of the assert in the >> cpool_remove_conn() function in \lib\conncache.c: >> >> static void cpool_remove_conn(struct cpool *cpool, >> struct connectdata *conn) { …. >> else { >> /* Should have been in the bundle list */ >> DEBUGASSERT(NULL); // Crash happens here!!! >> } >> } >> } >> In the release build it happens at some point after the assertion is ignored >> when trying to remove non-existing connection from the multi-handle >> connection pool. >> >> The reason for the assertion/crash is that when we set a null share >> to the easy handle before calling the curl_multi_remove_handle(), then >> cpool_get_instance() returns the connection pool for the multi-handle while >> the easy handle keep the connection data from the shared handle connection >> pool. >> >> And when the curl_multi_remove_handle() tries to remove the connection data >> from the easy handle, it uses the connection pool from the multi-handle, not >> from the share and asserts/crashes. >> A possible logical fix for this issue would be detaching the connection data >> from the easy handle when a new share handle is set, similar to the other >> easy settings, like: >> >> \lib\setopt.c: >> >> case CURLOPT_SHARE: { >> … >> if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) { >> Curl_resolv_unlink(data, &data->state.dns[0]); >> Curl_resolv_unlink(data, &data->state.dns[1]); >> } >> /* Detaching the connection if the easy handle was using connection >> sharing */ >> if (data->share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) >> Curl_detach_connection(data); … >> } >> It worked in my test, but maybe there is a better solution. >> Here is some small example program that can help to reproduce the crash: >> >> static size_t writeFunc(void* ptr, size_t size, size_t nmemb, >> void* data) { >> (void)ptr; >> bool* data_received = (bool*)data; >> *data_received = true; >> return size * nmemb; >> } >> void setNullShareRemoveTest() { >> // Creating share with connection sharing option. >> CURLSH* share = curl_share_init(); >> curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); >> // Creating an easy handle and set share and other options. >> CURL* easy = curl_easy_init(); >> curl_easy_setopt(easy, CURLOPT_SHARE, share); >> // The URL should be for a long enough download, so the transfer is not >> // completed when the first data chunk is delivered to the write function. >> curl_easy_setopt(easy, CURLOPT_URL, ”http://example.com/file_1MB.bin”); >> bool data_received = false; >> curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, writeFunc); >> curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void*)&data_received); >> curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L); >> // Creating a multi handle. >> CURLM* multi = curl_multi_init(); >> // Run transfer >> curl_multi_add_handle(multi, easy); >> int still_running = 0; >> for (;;) { >> CURLMcode mresult = curl_multi_perform(multi, &still_running); >> if (mresult != CURLM_OK) { >> printf("curl_multi_perform() failed, code %d.\n", >> (int)mresult); >> break; >> } >> if (!still_running || data_received) { >> break; // Break when first data chunk is received or transfer is >> done. >> } >> /* wait for activity, timeout or "nothing" */ >> mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); >> if (mresult != CURLM_OK) { >> printf("curl_multi_poll() failed, code %d.\n", (int)mresult); >> break; >> } >> } /* if there are still transfers, loop */ >> // Set a null share first. >> curl_easy_setopt(easy, CURLOPT_SHARE, NULL); >> // Remove the easy handle after clearing the share. !!! Crash!!! >> curl_multi_remove_handle(multi, easy); // cleanup: >> curl_easy_cleanup(easy); >> curl_multi_cleanup(multi); >> curl_share_cleanup(share); >> printf("\nDone\n"); >> } >> Thanks, >> Dmitry Karpov >> >> >> -- >> Unsubscribe: https://lists.haxx.se/mailman/listinfo/curl-library >> Etiquette: https://curl.se/mail/etiquette.html > > -- Unsubscribe: https://lists.haxx.se/mailman/listinfo/curl-library Etiquette: https://curl.se/mail/etiquette.html -- Unsubscribe: https://lists.haxx.se/mailman/listinfo/curl-library Etiquette: https://curl.se/mail/etiquette.html
