diff --git a/curl.c b/curl.c index 1602593..e7ce1cf 100644 --- a/curl.c +++ b/curl.c @@ -9,6 +9,7 @@ typedef struct { + volatile HMODULE hPinnedModule; CRITICAL_SECTION csLock; CHAR szVersion[64]; CHAR szUserAgent[128]; @@ -185,7 +186,6 @@ ULONG CurlInitialize() _snprintf( g_Curl.szUserAgent, ARRAYSIZE( g_Curl.szUserAgent ), "nscurl/%s", g_Curl.szVersion ); } - curl_global_init( CURL_GLOBAL_ALL ); return ERROR_SUCCESS; } @@ -196,7 +196,41 @@ void CurlDestroy() TRACE( _T( "%hs()\n" ), __FUNCTION__ ); DeleteCriticalSection( &g_Curl.csLock ); - curl_global_cleanup(); +} + + +//++ CurlInitializeLibcurl +ULONG CurlInitializeLibcurl() +{ + if (InterlockedCompareExchangePointer( &g_Curl.hPinnedModule, NULL, NULL ) == NULL) { + + TCHAR szPath[MAX_PATH]; + TRACE( _T( "%hs()\n" ), __FUNCTION__ ); + + // Initialize libcurl + curl_global_init( CURL_GLOBAL_ALL ); + + //! CAUTION: + //? https://curl.haxx.se/libcurl/c/curl_global_cleanup.html + //? curl_global_cleanup does not block waiting for any libcurl-created threads + //? to terminate (such as threads used for name resolving). If a module 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. + + // It's confirmed that NScurl.dll crashes when unloaded after curl_global_cleanup() gets called + // Easily reproducible in XP and Vista + //! We'll pin it in memory forever and never call curl_global_cleanup() + if (GetModuleFileName( g_hInst, szPath, ARRAYSIZE( szPath ) ) > 0) { + g_Curl.hPinnedModule = LoadLibrary( szPath ); + assert( g_Curl.hPinnedModule ); + } + + // kernel32!GetModuleHandleEx is available in XP+ + //x GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)__FUNCTION__, (HMODULE*)&g_Curl.hPinnedModule ); + } + + return ERROR_SUCCESS; } diff --git a/curl.h b/curl.h index cfd2b74..287e457 100644 --- a/curl.h +++ b/curl.h @@ -148,6 +148,9 @@ ULONG CurlRequestErrorCode( _In_ PCURL_REQUEST pReq ); //? e.g. 404 //+ Initialization ULONG CurlInitialize(); void CurlDestroy(); + +ULONG CurlInitializeLibcurl(); //! Must be called only when starting transfers. This will lock NScurl.dll in memory until the process exists + ULONG CurlExtractCacert(); /// Called automatically //+ CurlParseRequestParam diff --git a/main.c b/main.c index 7f19dc8..66bc71a 100644 --- a/main.c +++ b/main.c @@ -235,9 +235,13 @@ void __cdecl http( HWND parent, int string_size, TCHAR *variables, stack_t **sta TRACE( _T( "%s!%hs\n" ), PLUGINNAME, __FUNCTION__ ); - // Lock the plugin in memory + // Lock the plugin in memory (against NSIS framework trying to unload it) extra->RegisterPluginCallback( g_hInst, UnloadCallback ); + // Lock the plugin in memory (against Windows itself trying to FreeLibrary it) + // Once curl_global_init gets called, the current module must never unload + CurlInitializeLibcurl(); + // Working structures psz = (LPTSTR)MyAlloc( string_size * sizeof(TCHAR) ); pReq = (PCURL_REQUEST)MyAlloc( sizeof( *pReq ) ); diff --git a/resource.rc b/resource.rc index 69c6088..e22cd9e 100644 --- a/resource.rc +++ b/resource.rc @@ -51,7 +51,7 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2020,6,6 + FILEVERSION 1,2020,6,21 PRODUCTVERSION 1,0,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG @@ -69,7 +69,7 @@ BEGIN BEGIN VALUE "CompanyName", "Marius Negrutiu (marius.negrutiu@protonmail.com)" VALUE "FileDescription", "NSIS libcurl plugin" - VALUE "FileVersion", "1.2020.6.6" + VALUE "FileVersion", "1.2020.6.21" VALUE "InternalName", "NScurl.dll" VALUE "LegalCopyright", "(C)2019-present, Marius Negrutiu. All rights reserved." VALUE "LegalTrademarks", "https://github.com/negrutiu/nsis-nscurl"