libthread_xu - Clean up the red zone on library unload
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 16 Apr 2014 17:01:14 +0000 (10:01 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 16 Apr 2014 17:01:14 +0000 (10:01 -0700)
* If the main program is not linked against -pthread but dynamically
  loads a shared library that is via dlopen(), then dlclose()'s it,
  libthread_xu leaves red-zone and cached thread stacks dangling.

  The second attempt to (indirectly) load libthread_xu then fails with a
  red-zone panic.

* Add a destructor for libthread_xu to unmap the red-zone and to clean out
  any cached thread stacks.

* Note that neither libthread_xu nor most other large libraries are designed
  to be unloaded.  That is, even if loading/unloading works, numerous libraries
  will almost certainly leak memory.  This change prevents the fatal error but
  doesn't fix the general memory leakage problem.

  IMHO Only libraries designed to be unloadable should be unloaded.

Reported-by: Vasily Postnicov <shamaz.mazum@gmail.com>
lib/libthread_xu/thread/thr_init.c
lib/libthread_xu/thread/thr_private.h
lib/libthread_xu/thread/thr_stack.c

index 2d184a0..96dbc84 100644 (file)
@@ -145,6 +145,7 @@ STATIC_LIB_REQUIRE(_pthread_yield);
 char           *_usrstack;
 struct pthread *_thr_initial;
 int            _thread_scope_system;
+static void    *_thr_main_redzone;
 
 pid_t          _thr_pid;
 size_t         _thr_guard_default;
@@ -153,6 +154,7 @@ size_t              _thr_stack_initial =    THR_STACK_INITIAL;
 int            _thr_page_size;
 
 static void    init_private(void);
+static void    _libpthread_uninit(void);
 static void    init_main_thread(struct pthread *thread);
 static int     init_once = 0;
 
@@ -163,6 +165,13 @@ _thread_init(void)
        _libpthread_init(NULL);
 }
 
+void   _thread_uninit(void) __attribute__ ((destructor));
+void
+_thread_uninit(void)
+{
+       _libpthread_uninit();
+}
+
 /*
  * This function is used by libc to initialise libpthread
  * early during dynamic linking.
@@ -250,6 +259,18 @@ _libpthread_init(struct pthread *curthread)
        }
 }
 
+static void
+_libpthread_uninit(void)
+{
+       if (_thr_initial == NULL)
+               return;
+       if (_thr_main_redzone && _thr_main_redzone != MAP_FAILED) {
+               munmap(_thr_main_redzone, _thr_guard_default);
+               _thr_main_redzone = NULL;
+       }
+       _thr_stack_cleanup();
+}
+
 /*
  * This function and pthread_create() do a lot of the same things.
  * It'd be nice to consolidate the common stuff in one place.
@@ -269,9 +290,10 @@ init_main_thread(struct pthread *thread)
         * resource limits, so this stack needs an explicitly mapped
         * red zone to protect the thread stack that is just beyond.
         */
-       if (mmap(_usrstack - _thr_stack_initial -
-               _thr_guard_default, _thr_guard_default,
-               0, MAP_ANON | MAP_TRYFIXED, -1, 0) == MAP_FAILED) {
+       _thr_main_redzone = mmap(_usrstack - _thr_stack_initial -
+                                _thr_guard_default, _thr_guard_default,
+                                0, MAP_ANON | MAP_TRYFIXED, -1, 0);
+       if (_thr_main_redzone == MAP_FAILED) {
                PANIC("Cannot allocate red zone for initial thread");
        }
 
index f615db2..f5c6b66 100644 (file)
@@ -655,6 +655,7 @@ void        _thr_rtld_init(void);
 void   _thr_rtld_fini(void);
 int    _thr_stack_alloc(struct pthread_attr *);
 void   _thr_stack_free(struct pthread_attr *);
+void   _thr_stack_cleanup(void);
 void   _thr_free(struct pthread *, struct pthread *);
 void   _thr_gc(struct pthread *);
 void    _thread_cleanupspecific(void);
index ee8d2b6..dc277a0 100644 (file)
@@ -266,3 +266,15 @@ _thr_stack_free(struct pthread_attr *attr)
                attr->stackaddr_attr = NULL;
        }
 }
+
+void
+_thr_stack_cleanup(void)
+{
+       struct stack *spare;
+
+       while ((spare = LIST_FIRST(&dstackq)) != NULL) {
+               LIST_REMOVE(spare, qe);
+               munmap(spare->stackaddr,
+                      spare->stacksize + spare->guardsize);
+       }
+}