-
-
Notifications
You must be signed in to change notification settings - Fork 6.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
libcurl.dll memory leak #12327
Comments
Please provide a stand-alone source code file that reproduces the problem, ideally without Windows specific code so that we can try it elsewhere. You say it leaks in any libcurl version and I find it hard to believe that you tried most of them. Which versions did you use? |
For this test I used 8.4.0 |
If you cannot make a smaller and less Windows-centric recipe (I don't use Windows), then perhaps you can instead give us much more details about this claimed leak. What memory is leaking? Where is it allocated? How much? (We test for memory leaks extensively, in all versions and all commits all the time.) |
You do not seems to call CInet::~CInet( void )
{
} should at least call |
I cannot give you an exact answer to your questions. Memory is leaking inside the dll module. The Visual Studio debugger does not show memory leaks inside external dll modules. The memory leak problem is not noticeable if the command line curl.exe version is running - file downloaded and curl.exe exits. |
|
It's not an easily read program. I did not spot a problem. |
Step by step it looks like this:
|
static size_t WriteCallback( void *contents, size_t size, size_t nmemb, void *userp )
{
WriteCallback_param *pParam = (WriteCallback_param*)userp;
if ( pParam == NULL ) return 0;
pParam->m_read += size * nmemb;
if ( pParam->m_szMaxData > 0 && pParam->m_szMaxData < pParam->m_read ) return 0;
return size * nmemb;
} This callback function does not save anything inside the program, but there is a gradual increase in the memory. |
Dear Windows users, |
If so, then if you just edit the simple.c example and add your callback in that in a single transfer in a single thread, that also leaks memory? |
|
Here is my test code. Difficult to do a test for downloading a large file. I took a small 1.3MB file for the test. I can not explain it. static size_t WriteCallback( void *contents, size_t size, size_t nmemb, void *userp )
{
return size * nmemb;
}
UINT WINAPI OnThread_Download( PVOID pData )
{
CURL *curl;
CURLcode res;
curl = CInet::curl_easy_init();
if(curl) {
CInet::curl_easy_setopt(curl, CURLOPT_URL, "https://download.sysinternals.com/files/DebugView.zip" );
/* example.com is redirected, so we tell libcurl to follow redirection */
CInet::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
res = CInet::curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, WriteCallback ); _ASSERTE( res == CURLE_OK );
/* Perform the request, res will get the return code */
res = CInet::curl_easy_perform(curl);
/* always cleanup */
CInet::curl_easy_cleanup(curl);
}
return 0;
}
int main(void)
{
if ( CInet::Init() == FALSE )
return 0;
int nDownloads = 0;
while ( 1 )
{
printf( "Start=%d ", nDownloads + 1 );
HANDLE hThread = ::CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)OnThread_Download, nullptr, 0, NULL );
DWORD dwObject = WaitForSingleObject( hThread, INFINITE );
CloseHandle( hThread );
printf( "Downloads=%d\n", ++nDownloads );
//Sleep( 5000 );
}
return 0;
} I've edited the code-formatting. It looked like shit. Better? |
If I comment out this line, there is no memory leak. |
I don't believe there is a memory-leak in curl when running this program, but I also cannot exactly explain the growth. (Based on the fact that it is so simple and there are literally many thousands of users using this simple transfers without seeing any leaks, include all the testing we do ourselves.) If you run it 2000 laps, does it then grow the size by 10MB so it is consistently 5KB per transfer? Is there no valgrind-like tool on Windows to pinpoint a memory leak? |
Which TLS backend does your curl version use and which version of the library? |
Could you upload your test project for test? |
Sure @southernedg |
Thanks, but unfortunately I can't load your library. |
I set 5KB transfer (curl_easy_setopt( curl, CURLOPT_BUFFERSIZE, 5000 ) ). This did not affect the result in any way.
Unfortunately, I don't see a Windows tool that shows memory allocation inside an external library. |
8.4 - memory leak (my current curl dll) |
Unfortunately, I don't see a Windows tool that shows memory allocation inside
an external library.
Why not link the application with the static curl library?
|
Because easy to find a dll curl library, but a static library for linking in MS Visual Studio is difficult. |
I tried this and there was no leaks. D:\curl-8.4.0\winbuild>nmake /f Makefile.vc mode=dll VC=14 ENABLE_UNICODE=yes DEBUG=yes
|
- Support Windows CRT heap memory tracking (_CRTDBG_MAP_ALLOC defined) as an alternative to libcurl heap memory tracking (CURLDEBUG defined). This commit is to demonstrate how it's possible to use Windows CRT heap memory tracking to detect leaks in libcurl *and* the application, with filenames and line numbers recorded for the leaks. This is not intended to go upstream, it was written to further the discussion. See winbuild\README_HEAP_DEBUG.md for instructions. Ref: curl#12327 Ref: https://github.com/jay/curl/blob/crtdbg/winbuild/README_HEAP_DEBUG.md
I've created a branch jay:curl:crtdbg that makes it possible to use Windows CRT heap memory tracking to detect leaks in libcurl and the application, with filenames and line numbers recorded for the leaks. Instructions are in winbuild/README_HEAP_DEBUG.md. If you enable Windows CRT heap memory tracking without using that branch then the CRT will not show the filename or line numbers if the leak is from libcurl. Your _TestCurlLeakMem.cpp sets _CrtSetDbgFlag which overrides the default flags. That means |
I did more research.
Presumably the build with the WITH_SSL key causes a memory leak. I use the latest openssl-3.2.0. Can you comment on this and what else could I check to understand the problem? |
Possibly related #5282. In short if you use OpenSSL as a static library then you may have to call OPENSSL_thread_stop: "Similarly this message will also not be sent if OpenSSL is linked statically, and therefore applications using static linking should also call OPENSSL_thread_stop() on each thread. Additionally if OpenSSL is loaded dynamically via LoadLibrary() and the threads are not destroyed until after FreeLibrary() is called then each thread should call OPENSSL_thread_stop() prior to the FreeLibrary() call." |
Thank you for your advise! In order to follow your advice I had to add the following lines to your (curl) code and build it again.
Yes, it seems to work. I don't see any memory leaks right now. Now I have the next question. Can you provide this function are declared in the new implementation of curl? Thanks. |
No. In my opinion this is not a curl issue. If you choose to use static OpenSSL then you are responsible for cleanup. I suppose we could add something to the multithreading document that mentions this. |
The user's project uses curl exclusively (it does not directly use OpenSSL). curl is built with and uses OpenSSL support. It is logical for curl to allow the user to free up OpenSSL resources rather than supply OpenSSL dlls with the user project for the sake of one function OPENSSL_thread_stop. Once again in different words.
I don't like both solutions, but I don't like option 2 more. I'm not sure I explained the side project problem clearly, but it's significant. |
I suggest you take OpenSSL complaints to the OpenSSL team. I don't see any good way for curl to add this call, as it would require that the libcurl source code would need get told if the linking is static or shared. Which would be problematic. |
I asked OpenSSL if OPENSSL_thread_stop can be called multiple times. openssl/openssl#22831 If it can then for Windows we could add a OPENSSL_thread_stop call on thread detach, even if OpenSSL is also a DLL and does the same call on thread detach. In any case if libcurl is a static library and openssl is a static library then there's nothing we can do, it's up to the application or dll that's it's linked to. |
Ok, sad, thank you. |
I've updated #12408 to call OPENSSL_thread_stop on DLL_THREAD_DETACH and add a warning to libcurl-thread explaining that OPENSSL_thread_stop may be required in some unusual cases. |
@jay do you still intend to take this issue somewhere? |
Yes. The PR took more time than I expected so I moved on to other things. I still like it though and will try Viktor's suggestions. |
- 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 curl#12327 Closes #xxxx
I did this
Hello, I would like to contact the developers.
I noticed a memory leak in libcurl.dll (libcurl-x64.dll) a long time ago. This is version independent.
Here is a small MS Visual Studio C test project showing this problem.
Downloading a file occurs in an endless loop. To speed up the manifestation of memory leaks, the download occurs in 64 threads.
Leave the program running for 15-30 minutes and you will see how memory consumption will increase many times over.
If there is my error and I do not release curl resources in a timely manner, then please suggest a solution to this problem.
Thank you very much for your work, attention and time spent!
I expected the following
No response
curl/libcurl version
8.4.0
operating system
Windows any
The text was updated successfully, but these errors were encountered: