linux/kernel/futex_compat.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * linux/kernel/futex_compat.c
   4 *
   5 * Futex compatibililty routines.
   6 *
   7 * Copyright 2006, Red Hat, Inc., Ingo Molnar
   8 */
   9
  10#include <linux/linkage.h>
  11#include <linux/compat.h>
  12#include <linux/nsproxy.h>
  13#include <linux/futex.h>
  14#include <linux/ptrace.h>
  15#include <linux/syscalls.h>
  16
  17#include <linux/uaccess.h>
  18
  19
  20/*
  21 * Fetch a robust-list pointer. Bit 0 signals PI futexes:
  22 */
  23static inline int
  24fetch_robust_entry(compat_uptr_t *uentry, struct robust_list __user **entry,
  25                   compat_uptr_t __user *head, unsigned int *pi)
  26{
  27        if (get_user(*uentry, head))
  28                return -EFAULT;
  29
  30        *entry = compat_ptr((*uentry) & ~1);
  31        *pi = (unsigned int)(*uentry) & 1;
  32
  33        return 0;
  34}
  35
  36static void __user *futex_uaddr(struct robust_list __user *entry,
  37                                compat_long_t futex_offset)
  38{
  39        compat_uptr_t base = ptr_to_compat(entry);
  40        void __user *uaddr = compat_ptr(base + futex_offset);
  41
  42        return uaddr;
  43}
  44
  45/*
  46 * Walk curr->robust_list (very carefully, it's a userspace list!)
  47 * and mark any locks found there dead, and notify any waiters.
  48 *
  49 * We silently return on any sign of list-walking problem.
  50 */
  51void compat_exit_robust_list(struct task_struct *curr)
  52{
  53        struct compat_robust_list_head __user *head = curr->compat_robust_list;
  54        struct robust_list __user *entry, *next_entry, *pending;
  55        unsigned int limit = ROBUST_LIST_LIMIT, pi, pip;
  56        unsigned int uninitialized_var(next_pi);
  57        compat_uptr_t uentry, next_uentry, upending;
  58        compat_long_t futex_offset;
  59        int rc;
  60
  61        if (!futex_cmpxchg_enabled)
  62                return;
  63
  64        /*
  65         * Fetch the list head (which was registered earlier, via
  66         * sys_set_robust_list()):
  67         */
  68        if (fetch_robust_entry(&uentry, &entry, &head->list.next, &pi))
  69                return;
  70        /*
  71         * Fetch the relative futex offset:
  72         */
  73        if (get_user(futex_offset, &head->futex_offset))
  74                return;
  75        /*
  76         * Fetch any possibly pending lock-add first, and handle it
  77         * if it exists:
  78         */
  79        if (fetch_robust_entry(&upending, &pending,
  80                               &head->list_op_pending, &pip))
  81                return;
  82
  83        next_entry = NULL;      /* avoid warning with gcc */
  84        while (entry != (struct robust_list __user *) &head->list) {
  85                /*
  86                 * Fetch the next entry in the list before calling
  87                 * handle_futex_death:
  88                 */
  89                rc = fetch_robust_entry(&next_uentry, &next_entry,
  90                        (compat_uptr_t __user *)&entry->next, &next_pi);
  91                /*
  92                 * A pending lock might already be on the list, so
  93                 * dont process it twice:
  94                 */
  95                if (entry != pending) {
  96                        void __user *uaddr = futex_uaddr(entry, futex_offset);
  97
  98                        if (handle_futex_death(uaddr, curr, pi))
  99                                return;
 100                }
 101                if (rc)
 102                        return;
 103                uentry = next_uentry;
 104                entry = next_entry;
 105                pi = next_pi;
 106                /*
 107                 * Avoid excessively long or circular lists:
 108                 */
 109                if (!--limit)
 110                        break;
 111
 112                cond_resched();
 113        }
 114        if (pending) {
 115                void __user *uaddr = futex_uaddr(pending, futex_offset);
 116
 117                handle_futex_death(uaddr, curr, pip);
 118        }
 119}
 120
 121COMPAT_SYSCALL_DEFINE2(set_robust_list,
 122                struct compat_robust_list_head __user *, head,
 123                compat_size_t, len)
 124{
 125        if (!futex_cmpxchg_enabled)
 126                return -ENOSYS;
 127
 128        if (unlikely(len != sizeof(*head)))
 129                return -EINVAL;
 130
 131        current->compat_robust_list = head;
 132
 133        return 0;
 134}
 135
 136COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid,
 137                        compat_uptr_t __user *, head_ptr,
 138                        compat_size_t __user *, len_ptr)
 139{
 140        struct compat_robust_list_head __user *head;
 141        unsigned long ret;
 142        struct task_struct *p;
 143
 144        if (!futex_cmpxchg_enabled)
 145                return -ENOSYS;
 146
 147        rcu_read_lock();
 148
 149        ret = -ESRCH;
 150        if (!pid)
 151                p = current;
 152        else {
 153                p = find_task_by_vpid(pid);
 154                if (!p)
 155                        goto err_unlock;
 156        }
 157
 158        ret = -EPERM;
 159        if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS))
 160                goto err_unlock;
 161
 162        head = p->compat_robust_list;
 163        rcu_read_unlock();
 164
 165        if (put_user(sizeof(*head), len_ptr))
 166                return -EFAULT;
 167        return put_user(ptr_to_compat(head), head_ptr);
 168
 169err_unlock:
 170        rcu_read_unlock();
 171
 172        return ret;
 173}
 174
 175COMPAT_SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
 176                struct compat_timespec __user *, utime, u32 __user *, uaddr2,
 177                u32, val3)
 178{
 179        struct timespec ts;
 180        ktime_t t, *tp = NULL;
 181        int val2 = 0;
 182        int cmd = op & FUTEX_CMD_MASK;
 183
 184        if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
 185                      cmd == FUTEX_WAIT_BITSET ||
 186                      cmd == FUTEX_WAIT_REQUEUE_PI)) {
 187                if (compat_get_timespec(&ts, utime))
 188                        return -EFAULT;
 189                if (!timespec_valid(&ts))
 190                        return -EINVAL;
 191
 192                t = timespec_to_ktime(ts);
 193                if (cmd == FUTEX_WAIT)
 194                        t = ktime_add_safe(ktime_get(), t);
 195                tp = &t;
 196        }
 197        if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
 198            cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
 199                val2 = (int) (unsigned long) utime;
 200
 201        return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
 202}
 203