memdebug: close debug logfile explicitly on exit

- Use atexit to register a dbg cleanup function that closes the logfile.

LeakSantizier (LSAN) calls _exit() instead of exit() when a leak is
detected on exit so the logfile must be closed explicitly or data could
be lost. Though _exit() does not call atexit handlers such as this,
LSAN's call to _exit() comes after the atexit handlers are called.

Prior to this change the logfile was not explicitly closed so it was
possible that if LSAN detected a leak and called _exit (which does
not flush or close files like exit) then the logfile could be missing
data. That could then cause curl's memanalyze to report false leaks
(eg a malloc was recorded to the logfile but the corresponding free was
discarded from the buffer instead of written to the logfile, then
memanalyze reports that as a leak).

Ref: https://github.com/google/sanitizers/issues/1374

Bug: https://github.com/curl/curl/pull/6591#issuecomment-780396541

Closes https://github.com/curl/curl/pull/6620
This commit is contained in:
Jay Satiro 2021-02-17 17:46:16 -05:00
parent 09363500b9
commit eb36c03e83

View File

@ -55,9 +55,24 @@ struct memdebug {
*/
FILE *curl_dbg_logfile = NULL;
static bool registered_cleanup = FALSE; /* atexit registered cleanup */
static bool memlimit = FALSE; /* enable memory limit */
static long memsize = 0; /* set number of mallocs allowed */
/* LeakSantizier (LSAN) calls _exit() instead of exit() when a leak is detected
on exit so the logfile must be closed explicitly or data could be lost.
Though _exit() does not call atexit handlers such as this, LSAN's call to
_exit() comes after the atexit handlers are called. curl/curl#6620 */
static void curl_dbg_cleanup(void)
{
if(curl_dbg_logfile &&
curl_dbg_logfile != stderr &&
curl_dbg_logfile != stdout) {
fclose(curl_dbg_logfile);
}
curl_dbg_logfile = NULL;
}
/* this sets the log file name */
void curl_dbg_memdebug(const char *logname)
{
@ -72,6 +87,8 @@ void curl_dbg_memdebug(const char *logname)
setbuf(curl_dbg_logfile, (char *)NULL);
#endif
}
if(!registered_cleanup)
registered_cleanup = !atexit(curl_dbg_cleanup);
}
/* This function sets the number of malloc() calls that should return