[RFC] Race in _dl_open()

suzuki suzuki@in.ibm.con
Wed Oct 18 19:32:00 GMT 2006


hi,

We haven't had the luck to reproduce this with a simple test case :( .. 
We are still working on it.

Btw, do you see the possibility of the kind of race pointed out here ? 
Also is this the proper way to fix it ?

Thanks,

Suzuki

suzuki wrote:
> Hi,
> 
> One of our application was getting terminated with the following error
> 
>  "Inconsistency detected by ld.so: dl-open.c: 610: _dl_open: Assertion 
> `_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT' failed!
> 
> with glibc-2.4.31. This race seems to be present in the libc I got from 
> the CVS [at code inspection]. We were able to reproduce this 
> consistently after running for 4-5hrs.
> 
> Upon debugging we found that it is due to a race between two threads 
> doing a _dl_open().
> 
> The scenario is something like this :
> 
> In elf/dl-open.c, _dl_open:
> 
> 
>   /* Make sure we are alone.  */
>   __rtld_lock_lock_recursive (GL(dl_load_lock));
> 
> [...]
> 
>   int errcode = _dl_catch_error (&objname, &errstring, &malloced,
>                                  dl_open_worker, &args);
> #ifndef MAP_COPY
>   /* We must munmap() the cache file.  */
>   _dl_unload_cache ();
> #endif
> 
>   /* Release the lock.  */
>   __rtld_lock_unlock_recursive (GL(dl_load_lock));
> 
> ^^^^^ This would kick any other thread waiting on the lock.
> 
> 
> if (__builtin_expect (errstring != NULL, 0))
>   {
>      [...]
>   }
> 
> assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
> 
> And, if the thread which gets woken up is playing with the same 
> namespace, and sets the r_state to RT_ADD in _dl_map_object_from_fd even 
> before we reach here (truly possible in an SMP system),  ( due to 
> getting scheduled out ), we would hit the assert !
> 
> In our case we found that the r_state was RT_ADD and which was set by 
> another thread.
> 
> So, it is not safe to believe that the r_state won't get changed once we 
> release the lock.
> 
> We have tried the following solution for this issue, and it works fine.
> 
> Solution: Ensure the consistency in case of a successful dl_open_worker, 
> before releasing the lock ! Our solution ignored the case where 
> dl_open_worker may fail.
> 
> Attached patch addresses all the issues and we think this may be a 
> complete solution for the problem.
> 
> 1) For successful load by dl_open_worker or a failure where we don't 
> have to remove any object from memory ( i.e, we don't have to do a 
> _dl_close to ensure the consistency ), the assertion is performed before 
> unlocking.
> 
> 2) For the latter case, where we have to do a _dl_close(), the assertion 
> is done just after the _dl_close.
> 
> 
> 
> Comments ?
> 
> 
> Thanks,
> 
> Suzuki K P <suzuki@in.ibm.com>
> Linux Technology Center,
> IBM Systems & Technology Labs.
> 
> 
> 
> 
> ------------------------------------------------------------------------
> 
> Index: glibc-2.4/elf/dl-open.c
> ===================================================================
> --- glibc-2.4.orig/elf/dl-open.c	2006-10-03 11:10:22.000000000 -0700
> +++ glibc-2.4/elf/dl-open.c	2006-10-10 15:28:33.000000000 -0700
> @@ -554,6 +554,16 @@
>    _dl_unload_cache ();
>  #endif
>  
> +  /* Check the consistency state, for a successful load, before we release
> +     the lock. Somebody else might come up and modify the states once we 
> +     release this lock. In case of the failures, where we need a _dl_close,
> +     consistency is achieved only after the _dl_close. So for that case
> +     we delay the check until we finish _dl_close.  */
> +  if (__builtin_expect (errstring == NULL, 1) 
> +      || args.map == NULL)
> +    assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
> +    
> +
>    /* Release the lock.  */
>    __rtld_lock_unlock_recursive (GL(dl_load_lock));
>  
> @@ -564,7 +574,8 @@
>        size_t len_errstring;
>  
>        /* Remove the object from memory.  It may be in an inconsistent
> -	 state if relocation failed, for example.  */
> +	 state if relocation failed, for example. Enusre the conistent
> +	 state.  */
>        if (args.map)
>  	{
>  #ifdef USE_TLS
> @@ -580,6 +591,7 @@
>  #endif
>  
>  	  _dl_close (args.map);
> +          assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
>  	}
>  
>        /* Make a local copy of the error string so that we can release the
> @@ -601,13 +613,9 @@
>        if (malloced)
>  	free ((char *) errstring);
>  
> -      assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
> -
>        /* Reraise the error.  */
>        _dl_signal_error (errcode, objname, NULL, local_errstring);
> -    }
> -
> -  assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
> +    } 
>  
>  #ifndef SHARED
>    DL_STATIC_INIT (args.map);



More information about the Libc-alpha mailing list