summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/futex.h8
-rw-r--r--kernel/Makefile3
-rw-r--r--kernel/futex.c195
-rw-r--r--kernel/futex_compat.c202
4 files changed, 192 insertions, 216 deletions
diff --git a/include/linux/futex.h b/include/linux/futex.h
index 821ae502d3d8..ccaef0097785 100644
--- a/include/linux/futex.h
+++ b/include/linux/futex.h
@@ -9,9 +9,6 @@ struct inode;
9struct mm_struct; 9struct mm_struct;
10struct task_struct; 10struct task_struct;
11 11
12extern int
13handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi);
14
15/* 12/*
16 * Futexes are matched on equal values of this key. 13 * Futexes are matched on equal values of this key.
17 * The key type depends on whether it's a shared or private mapping. 14 * The key type depends on whether it's a shared or private mapping.
@@ -55,11 +52,6 @@ extern void exit_robust_list(struct task_struct *curr);
55 52
56long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout, 53long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
57 u32 __user *uaddr2, u32 val2, u32 val3); 54 u32 __user *uaddr2, u32 val2, u32 val3);
58#ifdef CONFIG_HAVE_FUTEX_CMPXCHG
59#define futex_cmpxchg_enabled 1
60#else
61extern int futex_cmpxchg_enabled;
62#endif
63#else 55#else
64static inline void exit_robust_list(struct task_struct *curr) 56static inline void exit_robust_list(struct task_struct *curr)
65{ 57{
diff --git a/kernel/Makefile b/kernel/Makefile
index 7343b3a9bff0..8e40a6742d23 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -49,9 +49,6 @@ obj-$(CONFIG_PROFILING) += profile.o
49obj-$(CONFIG_STACKTRACE) += stacktrace.o 49obj-$(CONFIG_STACKTRACE) += stacktrace.o
50obj-y += time/ 50obj-y += time/
51obj-$(CONFIG_FUTEX) += futex.o 51obj-$(CONFIG_FUTEX) += futex.o
52ifeq ($(CONFIG_COMPAT),y)
53obj-$(CONFIG_FUTEX) += futex_compat.o
54endif
55obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o 52obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
56obj-$(CONFIG_SMP) += smp.o 53obj-$(CONFIG_SMP) += smp.o
57ifneq ($(CONFIG_SMP),y) 54ifneq ($(CONFIG_SMP),y)
diff --git a/kernel/futex.c b/kernel/futex.c
index f423f9b6577e..5cc7c3b098e9 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -44,6 +44,7 @@
44 * along with this program; if not, write to the Free Software 44 * along with this program; if not, write to the Free Software
45 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 45 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
46 */ 46 */
47#include <linux/compat.h>
47#include <linux/slab.h> 48#include <linux/slab.h>
48#include <linux/poll.h> 49#include <linux/poll.h>
49#include <linux/fs.h> 50#include <linux/fs.h>
@@ -173,8 +174,10 @@
173 * double_lock_hb() and double_unlock_hb(), respectively. 174 * double_lock_hb() and double_unlock_hb(), respectively.
174 */ 175 */
175 176
176#ifndef CONFIG_HAVE_FUTEX_CMPXCHG 177#ifdef CONFIG_HAVE_FUTEX_CMPXCHG
177int __read_mostly futex_cmpxchg_enabled; 178#define futex_cmpxchg_enabled 1
179#else
180static int __read_mostly futex_cmpxchg_enabled;
178#endif 181#endif
179 182
180/* 183/*
@@ -3360,7 +3363,7 @@ err_unlock:
3360 * Process a futex-list entry, check whether it's owned by the 3363 * Process a futex-list entry, check whether it's owned by the
3361 * dying task, and do notification if so: 3364 * dying task, and do notification if so:
3362 */ 3365 */
3363int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi) 3366static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi)
3364{ 3367{
3365 u32 uval, uninitialized_var(nval), mval; 3368 u32 uval, uninitialized_var(nval), mval;
3366 3369
@@ -3589,6 +3592,192 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
3589 return do_futex(uaddr, op, val, tp, uaddr2, val2, val3); 3592 return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
3590} 3593}
3591 3594
3595#ifdef CONFIG_COMPAT
3596/*
3597 * Fetch a robust-list pointer. Bit 0 signals PI futexes:
3598 */
3599static inline int
3600compat_fetch_robust_entry(compat_uptr_t *uentry, struct robust_list __user **entry,
3601 compat_uptr_t __user *head, unsigned int *pi)
3602{
3603 if (get_user(*uentry, head))
3604 return -EFAULT;
3605
3606 *entry = compat_ptr((*uentry) & ~1);
3607 *pi = (unsigned int)(*uentry) & 1;
3608
3609 return 0;
3610}
3611
3612static void __user *futex_uaddr(struct robust_list __user *entry,
3613 compat_long_t futex_offset)
3614{
3615 compat_uptr_t base = ptr_to_compat(entry);
3616 void __user *uaddr = compat_ptr(base + futex_offset);
3617
3618 return uaddr;
3619}
3620
3621/*
3622 * Walk curr->robust_list (very carefully, it's a userspace list!)
3623 * and mark any locks found there dead, and notify any waiters.
3624 *
3625 * We silently return on any sign of list-walking problem.
3626 */
3627void compat_exit_robust_list(struct task_struct *curr)
3628{
3629 struct compat_robust_list_head __user *head = curr->compat_robust_list;
3630 struct robust_list __user *entry, *next_entry, *pending;
3631 unsigned int limit = ROBUST_LIST_LIMIT, pi, pip;
3632 unsigned int uninitialized_var(next_pi);
3633 compat_uptr_t uentry, next_uentry, upending;
3634 compat_long_t futex_offset;
3635 int rc;
3636
3637 if (!futex_cmpxchg_enabled)
3638 return;
3639
3640 /*
3641 * Fetch the list head (which was registered earlier, via
3642 * sys_set_robust_list()):
3643 */
3644 if (compat_fetch_robust_entry(&uentry, &entry, &head->list.next, &pi))
3645 return;
3646 /*
3647 * Fetch the relative futex offset:
3648 */
3649 if (get_user(futex_offset, &head->futex_offset))
3650 return;
3651 /*
3652 * Fetch any possibly pending lock-add first, and handle it
3653 * if it exists:
3654 */
3655 if (compat_fetch_robust_entry(&upending, &pending,
3656 &head->list_op_pending, &pip))
3657 return;
3658
3659 next_entry = NULL; /* avoid warning with gcc */
3660 while (entry != (struct robust_list __user *) &head->list) {
3661 /*
3662 * Fetch the next entry in the list before calling
3663 * handle_futex_death:
3664 */
3665 rc = compat_fetch_robust_entry(&next_uentry, &next_entry,
3666 (compat_uptr_t __user *)&entry->next, &next_pi);
3667 /*
3668 * A pending lock might already be on the list, so
3669 * dont process it twice:
3670 */
3671 if (entry != pending) {
3672 void __user *uaddr = futex_uaddr(entry, futex_offset);
3673
3674 if (handle_futex_death(uaddr, curr, pi))
3675 return;
3676 }
3677 if (rc)
3678 return;
3679 uentry = next_uentry;
3680 entry = next_entry;
3681 pi = next_pi;
3682 /*
3683 * Avoid excessively long or circular lists:
3684 */
3685 if (!--limit)
3686 break;
3687
3688 cond_resched();
3689 }
3690 if (pending) {
3691 void __user *uaddr = futex_uaddr(pending, futex_offset);
3692
3693 handle_futex_death(uaddr, curr, pip);
3694 }
3695}
3696
3697COMPAT_SYSCALL_DEFINE2(set_robust_list,
3698 struct compat_robust_list_head __user *, head,
3699 compat_size_t, len)
3700{
3701 if (!futex_cmpxchg_enabled)
3702 return -ENOSYS;
3703
3704 if (unlikely(len != sizeof(*head)))
3705 return -EINVAL;
3706
3707 current->compat_robust_list = head;
3708
3709 return 0;
3710}
3711
3712COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid,
3713 compat_uptr_t __user *, head_ptr,
3714 compat_size_t __user *, len_ptr)
3715{
3716 struct compat_robust_list_head __user *head;
3717 unsigned long ret;
3718 struct task_struct *p;
3719
3720 if (!futex_cmpxchg_enabled)
3721 return -ENOSYS;
3722
3723 rcu_read_lock();
3724
3725 ret = -ESRCH;
3726 if (!pid)
3727 p = current;
3728 else {
3729 p = find_task_by_vpid(pid);
3730 if (!p)
3731 goto err_unlock;
3732 }
3733
3734 ret = -EPERM;
3735 if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS))
3736 goto err_unlock;
3737
3738 head = p->compat_robust_list;
3739 rcu_read_unlock();
3740
3741 if (put_user(sizeof(*head), len_ptr))
3742 return -EFAULT;
3743 return put_user(ptr_to_compat(head), head_ptr);
3744
3745err_unlock:
3746 rcu_read_unlock();
3747
3748 return ret;
3749}
3750
3751COMPAT_SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
3752 struct old_timespec32 __user *, utime, u32 __user *, uaddr2,
3753 u32, val3)
3754{
3755 struct timespec ts;
3756 ktime_t t, *tp = NULL;
3757 int val2 = 0;
3758 int cmd = op & FUTEX_CMD_MASK;
3759
3760 if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
3761 cmd == FUTEX_WAIT_BITSET ||
3762 cmd == FUTEX_WAIT_REQUEUE_PI)) {
3763 if (compat_get_timespec(&ts, utime))
3764 return -EFAULT;
3765 if (!timespec_valid(&ts))
3766 return -EINVAL;
3767
3768 t = timespec_to_ktime(ts);
3769 if (cmd == FUTEX_WAIT)
3770 t = ktime_add_safe(ktime_get(), t);
3771 tp = &t;
3772 }
3773 if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
3774 cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
3775 val2 = (int) (unsigned long) utime;
3776
3777 return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
3778}
3779#endif /* CONFIG_COMPAT */
3780
3592static void __init futex_detect_cmpxchg(void) 3781static void __init futex_detect_cmpxchg(void)
3593{ 3782{
3594#ifndef CONFIG_HAVE_FUTEX_CMPXCHG 3783#ifndef CONFIG_HAVE_FUTEX_CMPXCHG
diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c
deleted file mode 100644
index 410a77a8f6e2..000000000000
--- a/kernel/futex_compat.c
+++ /dev/null
@@ -1,202 +0,0 @@
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 old_timespec32 __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}