Skip to content

Commit

Permalink
asyn-thread: fix curl_global_cleanup crash in Windows
Browse files Browse the repository at this point in the history
- Make sure that asynchronous resolves handled by Winsock are stopped
  before WSACleanup is called.

This is implemented by ensuring that when Curl_resolver_kill is called
(eg via multi_done) it will cancel the Winsock asynchronous resolve and
wait for the cancellation to complete. Winsock runs the asynchronous
completion routine immediately when a resolve is canceled.

Prior to this change it was possible that during curl_global_cleanup
"a DNS resolver thread created by GetAddrInfoExW did not terminate yet,
however curl is already shutting down, deinitializing Winsock with
WSACleanup() leading to an access violation."

Background:

If libcurl is built with the asynchronous threaded resolver option for
Windows then it resolves in one of two ways. For Windows 8.1 and later,
libcurl resolves by using the Winsock asynchronous resolver which does
its own thread management. For older versions of Windows, libcurl
resolves by creating a separate thread that calls getaddrinfo. This
change only affects the former and it's already handled for the latter.

Reported-by: Ch40zz@users.noreply.github.com

Fixes #13509
Closes #13518
  • Loading branch information
pps83 authored and jay committed May 7, 2024
1 parent 62ae1f1 commit 428579f
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 4 deletions.
19 changes: 16 additions & 3 deletions lib/asyn-thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -554,18 +554,23 @@ static void destroy_async_data(struct Curl_async *async)

if(!done) {
#ifdef _WIN32
if(td->complete_ev)
if(td->complete_ev) {
CloseHandle(td->complete_ev);
else
td->complete_ev = NULL;
}
#endif
Curl_thread_destroy(td->thread_hnd);
if(td->thread_hnd != curl_thread_t_null) {
Curl_thread_destroy(td->thread_hnd);
td->thread_hnd = curl_thread_t_null;
}
}
else {
#ifdef _WIN32
if(td->complete_ev) {
Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
WaitForSingleObject(td->complete_ev, INFINITE);
CloseHandle(td->complete_ev);
td->complete_ev = NULL;
}
#endif
if(td->thread_hnd != curl_thread_t_null)
Expand Down Expand Up @@ -713,6 +718,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
if(td->complete_ev) {
WaitForSingleObject(td->complete_ev, INFINITE);
CloseHandle(td->complete_ev);
td->complete_ev = NULL;
if(entry)
result = getaddrinfo_complete(data);
}
Expand Down Expand Up @@ -754,6 +760,13 @@ void Curl_resolver_kill(struct Curl_easy *data)
/* If we're still resolving, we must wait for the threads to fully clean up,
unfortunately. Otherwise, we can simply cancel to clean up any resolver
data. */
#ifdef _WIN32
if(td && td->complete_ev) {
Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
(void)thread_wait_resolv(data, NULL, FALSE);
}
else
#endif
if(td && td->thread_hnd != curl_thread_t_null
&& (data->set.quick_exit != 1L))
(void)thread_wait_resolv(data, NULL, FALSE);
Expand Down
3 changes: 2 additions & 1 deletion lib/curl_threads.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ curl_thread_t Curl_thread_create(unsigned int (CURL_STDCALL *func) (void *),

void Curl_thread_destroy(curl_thread_t hnd)
{
CloseHandle(hnd);
if(hnd != curl_thread_t_null)
CloseHandle(hnd);
}

int Curl_thread_join(curl_thread_t *hnd)
Expand Down

0 comments on commit 428579f

Please sign in to comment.