Skip to content

Commit

Permalink
dllmain: Call OpenSSL thread cleanup for Windows and Cygwin
Browse files Browse the repository at this point in the history
- Call OPENSSL_thread_stop on thread termination (DLL_THREAD_DETACH)
  to prevent a memory leak in case OpenSSL is linked statically.

- Warn in libcurl-thread.3 that in some cases OpenSSL may need a call to
  OPENSSL_thread_stop before thread termination in order to stop a
  memory leak.

OpenSSL may need per-thread cleanup to stop a memory leak. For Windows
and Cygwin if libcurl was built as a DLL then we can do that for the
user by calling OPENSSL_thread_stop on thread termination. However, if
libcurl was built statically then we do not have notification of thread
termination and cannot do that for the user.

Also, there are several other unusual cases where it may be necessary
for the user to call OPENSSL_thread_stop, so in the libcurl-thread
warning I added a link to the OpenSSL documentation.

Reported-by: [email protected]
Reported-by: [email protected]

Ref: https://www.openssl.org/docs/man3.0/man3/OPENSSL_thread_stop.html#NOTES

Fixes curl#12327
Closes #xxxx
  • Loading branch information
jay committed Nov 28, 2023
1 parent 1b04dfa commit 3b2522f
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 4 deletions.
3 changes: 3 additions & 0 deletions docs/libcurl/curl_global_cleanup.3
Expand Up @@ -56,6 +56,9 @@ containing libcurl is dynamically unloaded while libcurl-created threads are
still running then your program may crash or other corruption may occur. We
recommend you do not run libcurl from any module that may be unloaded
dynamically. This behavior may be addressed in the future.

OpenSSL may require thread cleanup in some unusual cases so that it won't leak
memory. Refer to \fIlibcurl-thread(3)\fP.
.SH EXAMPLE
.nf
curl_global_init(CURL_GLOBAL_DEFAULT);
Expand Down
20 changes: 16 additions & 4 deletions docs/libcurl/libcurl-thread.3
Expand Up @@ -42,10 +42,22 @@ interface but you must provide your own locking and set
Note that some items are specifically documented as not thread-safe in the
share API (the connection pool and HSTS cache for example).
.SH TLS
All current TLS libraries libcurl supports are thread-safe. OpenSSL 1.1.0+ can
be safely used in multi-threaded applications provided that support for the
underlying OS threading API is built-in. For older versions of OpenSSL, the
user must set mutex callbacks.
All current TLS libraries libcurl supports are thread-safe.

OpenSSL 1.1.0+ can be safely used in multi-threaded applications provided that
support for the underlying OS threading API is built-in. For older versions of
OpenSSL, the user must set mutex callbacks.

OpenSSL may require thread cleanup in some unusual cases so that it won't leak
memory. The most significant case of this is if OpenSSL was linked statically.
For example, in Windows if both libcurl and OpenSSL are linked statically to a
DLL or application then OpenSSL will leak memory unless the DLL or application
calls OPENSSL_thread_stop() before each thread terminates. However, if libcurl
is built as a DLL and OpenSSL is linked statically to it then since libcurl
8.5.0 it will call OPENSSL_thread_stop() for you. Otherwise, libcurl cannot
call it for you and it is up to the DLL or application. There are several other
cases that are unusual. Refer to OpenSSL documentation:
https://www.openssl.org/docs/man3.0/man3/OPENSSL_thread_stop.html#NOTES
.SH "Signals"
Signals are used for timing out name resolves (during DNS lookup) - when built
without using either the c-ares or threaded resolver backends. On systems that
Expand Down
6 changes: 6 additions & 0 deletions lib/CMakeLists.txt
Expand Up @@ -63,6 +63,12 @@ if(ENABLE_CURLDEBUG)
set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
endif()

if(CYGWIN)
# for cygwin compile dllmain.c separately since it includes windows.h, which
# shouldn't be included for other units.
set_source_files_properties(dllmain.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
endif()

if(BUILD_TESTING)
target_link_libraries(curlu PRIVATE ${CURL_LIBS})
endif()
Expand Down
1 change: 1 addition & 0 deletions lib/Makefile.inc
Expand Up @@ -134,6 +134,7 @@ LIB_CFILES = \
curl_threads.c \
curl_trc.c \
dict.c \
dllmain.c \
doh.c \
dynbuf.c \
dynhds.c \
Expand Down
67 changes: 67 additions & 0 deletions lib/dllmain.c
@@ -0,0 +1,67 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <[email protected]>, et al.
*
* 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 "curl_setup.h"

#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(CURL_STATICLIB)

/* WARNING: Including Cygwin windows.h may define _WIN32 in old versions or
may have unexpected behavior in unity builds (where all source files are
bundled into a single unit). For that reason, this source file must be
compiled separately for Cygwin unity builds. */

#ifdef __CYGWIN__
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

#ifdef USE_OPENSSL
#include <openssl/crypto.h>
#endif

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
(void)hinstDLL;
(void)lpvReserved;

switch(fdwReason) {
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
#if defined(USE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
/* Call OPENSSL_thread_stop to prevent a memory leak in case OpenSSL is
linked statically.
https://github.com/curl/curl/issues/12327#issuecomment-1826405944 */
OPENSSL_thread_stop();
#endif
break;
}
return TRUE;
}

#endif /* (_WIN32 || __CYGWIN__) && !CURL_STATICLIB */

0 comments on commit 3b2522f

Please sign in to comment.