summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2018-04-17 10:31:07 -0400
committerArnd Bergmann <arnd@arndb.de>2018-12-07 16:19:07 -0500
commit04e7712f4460585e5eed5b853fd8b82a9943958f (patch)
treefe01703eff2d685421e75d2a6e9e27d3b9707aa8
parent7a35397f8c06bffd4c747537095321ff971045a5 (diff)
y2038: futex: Move compat implementation into futex.c
We are going to share the compat_sys_futex() handler between 64-bit architectures and 32-bit architectures that need to deal with both 32-bit and 64-bit time_t, and this is easier if both entry points are in the same file. In fact, most other system call handlers do the same thing these days, so let's follow the trend here and merge all of futex_compat.c into futex.c. In the process, a few minor changes have to be done to make sure everything still makes sense: handle_futex_death() and futex_cmpxchg_enabled() become local symbol, and the compat version of the fetch_robust_entry() function gets renamed to compat_fetch_robust_entry() to avoid a symbol clash. This is intended as a purely cosmetic patch, no behavior should change. Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-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}