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