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