git: 41ff17fb531b - stable/13 - linux(4): Factor out the FUTEX_CMP_REQUEUE op into linux_futex_requeue().
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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) {