diff options
-rw-r--r-- | include/linux/futex.h | 8 | ||||
-rw-r--r-- | kernel/Makefile | 3 | ||||
-rw-r--r-- | kernel/futex.c | 195 | ||||
-rw-r--r-- | kernel/futex_compat.c | 202 |
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; | |||
9 | struct mm_struct; | 9 | struct mm_struct; |
10 | struct task_struct; | 10 | struct task_struct; |
11 | 11 | ||
12 | extern int | ||
13 | handle_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 | ||
56 | long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout, | 53 | long 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 | ||
61 | extern int futex_cmpxchg_enabled; | ||
62 | #endif | ||
63 | #else | 55 | #else |
64 | static inline void exit_robust_list(struct task_struct *curr) | 56 | static 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 | |||
49 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 49 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
50 | obj-y += time/ | 50 | obj-y += time/ |
51 | obj-$(CONFIG_FUTEX) += futex.o | 51 | obj-$(CONFIG_FUTEX) += futex.o |
52 | ifeq ($(CONFIG_COMPAT),y) | ||
53 | obj-$(CONFIG_FUTEX) += futex_compat.o | ||
54 | endif | ||
55 | obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o | 52 | obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o |
56 | obj-$(CONFIG_SMP) += smp.o | 53 | obj-$(CONFIG_SMP) += smp.o |
57 | ifneq ($(CONFIG_SMP),y) | 54 | ifneq ($(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 |
177 | int __read_mostly futex_cmpxchg_enabled; | 178 | #define futex_cmpxchg_enabled 1 |
179 | #else | ||
180 | static 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 | */ |
3363 | int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi) | 3366 | static 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 | */ | ||
3599 | static inline int | ||
3600 | compat_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 | |||
3612 | static 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 | */ | ||
3627 | void 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 | |||
3697 | COMPAT_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 | |||
3712 | COMPAT_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 | |||
3745 | err_unlock: | ||
3746 | rcu_read_unlock(); | ||
3747 | |||
3748 | return ret; | ||
3749 | } | ||
3750 | |||
3751 | COMPAT_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 | |||
3592 | static void __init futex_detect_cmpxchg(void) | 3781 | static 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 | */ | ||
23 | static inline int | ||
24 | fetch_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 | |||
36 | static 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 | */ | ||
51 | void 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 | |||
121 | COMPAT_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 | |||
136 | COMPAT_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 | |||
169 | err_unlock: | ||
170 | rcu_read_unlock(); | ||
171 | |||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | COMPAT_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 | } | ||