This is the mail archive of the libc-hacker@sources.redhat.com mailing list for the glibc project.

Note that libc-hacker is a closed list. You may look at the archives of this list, but subscription and posting are not open.


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

linuxthread race condition


There is a race condition in linuxthreads during thread creation on
architectures that use a dedicated thread register.  Between the point the
thread is created and inserted in the list of active threads, and the
point the thread register is initialized in the child a cancel signal may
arrive, with the thread manager calling pthread_handle_exit.  The newly
created thread now calls pthread_handle_sigcancel, which uses thread_self
to find itself.  But since the thread register is not yet initialized it
still points to the manager thread, and the signal handler falls trough to
__pthread_manager_sighandler.  Now if main is calling exit without joining
the threads it is waiting for all threads to terminate, but if the new
thread goes on waiting on a condition or similar it will never be able to
exit.

To fix this race I have blocked the cancel signal in the manager around
the clone call, so that the child does not call the signal handler before
thread_self is initialized.

An easy way to reproduce the race is to insert a sleep in
pthread_start_thread before INIT_THREAD_SELF.

Andreas.

2001-11-20  Andreas Schwab  <schwab@suse.de>

	* manager.c (pthread_handle_create): Make sure the cancel signal
	is initially blocked in the child so that it can initialize
	thread_self before the signal is handled.

--- linuxthreads/manager.c.~1.73.~	Wed Sep 12 15:09:52 2001
+++ linuxthreads/manager.c	Tue Nov 20 22:55:36 2001
@@ -611,6 +611,13 @@
       if ((mask & (__pthread_threads_events.event_bits[idx]
 		   | event_maskp->event_bits[idx])) != 0)
 	{
+	  sigset_t newmask, oldmask;
+
+	  /* Block cancel signal in the child until it is fully
+	     initialized.  */
+	  sigemptyset(&newmask);
+	  sigaddset(&newmask, __pthread_sig_cancel);
+	  sigprocmask(SIG_BLOCK, &newmask, &oldmask);
 	  /* Lock the mutex the child will use now so that it will stop.  */
 	  __pthread_lock(new_thread->p_lock, NULL);
 
@@ -638,6 +645,8 @@
 			CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
 			__pthread_sig_cancel, new_thread);
 #endif
+	  if (pid != 0)
+	    sigprocmask(SIG_SETMASK, &oldmask, NULL);
 	  if (pid != -1)
 	    {
 	      /* Now fill in the information about the new thread in
@@ -663,6 +672,13 @@
     }
   if (pid == 0)
     {
+      sigset_t newmask, oldmask;
+
+      /* Block cancel signal in the child until it is fully
+	 initialized.  */
+      sigemptyset(&newmask);
+      sigaddset(&newmask, __pthread_sig_cancel);
+      sigprocmask(SIG_BLOCK, &newmask, &oldmask);
 #ifdef NEED_SEPARATE_REGISTER_STACK
       pid = __clone2(pthread_start_thread,
 		     (void **)new_thread_bottom,
@@ -678,6 +694,8 @@
 		    CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
 		    __pthread_sig_cancel, new_thread);
 #endif /* !NEED_SEPARATE_REGISTER_STACK */
+      if (pid != 0)
+	sigprocmask(SIG_SETMASK, &oldmask, NULL);
     }
   /* Check if cloning succeeded */
   if (pid == -1) {

-- 
Andreas Schwab                                  "And now for something
Andreas.Schwab@suse.de				completely different."
SuSE Labs, SuSE GmbH, Schanzäckerstr. 10, D-90443 Nürnberg
Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5


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