memory leak in get_cached_stack ?

Mathieu Lacage mathieu.lacage@gmail.com
Mon Feb 8 19:57:00 GMT 2010


hi,

The attached testcase leaks 400000 bytes:

[mathieu@mathieu-laptop test]$ valgrind --leak-check=full
--show-reachable=yes ./test10
==3381== Memcheck, a memory error detector.
==3381== Copyright (C) 2002-2008, and GNU GPL'd, by Julian Seward et al.
==3381== Using LibVEX rev 1884, a library for dynamic binary translation.
==3381== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP.
==3381== Using valgrind-3.4.1, a dynamic binary instrumentation framework.
==3381== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al.
==3381== For more details, rerun with: -v
==3381==
libtest10 constructor
enter main
main i=1
th i=1
th i=2
main i=1
th i=1
th i=2
main i=1
main i=1
th i=1
th i=2
main i=1
th i=1
th i=2
main i=1
leave main
libtest10 destructor
==3381==
==3381== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 2)
==3381== malloc/free: in use at exit: 800,032 bytes in 3 blocks.
==3381== malloc/free: 30 allocs, 27 frees, 3,206,192 bytes allocated.
==3381== For counts of detected errors, rerun with: -v
==3381== searching for pointers to 3 not-freed blocks.
==3381== checked 494,024 bytes.
==3381==
==3381== 32 bytes in 1 blocks are still reachable in loss record 1 of 3
==3381==    at 0x4A05414: calloc (vg_replace_malloc.c:397)
==3381==    by 0x339E80130F: _dlerror_run (in /lib64/libdl-2.10.2.so)
==3381==    by 0x339E800EE0: dlopen@@GLIBC_2.2.5 (in /lib64/libdl-2.10.2.so)
==3381==    by 0x400A3B: test_one (test10.c:31)
==3381==    by 0x400BA3: main (test10.c:65)
==3381==
==3381==
==3381== 400,000 bytes in 1 blocks are still reachable in loss record 2 of 3
==3381==    at 0x4A05260: memalign (vg_replace_malloc.c:460)
==3381==    by 0x339DC1123F: tls_get_addr_tail (in /lib64/ld-2.10.2.so)
==3381==    by 0x339DC1175F: __tls_get_addr (in /lib64/ld-2.10.2.so)
==3381==    by 0x502E66F: ???
==3381==    by 0x400B1F: test_one (test10.c:49)
==3381==    by 0x400BA8: main (test10.c:66)
==3381==
==3381==
==3381== 400,000 bytes in 1 blocks are definitely lost in loss record 3 of 3
==3381==    at 0x4A05260: memalign (vg_replace_malloc.c:460)
==3381==    by 0x339DC1123F: tls_get_addr_tail (in /lib64/ld-2.10.2.so)
==3381==    by 0x339DC1175F: __tls_get_addr (in /lib64/ld-2.10.2.so)
==3381==    by 0x502E66F: ???
==3381==    by 0x4009DA: thread (test10.c:21)
==3381==    by 0x339EC06859: start_thread (in /lib64/libpthread-2.10.2.so)
==3381==    by 0x339E0DE22C: clone (in /lib64/libc-2.10.2.so)
==3381==
==3381== LEAK SUMMARY:
==3381==    definitely lost: 400,000 bytes in 1 blocks.
==3381==      possibly lost: 0 bytes in 0 blocks.
==3381==    still reachable: 400,032 bytes in 2 blocks.
==3381==         suppressed: 0 bytes in 0 blocks.
[mathieu@mathieu-laptop test]$

I looked a bit in this: it looks like there is a missing call to
_dl_deallocate_tls which makes the dynamic loader leak the memory
pointed to by one of the dtv[i] entries.

When my thread terminates, queue_stack is called by __deallocate_stack
by __free_tcb by start_thread. queue_stack adds the thread data
structure in the free list of stacks and it is then unqueued from this
list upon the next call to pthread_create by get_cached_stack. What
appears to go really wrong here is that get_cached_stack uses memset
to clear the content of the reused dtv and then _dl_allocate_tls_init
to re-initialize it. It looks like the memset should be replaced by
something like _dl_deallocate_tls to give the dynamic loader a chance
to free the entries dtvi points to.

Mathieu
-- 
Mathieu Lacage <mathieu.lacage@gmail.com>
-------------- next part --------------
__thread int g_i[100000] = {1, 1};

int * get_i (void)
{
  return &g_i[0];
}
-------------- next part --------------
#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h>
#include "test.h"
LIB(test10)

static int *(*g_get_i) (void) = 0;
static sem_t g_wait_a;
static sem_t g_wait_b;

static void *thread (void *ctx)
{
  int *i = g_get_i ();
  printf ("th i=%d\n", *i);
  *i = 2;
  printf ("th i=%d\n", *i);
  sem_post (&g_wait_a);
  sem_wait (&g_wait_b);
  i = g_get_i ();
  printf ("th i=%d\n", *i);
  *i = 2;
  printf ("th i=%d\n", *i);
  return 0;
}

static void
test_one (void)
{
  void *handle = dlopen ("libi.so", RTLD_LAZY);

  g_get_i = (int *(*) (void)) dlsym (handle, "get_i");
  int *i = g_get_i ();
  printf ("main i=%d\n", *i);

  sem_init (&g_wait_a, 0, 0);
  sem_init (&g_wait_b, 0, 0);
  pthread_attr_t attr;
  pthread_attr_init (&attr);
  pthread_t th;
  pthread_create (&th, &attr, thread, 0);

  sem_wait (&g_wait_a);
  dlclose (handle);
  handle = dlopen ("libi.so", RTLD_LAZY);

  g_get_i = (int *(*) (void)) dlsym (handle, "get_i");
  i = g_get_i ();
  printf ("main i=%d\n", *i);
  sem_post (&g_wait_b);

  pthread_join (th, 0);
  printf ("main i=%d\n", *i);

  dlclose (handle);
}

extern void __libc_freeres (void);

int main (int argc, char *argv[])
{
  printf ("enter main\n");

  test_one ();
  test_one ();

  printf ("leave main\n");

  atexit (__libc_freeres);
  return 0;
}


More information about the Libc-help mailing list