aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorBrian Silverman <bsilver16384@gmail.com>2014-10-25 20:20:37 -0400
committerThomas Gleixner <tglx@linutronix.de>2014-10-26 11:16:18 -0400
commit30a6b8031fe14031ab27c1fa3483cb9780e7f63c (patch)
treeda56baddfc24fef71ca5b6f123c947e784527d87 /kernel
parent993b2ff221999066fcff231590593d0b98f45d32 (diff)
futex: Fix a race condition between REQUEUE_PI and task death
free_pi_state and exit_pi_state_list both clean up futex_pi_state's. exit_pi_state_list takes the hb lock first, and most callers of free_pi_state do too. requeue_pi doesn't, which means free_pi_state can free the pi_state out from under exit_pi_state_list. For example: task A | task B exit_pi_state_list | pi_state = | curr->pi_state_list->next | | futex_requeue(requeue_pi=1) | // pi_state is the same as | // the one in task A | free_pi_state(pi_state) | list_del_init(&pi_state->list) | kfree(pi_state) list_del_init(&pi_state->list) | Move the free_pi_state calls in requeue_pi to before it drops the hb locks which it's already holding. [ tglx: Removed a pointless free_pi_state() call and the hb->lock held debugging. The latter comes via a seperate patch ] Signed-off-by: Brian Silverman <bsilver16384@gmail.com> Cc: austin.linux@gmail.com Cc: darren@dvhart.com Cc: peterz@infradead.org Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/1414282837-23092-1-git-send-email-bsilver16384@gmail.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/futex.c22
1 files changed, 11 insertions, 11 deletions
diff --git a/kernel/futex.c b/kernel/futex.c
index bbf071f325b8..63678b573d61 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -647,8 +647,14 @@ static struct futex_pi_state * alloc_pi_state(void)
647 return pi_state; 647 return pi_state;
648} 648}
649 649
650/*
651 * Must be called with the hb lock held.
652 */
650static void free_pi_state(struct futex_pi_state *pi_state) 653static void free_pi_state(struct futex_pi_state *pi_state)
651{ 654{
655 if (!pi_state)
656 return;
657
652 if (!atomic_dec_and_test(&pi_state->refcount)) 658 if (!atomic_dec_and_test(&pi_state->refcount))
653 return; 659 return;
654 660
@@ -1527,15 +1533,6 @@ static int futex_requeue(u32 __user *uaddr1, unsigned int flags,
1527 } 1533 }
1528 1534
1529retry: 1535retry:
1530 if (pi_state != NULL) {
1531 /*
1532 * We will have to lookup the pi_state again, so free this one
1533 * to keep the accounting correct.
1534 */
1535 free_pi_state(pi_state);
1536 pi_state = NULL;
1537 }
1538
1539 ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ); 1536 ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ);
1540 if (unlikely(ret != 0)) 1537 if (unlikely(ret != 0))
1541 goto out; 1538 goto out;
@@ -1625,6 +1622,8 @@ retry_private:
1625 case 0: 1622 case 0:
1626 break; 1623 break;
1627 case -EFAULT: 1624 case -EFAULT:
1625 free_pi_state(pi_state);
1626 pi_state = NULL;
1628 double_unlock_hb(hb1, hb2); 1627 double_unlock_hb(hb1, hb2);
1629 hb_waiters_dec(hb2); 1628 hb_waiters_dec(hb2);
1630 put_futex_key(&key2); 1629 put_futex_key(&key2);
@@ -1640,6 +1639,8 @@ retry_private:
1640 * exit to complete. 1639 * exit to complete.
1641 * - The user space value changed. 1640 * - The user space value changed.
1642 */ 1641 */
1642 free_pi_state(pi_state);
1643 pi_state = NULL;
1643 double_unlock_hb(hb1, hb2); 1644 double_unlock_hb(hb1, hb2);
1644 hb_waiters_dec(hb2); 1645 hb_waiters_dec(hb2);
1645 put_futex_key(&key2); 1646 put_futex_key(&key2);
@@ -1716,6 +1717,7 @@ retry_private:
1716 } 1717 }
1717 1718
1718out_unlock: 1719out_unlock:
1720 free_pi_state(pi_state);
1719 double_unlock_hb(hb1, hb2); 1721 double_unlock_hb(hb1, hb2);
1720 hb_waiters_dec(hb2); 1722 hb_waiters_dec(hb2);
1721 1723
@@ -1733,8 +1735,6 @@ out_put_keys:
1733out_put_key1: 1735out_put_key1:
1734 put_futex_key(&key1); 1736 put_futex_key(&key1);
1735out: 1737out:
1736 if (pi_state != NULL)
1737 free_pi_state(pi_state);
1738 return ret ? ret : task_count; 1738 return ret ? ret : task_count;
1739} 1739}
1740 1740