[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Bug] dlopen() might cause deadlock



Hi all,

When I tried the simple test bellow, I found it blocked sometime.
This test case creates a thread, right after that, it forks a process.
The thread and the child process both call dlopen().

My platform:

SMP system with kernel Linux-2.6.0-test11 
glibc (20031130)
redhat 9.0

Using strace shows that the child process forked blocked on system call futex(..,FUTEX_WAIT..).

It seems the block happens when the forked process calls dlopen(). 

src/elf/dl-open.c: 

498   /* Make sure we are alone.  */
499   __rtld_lock_lock_recursive (GL(dl_load_lock));

The child process try to get the lock, but if the thread is already in dlopen() and has got that lock.
After fork, the lock is already locked. So the child process will block on line 499.

If the lock can be reset when the child process is forked, the block may be removed. Here is a possible
fix (maybe not proper, just a workaround).

-------------------------------------------------------------------------------------
--- fork.c.orig 2003-12-10 21:43:25.906943104 -0500
+++ fork.c      2003-12-10 22:47:49.479590784 -0500
@@ -150,6 +150,9 @@

       /* Reset locks in the I/O code.  */
       _IO_list_resetlock ();
+
+      __rtld_lock_define_initialized_recursive(,_dl_load_lock)
+      memcpy(&GL(dl_load_lock), &_dl_load_lock, sizeof(_dl_load_lock));

       /* Run the handlers registered for the child.  */
       while (allp != NULL)

--------------------------------------------

/* dlopen_test.c */

#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

void * th_fn(void *arg)
{
        char *dso_name = (char *)arg;
        void * handle = dlopen (dso_name, RTLD_NOW | RTLD_GLOBAL);

        if (handle == NULL) {
                fprintf(stderr, "Didn't open lib: %s: %s\n",
                        dso_name, dlerror());
                return (void *)-1;
        }
        dlclose(handle);
        return (void *)0;
}

int main(int argc, char *argv[])
{
        char * dso_name;
        pthread_t child_th;

        if (argc == 1) {
                printf("dlopen_test DSO_TO_LOAD\n");
                exit(-1);
        }

        dso_name = argv[1];
        pthread_create(&child_th, NULL, th_fn, dso_name);
        if (fork() == 0) {
                th_fn(dso_name);
                exit(0);
        }

        pthread_join(child_th, NULL);
        wait(NULL);
        return 0;
} 

I think the block can be reproduced more easily on SMP system. To make it easy to reproduce, here is a script 
executing this test repeatly:

-------------------------------------------------------------------------------

#!/bin/sh
# Script to run the dlopen test case. Since the block
# NOT happen all the time, try to run it repeatly.

DSO_NAME=/lib/libc.so.6

for ((i=0; i<1000; i++))
do
./dlopen_test $DSO_NAME
done

Did I miss anything? Hoping for your comments.

====================================================================
Information above represents only my personal view, not corporate.  
Adam Li  [Li Yi]
Intel China Software Lab
Tel:  86-21-5257-4545-1338
adam li intel com




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]