00db10
commit 353683a22ed8a493a6bd1d78d63e144bc3e85d2f
00db10
Author: Torvald Riegel <triegel@redhat.com>
00db10
Date:   Thu Dec 15 16:06:28 2016 +0100
00db10
00db10
    Robust mutexes: Fix lost wake-up.
00db10
    
00db10
    Assume that Thread 1 waits to acquire a robust mutex using futexes to
00db10
    block (and thus sets the FUTEX_WAITERS flag), and is unblocked when this
00db10
    mutex is released.  If Thread 2 concurrently acquires the lock and is
00db10
    killed, Thread 1 can recover from the died owner but fail to restore the
00db10
    FUTEX_WAITERS flag.  This can lead to a Thread 3 that also blocked using
00db10
    futexes at the same time as Thread 1 to not get woken up because
00db10
    FUTEX_WAITERS is not set anymore.
00db10
    
00db10
    The fix for this is to ensure that we continue to preserve the
00db10
    FUTEX_WAITERS flag whenever we may have set it or shared it with another
00db10
    thread.  This is the same requirement as in the algorithm for normal
00db10
    mutexes, only that the robust mutexes need additional handling for died
00db10
    owners and thus preserving the FUTEX_WAITERS flag cannot be done just in
00db10
    the futex slowpath code.
00db10
    
00db10
            [BZ #20973]
00db10
            * nptl/pthread_mutex_lock.c (__pthread_mutex_lock_full): Fix lost
00db10
            wake-up in robust mutexes.
00db10
            * nptl/pthread_mutex_timedlock.c (pthread_mutex_timedlock): Likewise.
00db10
00db10
Index: glibc-2.17-c758a686/nptl/pthread_mutex_lock.c
00db10
===================================================================
00db10
--- glibc-2.17-c758a686.orig/nptl/pthread_mutex_lock.c
00db10
+++ glibc-2.17-c758a686/nptl/pthread_mutex_lock.c
00db10
@@ -183,6 +183,11 @@ __pthread_mutex_lock_full (pthread_mutex
00db10
 		     &mutex->__data.__list.__next);
00db10
 
00db10
       oldval = mutex->__data.__lock;
00db10
+      /* This is set to FUTEX_WAITERS iff we might have shared the
00db10
+	 FUTEX_WAITERS flag with other threads, and therefore need to keep it
00db10
+	 set to avoid lost wake-ups.  We have the same requirement in the
00db10
+	 simple mutex algorithm.  */
00db10
+      unsigned int assume_other_futex_waiters = 0;
00db10
       do
00db10
 	{
00db10
 	again:
00db10
@@ -191,9 +196,11 @@ __pthread_mutex_lock_full (pthread_mutex
00db10
 	      /* The previous owner died.  Try locking the mutex.  */
00db10
 	      int newval = id;
00db10
 #ifdef NO_INCR
00db10
+	      /* We are not taking assume_other_futex_waiters into accoount
00db10
+		 here simply because we'll set FUTEX_WAITERS anyway.  */
00db10
 	      newval |= FUTEX_WAITERS;
00db10
 #else
00db10
-	      newval |= (oldval & FUTEX_WAITERS);
00db10
+	      newval |= (oldval & FUTEX_WAITERS) | assume_other_futex_waiters;
00db10
 #endif
00db10
 
00db10
 	      newval
00db10
@@ -254,7 +261,11 @@ __pthread_mutex_lock_full (pthread_mutex
00db10
 		}
00db10
 	    }
00db10
 
00db10
-	  oldval = LLL_ROBUST_MUTEX_LOCK (mutex, id);
00db10
+	  oldval = LLL_ROBUST_MUTEX_LOCK (mutex,
00db10
+					  id | assume_other_futex_waiters);
00db10
+	  /* See above.  We set FUTEX_WAITERS and might have shared this flag
00db10
+	     with other threads; thus, we need to preserve it.  */
00db10
+	  assume_other_futex_waiters = FUTEX_WAITERS;
00db10
 
00db10
 	  if (__builtin_expect (mutex->__data.__owner
00db10
 				== PTHREAD_MUTEX_NOTRECOVERABLE, 0))
00db10
Index: glibc-2.17-c758a686/nptl/pthread_mutex_timedlock.c
00db10
===================================================================
00db10
--- glibc-2.17-c758a686.orig/nptl/pthread_mutex_timedlock.c
00db10
+++ glibc-2.17-c758a686/nptl/pthread_mutex_timedlock.c
00db10
@@ -142,13 +142,19 @@ pthread_mutex_timedlock (pthread_mutex_t
00db10
 		     &mutex->__data.__list.__next);
00db10
 
00db10
       oldval = mutex->__data.__lock;
00db10
+      /* This is set to FUTEX_WAITERS iff we might have shared the
00db10
+	 FUTEX_WAITERS flag with other threads, and therefore need to keep it
00db10
+	 set to avoid lost wake-ups.  We have the same requirement in the
00db10
+	 simple mutex algorithm.  */
00db10
+      unsigned int assume_other_futex_waiters = 0;
00db10
       do
00db10
 	{
00db10
 	again:
00db10
 	  if ((oldval & FUTEX_OWNER_DIED) != 0)
00db10
 	    {
00db10
 	      /* The previous owner died.  Try locking the mutex.  */
00db10
-	      int newval = id | (oldval & FUTEX_WAITERS);
00db10
+	      int newval = id | (oldval & FUTEX_WAITERS)
00db10
+		  | assume_other_futex_waiters;
00db10
 
00db10
 	      newval
00db10
 		= atomic_compare_and_exchange_val_acq (&mutex->__data.__lock,
00db10
@@ -203,8 +209,12 @@ pthread_mutex_timedlock (pthread_mutex_t
00db10
 		}
00db10
 	    }
00db10
 
00db10
-	  result = lll_robust_timedlock (mutex->__data.__lock, abstime, id,
00db10
+	  result = lll_robust_timedlock (mutex->__data.__lock, abstime,
00db10
+					 id | assume_other_futex_waiters,
00db10
 					 PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
00db10
+	  /* See above.  We set FUTEX_WAITERS and might have shared this flag
00db10
+	     with other threads; thus, we need to preserve it.  */
00db10
+	  assume_other_futex_waiters = FUTEX_WAITERS;
00db10
 
00db10
 	  if (__builtin_expect (mutex->__data.__owner
00db10
 				== PTHREAD_MUTEX_NOTRECOVERABLE, 0))