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 if OpenSSL is linked statically then it
  may require thread cleanup.

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.

Co-authored-by: Viktor Szakats

Reported-by: southernedge@users.noreply.github.com
Reported-by: zmcx16@users.noreply.github.com

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

Fixes #12327
Closes #xxxx
  • Loading branch information
jay committed Apr 13, 2024
1 parent 49f83c3 commit 31f5c76
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 11 deletions.
3 changes: 3 additions & 0 deletions docs/libcurl/curl_global_cleanup.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ 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 if it is linked statically. Refer to
libcurl-thread(3) for more information.
# EXAMPLE
~~~c
Expand Down
8 changes: 8 additions & 0 deletions docs/libcurl/libcurl-thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ 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.

If OpenSSL is linked statically it may require [thread cleanup]
(https://www.openssl.org/docs/man3.0/man3/OPENSSL_thread_stop.html#NOTES). For
example, in Windows if both libcurl and OpenSSL are linked statically to a DLL
or application then OpenSSL may leak memory unless the DLL or application calls
OPENSSL_thread_stop() before each thread terminates. If libcurl is built as a
DLL and OpenSSL is linked statically to it then libcurl will automatically call
OPENSSL_thread_stop() on thread termination (added in libcurl 8.7.0).

# Signals

Signals are used for timing out name resolves (during DNS lookup) - when built
Expand Down
33 changes: 22 additions & 11 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ configure_file(curl_config.h.cmake
transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake)

list(APPEND HHEADERS
${CMAKE_CURRENT_BINARY_DIR}/curl_config.h
)
# DllMain is added later for DLL builds only.
list(REMOVE_ITEM CSOURCES dllmain.c)

list(APPEND HHEADERS ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h)

# The rest of the build

Expand Down Expand Up @@ -114,17 +115,16 @@ if(NOT DEFINED SHARE_LIB_OBJECT)
endif()
endif()

if(WIN32)
# Define CURL_STATICLIB always, to disable __declspec(dllexport) for exported
# libcurl symbols. We handle exports via libcurl.def instead. Except with
# symbol hiding disabled or debug mode enabled, when we export _all_ symbols
# from libcurl DLL, without using libcurl.def.
add_definitions("-DCURL_STATICLIB")
endif()

if(SHARE_LIB_OBJECT)
set(LIB_OBJECT "libcurl_object")
add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES})
if(WIN32)
# Define CURL_STATICLIB always, to disable __declspec(dllexport) for exported
# libcurl symbols. We handle exports via libcurl.def instead. Except with
# symbol hiding disabled or debug mode enabled, when we export _all_ symbols
# from libcurl DLL, without using libcurl.def.
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
endif()
target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS})
set_target_properties(${LIB_OBJECT} PROPERTIES
POSITION_INDEPENDENT_CODE ON)
Expand Down Expand Up @@ -152,6 +152,9 @@ if(BUILD_STATIC_LIBS)
list(APPEND libcurl_export ${LIB_STATIC})
add_library(${LIB_STATIC} STATIC ${LIB_SOURCE})
add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC})
if(WIN32)
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
endif()
target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS})
# Remove the "lib" prefix since the library is already named "libcurl".
set_target_properties(${LIB_STATIC} PROPERTIES
Expand Down Expand Up @@ -181,6 +184,14 @@ if(BUILD_SHARED_LIBS)
list(APPEND libcurl_export ${LIB_SHARED})
add_library(${LIB_SHARED} SHARED ${LIB_SOURCE})
add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
if(WIN32 OR CYGWIN)
if(CYGWIN)
# For cygwin always compile dllmain.c as a separate unit since it includes
# windows.h, which shouldn't be included in other units.
set_source_files_properties(dllmain.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
endif()
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES dllmain.c)
endif()
if(WIN32)
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES libcurl.rc)
if(HIDES_CURL_PRIVATE_SYMBOLS)
Expand Down
1 change: 1 addition & 0 deletions lib/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ LIB_CFILES = \
curl_trc.c \
cw-out.c \
dict.c \
dllmain.c \
doh.c \
dynbuf.c \
dynhds.c \
Expand Down
83 changes: 83 additions & 0 deletions lib/dllmain.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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"

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

/* The fourth-to-last include */
#ifdef __CYGWIN__
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef _WIN32
#undef _WIN32
#endif
#endif

/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"

/*
* DllMain() must only be defined for Windows and Cygwin DLL builds. For most
* build systems that means CURL_STATICLIB is not defined.
*/

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

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

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:
MessageBoxA(NULL, "test", "", 0);
#if defined(USE_OPENSSL) && \
!defined(OPENSSL_IS_AWSLC) && \
!defined(OPENSSL_IS_BORINGSSL) && \
!defined(LIBRESSL_VERSION_NUMBER) && \
(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 /* DLL build */

0 comments on commit 31f5c76

Please sign in to comment.