git: 41ff17fb531b - stable/13 - linux(4): Factor out the FUTEX_CMP_REQUEUE op into linux_futex_requeue().

From: Dmitry Chagin <dchagin_at_FreeBSD.org>
Date: Fri, 17 Jun 2022 19:37:07 UTC
The branch stable/13 has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=41ff17fb531bea610d97b158b4af228100162e02

commit 41ff17fb531bea610d97b158b4af228100162e02
Author:     Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2021-07-20 11:38:27 +0000
Commit:     Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2022-06-17 19:33:11 +0000

    linux(4): Factor out the FUTEX_CMP_REQUEUE op into linux_futex_requeue().
    
    MFC after:              2 weeks
    
    (cherry picked from commit bb62a91944fa7985ebea29063721c633e28d0752)
---
 sys/compat/linux/linux_futex.c | 148 ++++++++++++++++++++++-------------------
 1 file changed, 80 insertions(+), 68 deletions(-)

diff --git a/sys/compat/linux/linux_futex.c b/sys/compat/linux/linux_futex.c
index 04f767b8aed0..71ea8e320d73 100644
--- a/sys/compat/linux/linux_futex.c
+++ b/sys/compat/linux/linux_futex.c
@@ -240,6 +240,7 @@ struct linux_futex_args {
 static int	linux_futex(struct thread *, struct linux_futex_args *);
 static int linux_futex_wait(struct thread *, struct linux_futex_args *);
 static int linux_futex_wake(struct thread *, struct linux_futex_args *);
+static int linux_futex_requeue(struct thread *, struct linux_futex_args *);
 
 static void
 futex_put(struct futex *f, struct waiting_proc *wp)
@@ -645,7 +646,7 @@ futex_atomic_op(struct thread *td, int encoded_op, uint32_t *uaddr)
 static int
 linux_futex(struct thread *td, struct linux_futex_args *args)
 {
-	int nrwake, nrrequeue, op_ret, ret;
+	int nrwake, op_ret, ret;
 	struct linux_pemuldata *pem;
 	struct futex *f, *f2;
 	int error, save;
@@ -709,73 +710,7 @@ linux_futex(struct thread *td, struct linux_futex_args *args)
 		    args->uaddr, args->val, args->val3, args->uaddr2,
 		    args->ts);
 
-		/*
-		 * Linux allows this, we would not, it is an incorrect
-		 * usage of declared ABI, so return EINVAL.
-		 */
-		if (args->uaddr == args->uaddr2) {
-			LIN_SDT_PROBE0(futex, linux_futex,
-			    invalid_cmp_requeue_use);
-			return (EINVAL);
-		}
-
-		nrrequeue = (int)(unsigned long)args->ts;
-		nrwake = args->val;
-		/*
-		 * Sanity check to prevent signed integer overflow,
-		 * see Linux CVE-2018-6927
-		 */
-		if (nrwake < 0 || nrrequeue < 0)
-			return (EINVAL);
-
-retry1:
-		error = futex_get(args->uaddr, NULL, &f,
-		    args->flags | FUTEX_DONTLOCK);
-		if (error)
-			return (error);
-
-		/*
-		 * To avoid deadlocks return EINVAL if second futex
-		 * exists at this time.
-		 *
-		 * Glibc fall back to FUTEX_WAKE in case of any error
-		 * returned by FUTEX_CMP_REQUEUE.
-		 */
-		error = futex_get(args->uaddr2, NULL, &f2,
-		    args->flags | FUTEX_DONTEXISTS | FUTEX_DONTLOCK);
-		if (error) {
-			futex_put(f, NULL);
-			return (error);
-		}
-		futex_lock(f);
-		futex_lock(f2);
-		error = copyin_nofault(args->uaddr, &val, sizeof(val));
-		if (error) {
-			futex_put(f2, NULL);
-			futex_put(f, NULL);
-			error = copyin(args->uaddr, &val, sizeof(val));
-			if (error == 0)
-				goto retry1;
-			LIN_SDT_PROBE1(futex, linux_futex, copyin_error,
-			    error);
-			LINUX_CTR1(sys_futex, "CMP_REQUEUE copyin failed %d",
-			    error);
-			return (error);
-		}
-		if (val != args->val3) {
-			LIN_SDT_PROBE2(futex, linux_futex,
-			    debug_cmp_requeue_value_neq, args->val, val);
-			LINUX_CTR2(sys_futex, "CMP_REQUEUE val 0x%x != uval 0x%x",
-			    args->val, val);
-			futex_put(f2, NULL);
-			futex_put(f, NULL);
-			return (EAGAIN);
-		}
-
-		td->td_retval[0] = futex_requeue(f, nrwake, f2, nrrequeue);
-		futex_put(f2, NULL);
-		futex_put(f, NULL);
-		break;
+		return (linux_futex_requeue(td, args));
 
 	case LINUX_FUTEX_WAKE_OP:
 		LIN_SDT_PROBE5(futex, linux_futex, debug_wake_op,
@@ -923,6 +858,83 @@ retry2:
 	return (error);
 }
 
+static int
+linux_futex_requeue(struct thread *td, struct linux_futex_args *args)
+{
+	int nrwake, nrrequeue;
+	struct futex *f, *f2;
+	int error;
+	uint32_t val;
+
+	/*
+	 * Linux allows this, we would not, it is an incorrect
+	 * usage of declared ABI, so return EINVAL.
+	 */
+	if (args->uaddr == args->uaddr2) {
+		LIN_SDT_PROBE0(futex, linux_futex,
+		    invalid_cmp_requeue_use);
+		return (EINVAL);
+	}
+
+	nrrequeue = (int)(unsigned long)args->ts;
+	nrwake = args->val;
+	/*
+	 * Sanity check to prevent signed integer overflow,
+	 * see Linux CVE-2018-6927
+	 */
+	if (nrwake < 0 || nrrequeue < 0)
+		return (EINVAL);
+
+retry:
+	f = f2 = NULL;
+	error = futex_get(args->uaddr, NULL, &f, args->flags | FUTEX_DONTLOCK);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * To avoid deadlocks return EINVAL if second futex
+	 * exists at this time.
+	 *
+	 * Glibc fall back to FUTEX_WAKE in case of any error
+	 * returned by FUTEX_CMP_REQUEUE.
+	 */
+	error = futex_get(args->uaddr2, NULL, &f2,
+	    args->flags | FUTEX_DONTEXISTS | FUTEX_DONTLOCK);
+	if (error != 0) {
+		futex_put(f, NULL);
+		return (error);
+	}
+	futex_lock(f);
+	futex_lock(f2);
+	error = copyin_nofault(args->uaddr, &val, sizeof(val));
+	if (error != 0) {
+		futex_put(f2, NULL);
+		futex_put(f, NULL);
+		error = copyin(args->uaddr, &val, sizeof(val));
+		if (error == 0)
+			goto retry;
+		LIN_SDT_PROBE1(futex, linux_futex, copyin_error,
+		    error);
+		LINUX_CTR1(sys_futex, "CMP_REQUEUE copyin failed %d",
+		    error);
+		return (error);
+	}
+	if (val != args->val3) {
+		LIN_SDT_PROBE2(futex, linux_futex,
+		    debug_cmp_requeue_value_neq, args->val, val);
+		LINUX_CTR2(sys_futex, "CMP_REQUEUE val 0x%x != uval 0x%x",
+		    args->val, val);
+		futex_put(f2, NULL);
+		futex_put(f, NULL);
+		return (EAGAIN);
+	}
+
+	td->td_retval[0] = futex_requeue(f, nrwake, f2, nrrequeue);
+	futex_put(f2, NULL);
+	futex_put(f, NULL);
+	return (0);
+}
+
 static int
 linux_futex_wake(struct thread *td, struct linux_futex_args *args)
 {