This is the mail archive of the libc-help@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Mtrace shows memory leak because of its own __cxa_atexit call


Hello,

I'm a noob here, but I've found a behaviour in mtrace that I found odd. If a certain number of atexit calls are placed before mtrace() is called, an additional memory allocation is registered and is reported as a leak by the auxiliary command line tool.

I found out that this happens because mtrace configures the malloc hooks before it calls __cxa_atexit, and this function may allocate memory for storing new function references. From what I understood by reading stdlib.h/cxa_atexit.c, __new_exitfn allocates space when registering the 33rd function reference.

I created a simple test.c file (attached) to test it, and it seems to work if I perform 31 calls to atexit before calling mtrace().

I attempted to "fix" the problem by moving the order of operations in the mtrace function. I've attached a patch that only sets the hooks after the __cxa_atexit call. It seems to have solved my problem, but I'm unsure if there are other consequences I can't foresee.

Would this "fix" be acceptable, or would it be better if I worked around the problem in my client program. If a workaround is better, what would be a good approach? I'm thinking of perhaps calling mtrace() followed by muntrace() at the start of the program once to make sure the __cxa_atexit function is called, and consequently preventing the memory leak from appearing in future calls of mtrace().

Thanks in advance for any feedback,

Janito

PS: To test it, I ran 'gcc test.c && ./a.out && cat /tmp/mtrace_test'

PS2: I made the patch from the 'glibc-2.21' tag, but I also tested it with the master branch at commit 'c5a3a509dfddad86b9fef84b46a172f969cb9b4b'
#include <mcheck.h>
#include <stdlib.h>

void test_func() {
}

int main(int argc, char* argv[]) {
    int count;

    if (argc < 2)
	count = 31;
    else
	count = atoi(argv[1]);

    for (; count > 0; --count)
	atexit(&test_func);

    setenv("MALLOC_TRACE", "/tmp/mtrace_test", 0);
    mtrace();
    muntrace();

    return 0;
}
>From a58d9a375c719a73abe882bda4f23c9b333d0664 Mon Sep 17 00:00:00 2001
From: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>
Date: Fri, 1 May 2015 17:15:45 +0000
Subject: [PATCH] Move mtrace exit handler registration

---
 malloc/mtrace.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/malloc/mtrace.c b/malloc/mtrace.c
index df10128..d9bca49 100644
--- a/malloc/mtrace.c
+++ b/malloc/mtrace.c
@@ -313,14 +313,6 @@ mtrace (void)
           malloc_trace_buffer = mtb;
           setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
           fprintf (mallstream, "= Start\n");
-          tr_old_free_hook = __free_hook;
-          __free_hook = tr_freehook;
-          tr_old_malloc_hook = __malloc_hook;
-          __malloc_hook = tr_mallochook;
-          tr_old_realloc_hook = __realloc_hook;
-          __realloc_hook = tr_reallochook;
-          tr_old_memalign_hook = __memalign_hook;
-          __memalign_hook = tr_memalignhook;
 #ifdef _LIBC
           if (!added_atexit_handler)
             {
@@ -330,6 +322,14 @@ mtrace (void)
                             &__dso_handle ? __dso_handle : NULL);
             }
 #endif
+          tr_old_free_hook = __free_hook;
+          __free_hook = tr_freehook;
+          tr_old_malloc_hook = __malloc_hook;
+          __malloc_hook = tr_mallochook;
+          tr_old_realloc_hook = __realloc_hook;
+          __realloc_hook = tr_reallochook;
+          tr_old_memalign_hook = __memalign_hook;
+          __memalign_hook = tr_memalignhook;
         }
       else
         free (mtb);
-- 
1.9.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]