linux/kernel/locking
Thomas Gleixner 01b8eab1cc rtmutex: Plug slow unlock race
commit 27e35715df upstream.

When the rtmutex fast path is enabled the slow unlock function can
create the following situation:

spin_lock(foo->m->wait_lock);
foo->m->owner = NULL;
	    			rt_mutex_lock(foo->m); <-- fast path
				free = atomic_dec_and_test(foo->refcnt);
				rt_mutex_unlock(foo->m); <-- fast path
				if (free)
				   kfree(foo);

spin_unlock(foo->m->wait_lock); <--- Use after free.

Plug the race by changing the slow unlock to the following scheme:

     while (!rt_mutex_has_waiters(m)) {
     	    /* Clear the waiters bit in m->owner */
	    clear_rt_mutex_waiters(m);
      	    owner = rt_mutex_owner(m);
      	    spin_unlock(m->wait_lock);
      	    if (cmpxchg(m->owner, owner, 0) == owner)
      	       return;
      	    spin_lock(m->wait_lock);
     }

So in case of a new waiter incoming while the owner tries the slow
path unlock we have two situations:

 unlock(wait_lock);
					lock(wait_lock);
 cmpxchg(p, owner, 0) == owner
 	    	   			mark_rt_mutex_waiters(lock);
	 				acquire(lock);

Or:

 unlock(wait_lock);
					lock(wait_lock);
	 				mark_rt_mutex_waiters(lock);
 cmpxchg(p, owner, 0) != owner
					enqueue_waiter();
					unlock(wait_lock);
 lock(wait_lock);
 wakeup_next waiter();
 unlock(wait_lock);
					lock(wait_lock);
					acquire(lock);

If the fast path is disabled, then the simple

   m->owner = NULL;
   unlock(m->wait_lock);

is sufficient as all access to m->owner is serialized via
m->wait_lock;

Also document and clarify the wakeup_next_waiter function as suggested
by Oleg Nesterov.

Reported-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Steven Rostedt <rostedt@goodmis.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20140611183852.937945560@linutronix.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
History 2014-06-30 20:11:58 -07:00
..
lglock.c locking: Move the lglocks code to kernel/locking/ 2013-11-06 09:24:20 +01:00
lockdep.c lockdep: Be nice about building from userspace 2013-11-27 11:55:20 +01:00
lockdep_internals.h locking: Move the lockdep code to kernel/locking/ 2013-11-06 07:55:08 +01:00
lockdep_proc.c lockdep/proc: Fix lock-time avg computation 2013-11-11 12:41:34 +01:00
lockdep_states.h locking: Move the lockdep code to kernel/locking/ 2013-11-06 07:55:08 +01:00
Makefile locking: Move the percpu-rwsem code to kernel/locking/ 2013-11-06 09:24:22 +01:00
mutex-debug.c mutexes: Give more informative mutex warning in the !lock->owner case 2013-12-17 15:35:10 +01:00
mutex-debug.h locking: Move the mutex code to kernel/locking/ 2013-11-06 07:55:07 +01:00
mutex.c locking/doc: Update references to kernel/mutex.c 2013-11-11 12:41:33 +01:00
mutex.h locking: Move the mutex code to kernel/locking/ 2013-11-06 07:55:07 +01:00
percpu-rwsem.c locking: Move the percpu-rwsem code to kernel/locking/ 2013-11-06 09:24:22 +01:00
rtmutex-debug.c rtmutex: Turn the plist into an rb-tree 2014-01-13 13:41:50 +01:00
rtmutex-debug.h rtmutex: Handle deadlock detection smarter 2014-06-30 20:11:58 -07:00
rtmutex-tester.c locking: Move the rtmutex code to kernel/locking/ 2013-11-06 09:23:59 +01:00
rtmutex.c rtmutex: Plug slow unlock race 2014-06-30 20:11:58 -07:00
rtmutex.h rtmutex: Handle deadlock detection smarter 2014-06-30 20:11:58 -07:00
rtmutex_common.h sched/deadline: Add SCHED_DEADLINE inheritance logic 2014-01-13 13:42:56 +01:00
rwsem-spinlock.c locking: Move the rwsem code to kernel/locking/ 2013-11-06 09:24:18 +01:00
rwsem-xadd.c locking: Move the rwsem code to kernel/locking/ 2013-11-06 09:24:18 +01:00
rwsem.c locking: Move the rwsem code to kernel/locking/ 2013-11-06 09:24:18 +01:00
semaphore.c locking: Move the semaphore core to kernel/locking/ 2013-11-06 07:55:22 +01:00
spinlock.c locking: Move the spinlock code to kernel/locking/ 2013-11-06 07:55:21 +01:00
spinlock_debug.c locking: Move the spinlock code to kernel/locking/ 2013-11-06 07:55:21 +01:00