d8307d
commit 823624bdc47f1f80109c9c52dee7939b9386d708
d8307d
Author: Stefan Liebler <stli@linux.ibm.com>
d8307d
Date:   Thu Feb 7 15:18:36 2019 +0100
d8307d
d8307d
    Add compiler barriers around modifications of the robust mutex list for pthread_mutex_trylock. [BZ #24180]
d8307d
    
d8307d
    While debugging a kernel warning, Thomas Gleixner, Sebastian Sewior and
d8307d
    Heiko Carstens found a bug in pthread_mutex_trylock due to misordered
d8307d
    instructions:
d8307d
    140:   a5 1b 00 01             oill    %r1,1
d8307d
    144:   e5 48 a0 f0 00 00       mvghi   240(%r10),0   <--- THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
    14a:   e3 10 a0 e0 00 24       stg     %r1,224(%r10) <--- last THREAD_SETMEM of ENQUEUE_MUTEX_PI
d8307d
    
d8307d
    vs (with compiler barriers):
d8307d
    140:   a5 1b 00 01             oill    %r1,1
d8307d
    144:   e3 10 a0 e0 00 24       stg     %r1,224(%r10)
d8307d
    14a:   e5 48 a0 f0 00 00       mvghi   240(%r10),0
d8307d
    
d8307d
    Please have a look at the discussion:
d8307d
    "Re: WARN_ON_ONCE(!new_owner) within wake_futex_pi() triggerede"
d8307d
    (https://lore.kernel.org/lkml/20190202112006.GB3381@osiris/)
d8307d
    
d8307d
    This patch is introducing the same compiler barriers and comments
d8307d
    for pthread_mutex_trylock as introduced for pthread_mutex_lock and
d8307d
    pthread_mutex_timedlock by commit 8f9450a0b7a9e78267e8ae1ab1000ebca08e473e
d8307d
    "Add compiler barriers around modifications of the robust mutex list."
d8307d
    
d8307d
    ChangeLog:
d8307d
    
d8307d
            [BZ #24180]
d8307d
            * nptl/pthread_mutex_trylock.c (__pthread_mutex_trylock):
d8307d
d8307d
diff --git a/nptl/pthread_mutex_trylock.c b/nptl/pthread_mutex_trylock.c
d8307d
index 8fe43b8f0f..bf2869eca2 100644
d8307d
--- a/nptl/pthread_mutex_trylock.c
d8307d
+++ b/nptl/pthread_mutex_trylock.c
d8307d
@@ -94,6 +94,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
     case PTHREAD_MUTEX_ROBUST_ADAPTIVE_NP:
d8307d
       THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
d8307d
 		     &mutex->__data.__list.__next);
d8307d
+      /* We need to set op_pending before starting the operation.  Also
d8307d
+	 see comments at ENQUEUE_MUTEX.  */
d8307d
+      __asm ("" ::: "memory");
d8307d
 
d8307d
       oldval = mutex->__data.__lock;
d8307d
       do
d8307d
@@ -119,7 +122,12 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 	      /* But it is inconsistent unless marked otherwise.  */
d8307d
 	      mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
d8307d
 
d8307d
+	      /* We must not enqueue the mutex before we have acquired it.
d8307d
+		 Also see comments at ENQUEUE_MUTEX.  */
d8307d
+	      __asm ("" ::: "memory");
d8307d
 	      ENQUEUE_MUTEX (mutex);
d8307d
+	      /* We need to clear op_pending after we enqueue the mutex.  */
d8307d
+	      __asm ("" ::: "memory");
d8307d
 	      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 
d8307d
 	      /* Note that we deliberately exist here.  If we fall
d8307d
@@ -135,6 +143,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 	      int kind = PTHREAD_MUTEX_TYPE (mutex);
d8307d
 	      if (kind == PTHREAD_MUTEX_ROBUST_ERRORCHECK_NP)
d8307d
 		{
d8307d
+		  /* We do not need to ensure ordering wrt another memory
d8307d
+		     access.  Also see comments at ENQUEUE_MUTEX. */
d8307d
 		  THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
d8307d
 				 NULL);
d8307d
 		  return EDEADLK;
d8307d
@@ -142,6 +152,8 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 
d8307d
 	      if (kind == PTHREAD_MUTEX_ROBUST_RECURSIVE_NP)
d8307d
 		{
d8307d
+		  /* We do not need to ensure ordering wrt another memory
d8307d
+		     access.  */
d8307d
 		  THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
d8307d
 				 NULL);
d8307d
 
d8307d
@@ -160,6 +172,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 							id, 0);
d8307d
 	  if (oldval != 0 && (oldval & FUTEX_OWNER_DIED) == 0)
d8307d
 	    {
d8307d
+	      /* We haven't acquired the lock as it is already acquired by
d8307d
+		 another owner.  We do not need to ensure ordering wrt another
d8307d
+		 memory access.  */
d8307d
 	      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 
d8307d
 	      return EBUSY;
d8307d
@@ -173,13 +188,20 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 	      if (oldval == id)
d8307d
 		lll_unlock (mutex->__data.__lock,
d8307d
 			    PTHREAD_ROBUST_MUTEX_PSHARED (mutex));
d8307d
+	      /* FIXME This violates the mutex destruction requirements.  See
d8307d
+		 __pthread_mutex_unlock_full.  */
d8307d
 	      THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 	      return ENOTRECOVERABLE;
d8307d
 	    }
d8307d
 	}
d8307d
       while ((oldval & FUTEX_OWNER_DIED) != 0);
d8307d
 
d8307d
+      /* We must not enqueue the mutex before we have acquired it.
d8307d
+	 Also see comments at ENQUEUE_MUTEX.  */
d8307d
+      __asm ("" ::: "memory");
d8307d
       ENQUEUE_MUTEX (mutex);
d8307d
+      /* We need to clear op_pending after we enqueue the mutex.  */
d8307d
+      __asm ("" ::: "memory");
d8307d
       THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 
d8307d
       mutex->__data.__owner = id;
d8307d
@@ -211,10 +233,15 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 	}
d8307d
 
d8307d
 	if (robust)
d8307d
-	  /* Note: robust PI futexes are signaled by setting bit 0.  */
d8307d
-	  THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
d8307d
-			 (void *) (((uintptr_t) &mutex->__data.__list.__next)
d8307d
-				   | 1));
d8307d
+	  {
d8307d
+	    /* Note: robust PI futexes are signaled by setting bit 0.  */
d8307d
+	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending,
d8307d
+			   (void *) (((uintptr_t) &mutex->__data.__list.__next)
d8307d
+				     | 1));
d8307d
+	    /* We need to set op_pending before starting the operation.  Also
d8307d
+	       see comments at ENQUEUE_MUTEX.  */
d8307d
+	    __asm ("" ::: "memory");
d8307d
+	  }
d8307d
 
d8307d
 	oldval = mutex->__data.__lock;
d8307d
 
d8307d
@@ -223,12 +250,16 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 	  {
d8307d
 	    if (kind == PTHREAD_MUTEX_ERRORCHECK_NP)
d8307d
 	      {
d8307d
+		/* We do not need to ensure ordering wrt another memory
d8307d
+		   access.  */
d8307d
 		THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 		return EDEADLK;
d8307d
 	      }
d8307d
 
d8307d
 	    if (kind == PTHREAD_MUTEX_RECURSIVE_NP)
d8307d
 	      {
d8307d
+		/* We do not need to ensure ordering wrt another memory
d8307d
+		   access.  */
d8307d
 		THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 
d8307d
 		/* Just bump the counter.  */
d8307d
@@ -250,6 +281,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 	  {
d8307d
 	    if ((oldval & FUTEX_OWNER_DIED) == 0)
d8307d
 	      {
d8307d
+		/* We haven't acquired the lock as it is already acquired by
d8307d
+		   another owner.  We do not need to ensure ordering wrt another
d8307d
+		   memory access.  */
d8307d
 		THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 
d8307d
 		return EBUSY;
d8307d
@@ -270,6 +304,9 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 	    if (INTERNAL_SYSCALL_ERROR_P (e, __err)
d8307d
 		&& INTERNAL_SYSCALL_ERRNO (e, __err) == EWOULDBLOCK)
d8307d
 	      {
d8307d
+		/* The kernel has not yet finished the mutex owner death.
d8307d
+		   We do not need to ensure ordering wrt another memory
d8307d
+		   access.  */
d8307d
 		THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 
d8307d
 		return EBUSY;
d8307d
@@ -287,7 +324,12 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 	    /* But it is inconsistent unless marked otherwise.  */
d8307d
 	    mutex->__data.__owner = PTHREAD_MUTEX_INCONSISTENT;
d8307d
 
d8307d
+	    /* We must not enqueue the mutex before we have acquired it.
d8307d
+	       Also see comments at ENQUEUE_MUTEX.  */
d8307d
+	    __asm ("" ::: "memory");
d8307d
 	    ENQUEUE_MUTEX (mutex);
d8307d
+	    /* We need to clear op_pending after we enqueue the mutex.  */
d8307d
+	    __asm ("" ::: "memory");
d8307d
 	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 
d8307d
 	    /* Note that we deliberately exit here.  If we fall
d8307d
@@ -310,13 +352,20 @@ __pthread_mutex_trylock (pthread_mutex_t *mutex)
d8307d
 						  PTHREAD_ROBUST_MUTEX_PSHARED (mutex)),
d8307d
 			      0, 0);
d8307d
 
d8307d
+	    /* To the kernel, this will be visible after the kernel has
d8307d
+	       acquired the mutex in the syscall.  */
d8307d
 	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 	    return ENOTRECOVERABLE;
d8307d
 	  }
d8307d
 
d8307d
 	if (robust)
d8307d
 	  {
d8307d
+	    /* We must not enqueue the mutex before we have acquired it.
d8307d
+	       Also see comments at ENQUEUE_MUTEX.  */
d8307d
+	    __asm ("" ::: "memory");
d8307d
 	    ENQUEUE_MUTEX_PI (mutex);
d8307d
+	    /* We need to clear op_pending after we enqueue the mutex.  */
d8307d
+	    __asm ("" ::: "memory");
d8307d
 	    THREAD_SETMEM (THREAD_SELF, robust_head.list_op_pending, NULL);
d8307d
 	  }
d8307d