Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 27f4837c authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'core-fixes-for-linus' of...

Merge branch 'core-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'core-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  futexes: fix fault handling in futex_lock_pi
parents 0e053738 1b7558e4
Loading
Loading
Loading
Loading
+73 −20
Original line number Diff line number Diff line
@@ -1096,21 +1096,64 @@ static void unqueue_me_pi(struct futex_q *q)
 * private futexes.
 */
static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
				struct task_struct *newowner)
				struct task_struct *newowner,
				struct rw_semaphore *fshared)
{
	u32 newtid = task_pid_vnr(newowner) | FUTEX_WAITERS;
	struct futex_pi_state *pi_state = q->pi_state;
	struct task_struct *oldowner = pi_state->owner;
	u32 uval, curval, newval;
	int ret;
	int ret, attempt = 0;

	/* Owner died? */
	if (!pi_state->owner)
		newtid |= FUTEX_OWNER_DIED;

	/*
	 * We are here either because we stole the rtmutex from the
	 * pending owner or we are the pending owner which failed to
	 * get the rtmutex. We have to replace the pending owner TID
	 * in the user space variable. This must be atomic as we have
	 * to preserve the owner died bit here.
	 *
	 * Note: We write the user space value _before_ changing the
	 * pi_state because we can fault here. Imagine swapped out
	 * pages or a fork, which was running right before we acquired
	 * mmap_sem, that marked all the anonymous memory readonly for
	 * cow.
	 *
	 * Modifying pi_state _before_ the user space value would
	 * leave the pi_state in an inconsistent state when we fault
	 * here, because we need to drop the hash bucket lock to
	 * handle the fault. This might be observed in the PID check
	 * in lookup_pi_state.
	 */
retry:
	if (get_futex_value_locked(&uval, uaddr))
		goto handle_fault;

	while (1) {
		newval = (uval & FUTEX_OWNER_DIED) | newtid;

		curval = cmpxchg_futex_value_locked(uaddr, uval, newval);

		if (curval == -EFAULT)
			goto handle_fault;
		if (curval == uval)
			break;
		uval = curval;
	}

	/*
	 * We fixed up user space. Now we need to fix the pi_state
	 * itself.
	 */
	if (pi_state->owner != NULL) {
		spin_lock_irq(&pi_state->owner->pi_lock);
		WARN_ON(list_empty(&pi_state->list));
		list_del_init(&pi_state->list);
		spin_unlock_irq(&pi_state->owner->pi_lock);
	} else
		newtid |= FUTEX_OWNER_DIED;
	}

	pi_state->owner = newowner;

@@ -1118,26 +1161,35 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
	WARN_ON(!list_empty(&pi_state->list));
	list_add(&pi_state->list, &newowner->pi_state_list);
	spin_unlock_irq(&newowner->pi_lock);
	return 0;

	/*
	 * We own it, so we have to replace the pending owner
	 * TID. This must be atomic as we have preserve the
	 * owner died bit here.
	 * To handle the page fault we need to drop the hash bucket
	 * lock here. That gives the other task (either the pending
	 * owner itself or the task which stole the rtmutex) the
	 * chance to try the fixup of the pi_state. So once we are
	 * back from handling the fault we need to check the pi_state
	 * after reacquiring the hash bucket lock and before trying to
	 * do another fixup. When the fixup has been done already we
	 * simply return.
	 */
	ret = get_futex_value_locked(&uval, uaddr);
handle_fault:
	spin_unlock(q->lock_ptr);

	while (!ret) {
		newval = (uval & FUTEX_OWNER_DIED) | newtid;
	ret = futex_handle_fault((unsigned long)uaddr, fshared, attempt++);

		curval = cmpxchg_futex_value_locked(uaddr, uval, newval);
	spin_lock(q->lock_ptr);

		if (curval == -EFAULT)
			ret = -EFAULT;
		if (curval == uval)
			break;
		uval = curval;
	}
	/*
	 * Check if someone else fixed it for us:
	 */
	if (pi_state->owner != oldowner)
		return 0;

	if (ret)
		return ret;

	goto retry;
}

/*
@@ -1507,7 +1559,7 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
		 * that case:
		 */
		if (q.pi_state->owner != curr)
			ret = fixup_pi_state_owner(uaddr, &q, curr);
			ret = fixup_pi_state_owner(uaddr, &q, curr, fshared);
	} else {
		/*
		 * Catch the rare case, where the lock was released
@@ -1539,7 +1591,8 @@ static int futex_lock_pi(u32 __user *uaddr, struct rw_semaphore *fshared,
				int res;

				owner = rt_mutex_owner(&q.pi_state->pi_mutex);
				res = fixup_pi_state_owner(uaddr, &q, owner);
				res = fixup_pi_state_owner(uaddr, &q, owner,
							   fshared);

				/* propagate -EFAULT, if the fixup failed */
				if (res)