1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2d97b46a6SCyrill Gorcunov #include <linux/kernel.h>
3d97b46a6SCyrill Gorcunov #include <linux/syscalls.h>
4d97b46a6SCyrill Gorcunov #include <linux/fdtable.h>
5d97b46a6SCyrill Gorcunov #include <linux/string.h>
6d97b46a6SCyrill Gorcunov #include <linux/random.h>
7d97b46a6SCyrill Gorcunov #include <linux/module.h>
844fd07e9SCyrill Gorcunov #include <linux/ptrace.h>
9d97b46a6SCyrill Gorcunov #include <linux/init.h>
10d97b46a6SCyrill Gorcunov #include <linux/errno.h>
11d97b46a6SCyrill Gorcunov #include <linux/cache.h>
12d97b46a6SCyrill Gorcunov #include <linux/bug.h>
13d97b46a6SCyrill Gorcunov #include <linux/err.h>
14d97b46a6SCyrill Gorcunov #include <linux/kcmp.h>
150791e364SCyrill Gorcunov #include <linux/capability.h>
160791e364SCyrill Gorcunov #include <linux/list.h>
170791e364SCyrill Gorcunov #include <linux/eventpoll.h>
180791e364SCyrill Gorcunov #include <linux/file.h>
19d97b46a6SCyrill Gorcunov
20d97b46a6SCyrill Gorcunov #include <asm/unistd.h>
21d97b46a6SCyrill Gorcunov
22d97b46a6SCyrill Gorcunov /*
23d97b46a6SCyrill Gorcunov * We don't expose the real in-memory order of objects for security reasons.
24d97b46a6SCyrill Gorcunov * But still the comparison results should be suitable for sorting. So we
25d97b46a6SCyrill Gorcunov * obfuscate kernel pointers values and compare the production instead.
26d97b46a6SCyrill Gorcunov *
27d97b46a6SCyrill Gorcunov * The obfuscation is done in two steps. First we xor the kernel pointer with
28d97b46a6SCyrill Gorcunov * a random value, which puts pointer into a new position in a reordered space.
29d97b46a6SCyrill Gorcunov * Secondly we multiply the xor production with a large odd random number to
30d97b46a6SCyrill Gorcunov * permute its bits even more (the odd multiplier guarantees that the product
31d97b46a6SCyrill Gorcunov * is unique ever after the high bits are truncated, since any odd number is
32d97b46a6SCyrill Gorcunov * relative prime to 2^n).
33d97b46a6SCyrill Gorcunov *
34d97b46a6SCyrill Gorcunov * Note also that the obfuscation itself is invisible to userspace and if needed
35d97b46a6SCyrill Gorcunov * it can be changed to an alternate scheme.
36d97b46a6SCyrill Gorcunov */
37d97b46a6SCyrill Gorcunov static unsigned long cookies[KCMP_TYPES][2] __read_mostly;
38d97b46a6SCyrill Gorcunov
kptr_obfuscate(long v,int type)39d97b46a6SCyrill Gorcunov static long kptr_obfuscate(long v, int type)
40d97b46a6SCyrill Gorcunov {
41d97b46a6SCyrill Gorcunov return (v ^ cookies[type][0]) * cookies[type][1];
42d97b46a6SCyrill Gorcunov }
43d97b46a6SCyrill Gorcunov
44d97b46a6SCyrill Gorcunov /*
45d97b46a6SCyrill Gorcunov * 0 - equal, i.e. v1 = v2
46d97b46a6SCyrill Gorcunov * 1 - less than, i.e. v1 < v2
47d97b46a6SCyrill Gorcunov * 2 - greater than, i.e. v1 > v2
48d97b46a6SCyrill Gorcunov * 3 - not equal but ordering unavailable (reserved for future)
49d97b46a6SCyrill Gorcunov */
kcmp_ptr(void * v1,void * v2,enum kcmp_type type)50d97b46a6SCyrill Gorcunov static int kcmp_ptr(void *v1, void *v2, enum kcmp_type type)
51d97b46a6SCyrill Gorcunov {
52acbbe6fbSRasmus Villemoes long t1, t2;
53d97b46a6SCyrill Gorcunov
54acbbe6fbSRasmus Villemoes t1 = kptr_obfuscate((long)v1, type);
55acbbe6fbSRasmus Villemoes t2 = kptr_obfuscate((long)v2, type);
56d97b46a6SCyrill Gorcunov
57acbbe6fbSRasmus Villemoes return (t1 < t2) | ((t1 > t2) << 1);
58d97b46a6SCyrill Gorcunov }
59d97b46a6SCyrill Gorcunov
60d97b46a6SCyrill Gorcunov /* The caller must have pinned the task */
61d97b46a6SCyrill Gorcunov static struct file *
get_file_raw_ptr(struct task_struct * task,unsigned int idx)62d97b46a6SCyrill Gorcunov get_file_raw_ptr(struct task_struct *task, unsigned int idx)
63d97b46a6SCyrill Gorcunov {
64ed77e80eSEric W. Biederman struct file *file;
65d97b46a6SCyrill Gorcunov
66d97b46a6SCyrill Gorcunov rcu_read_lock();
67ed77e80eSEric W. Biederman file = task_lookup_fd_rcu(task, idx);
68d97b46a6SCyrill Gorcunov rcu_read_unlock();
69d97b46a6SCyrill Gorcunov
70d97b46a6SCyrill Gorcunov return file;
71d97b46a6SCyrill Gorcunov }
72d97b46a6SCyrill Gorcunov
kcmp_unlock(struct rw_semaphore * l1,struct rw_semaphore * l2)73*f7cfd871SEric W. Biederman static void kcmp_unlock(struct rw_semaphore *l1, struct rw_semaphore *l2)
74d97b46a6SCyrill Gorcunov {
75*f7cfd871SEric W. Biederman if (likely(l2 != l1))
76*f7cfd871SEric W. Biederman up_read(l2);
77*f7cfd871SEric W. Biederman up_read(l1);
78d97b46a6SCyrill Gorcunov }
79d97b46a6SCyrill Gorcunov
kcmp_lock(struct rw_semaphore * l1,struct rw_semaphore * l2)80*f7cfd871SEric W. Biederman static int kcmp_lock(struct rw_semaphore *l1, struct rw_semaphore *l2)
81d97b46a6SCyrill Gorcunov {
82d97b46a6SCyrill Gorcunov int err;
83d97b46a6SCyrill Gorcunov
84*f7cfd871SEric W. Biederman if (l2 > l1)
85*f7cfd871SEric W. Biederman swap(l1, l2);
86d97b46a6SCyrill Gorcunov
87*f7cfd871SEric W. Biederman err = down_read_killable(l1);
88*f7cfd871SEric W. Biederman if (!err && likely(l1 != l2)) {
89*f7cfd871SEric W. Biederman err = down_read_killable_nested(l2, SINGLE_DEPTH_NESTING);
90d97b46a6SCyrill Gorcunov if (err)
91*f7cfd871SEric W. Biederman up_read(l1);
92d97b46a6SCyrill Gorcunov }
93d97b46a6SCyrill Gorcunov
94d97b46a6SCyrill Gorcunov return err;
95d97b46a6SCyrill Gorcunov }
96d97b46a6SCyrill Gorcunov
970791e364SCyrill Gorcunov #ifdef CONFIG_EPOLL
kcmp_epoll_target(struct task_struct * task1,struct task_struct * task2,unsigned long idx1,struct kcmp_epoll_slot __user * uslot)980791e364SCyrill Gorcunov static int kcmp_epoll_target(struct task_struct *task1,
990791e364SCyrill Gorcunov struct task_struct *task2,
1000791e364SCyrill Gorcunov unsigned long idx1,
1010791e364SCyrill Gorcunov struct kcmp_epoll_slot __user *uslot)
1020791e364SCyrill Gorcunov {
1030791e364SCyrill Gorcunov struct file *filp, *filp_epoll, *filp_tgt;
1040791e364SCyrill Gorcunov struct kcmp_epoll_slot slot;
1050791e364SCyrill Gorcunov
1060791e364SCyrill Gorcunov if (copy_from_user(&slot, uslot, sizeof(slot)))
1070791e364SCyrill Gorcunov return -EFAULT;
1080791e364SCyrill Gorcunov
1090791e364SCyrill Gorcunov filp = get_file_raw_ptr(task1, idx1);
1100791e364SCyrill Gorcunov if (!filp)
1110791e364SCyrill Gorcunov return -EBADF;
1120791e364SCyrill Gorcunov
113f43c283aSEric W. Biederman filp_epoll = fget_task(task2, slot.efd);
114f43c283aSEric W. Biederman if (!filp_epoll)
1150791e364SCyrill Gorcunov return -EBADF;
1160791e364SCyrill Gorcunov
1170791e364SCyrill Gorcunov filp_tgt = get_epoll_tfile_raw_ptr(filp_epoll, slot.tfd, slot.toff);
1180791e364SCyrill Gorcunov fput(filp_epoll);
1190791e364SCyrill Gorcunov
1200791e364SCyrill Gorcunov if (IS_ERR(filp_tgt))
1210791e364SCyrill Gorcunov return PTR_ERR(filp_tgt);
1220791e364SCyrill Gorcunov
1230791e364SCyrill Gorcunov return kcmp_ptr(filp, filp_tgt, KCMP_FILE);
1240791e364SCyrill Gorcunov }
1250791e364SCyrill Gorcunov #else
kcmp_epoll_target(struct task_struct * task1,struct task_struct * task2,unsigned long idx1,struct kcmp_epoll_slot __user * uslot)1260791e364SCyrill Gorcunov static int kcmp_epoll_target(struct task_struct *task1,
1270791e364SCyrill Gorcunov struct task_struct *task2,
1280791e364SCyrill Gorcunov unsigned long idx1,
1290791e364SCyrill Gorcunov struct kcmp_epoll_slot __user *uslot)
1300791e364SCyrill Gorcunov {
1310791e364SCyrill Gorcunov return -EOPNOTSUPP;
1320791e364SCyrill Gorcunov }
1330791e364SCyrill Gorcunov #endif
1340791e364SCyrill Gorcunov
SYSCALL_DEFINE5(kcmp,pid_t,pid1,pid_t,pid2,int,type,unsigned long,idx1,unsigned long,idx2)135d97b46a6SCyrill Gorcunov SYSCALL_DEFINE5(kcmp, pid_t, pid1, pid_t, pid2, int, type,
136d97b46a6SCyrill Gorcunov unsigned long, idx1, unsigned long, idx2)
137d97b46a6SCyrill Gorcunov {
138d97b46a6SCyrill Gorcunov struct task_struct *task1, *task2;
139d97b46a6SCyrill Gorcunov int ret;
140d97b46a6SCyrill Gorcunov
141d97b46a6SCyrill Gorcunov rcu_read_lock();
142d97b46a6SCyrill Gorcunov
143d97b46a6SCyrill Gorcunov /*
144d97b46a6SCyrill Gorcunov * Tasks are looked up in caller's PID namespace only.
145d97b46a6SCyrill Gorcunov */
146d97b46a6SCyrill Gorcunov task1 = find_task_by_vpid(pid1);
147d97b46a6SCyrill Gorcunov task2 = find_task_by_vpid(pid2);
148d97b46a6SCyrill Gorcunov if (!task1 || !task2)
149d97b46a6SCyrill Gorcunov goto err_no_task;
150d97b46a6SCyrill Gorcunov
151d97b46a6SCyrill Gorcunov get_task_struct(task1);
152d97b46a6SCyrill Gorcunov get_task_struct(task2);
153d97b46a6SCyrill Gorcunov
154d97b46a6SCyrill Gorcunov rcu_read_unlock();
155d97b46a6SCyrill Gorcunov
156d97b46a6SCyrill Gorcunov /*
157d97b46a6SCyrill Gorcunov * One should have enough rights to inspect task details.
158d97b46a6SCyrill Gorcunov */
159*f7cfd871SEric W. Biederman ret = kcmp_lock(&task1->signal->exec_update_lock,
160*f7cfd871SEric W. Biederman &task2->signal->exec_update_lock);
161d97b46a6SCyrill Gorcunov if (ret)
162d97b46a6SCyrill Gorcunov goto err;
163caaee623SJann Horn if (!ptrace_may_access(task1, PTRACE_MODE_READ_REALCREDS) ||
164caaee623SJann Horn !ptrace_may_access(task2, PTRACE_MODE_READ_REALCREDS)) {
165d97b46a6SCyrill Gorcunov ret = -EPERM;
166d97b46a6SCyrill Gorcunov goto err_unlock;
167d97b46a6SCyrill Gorcunov }
168d97b46a6SCyrill Gorcunov
169d97b46a6SCyrill Gorcunov switch (type) {
170d97b46a6SCyrill Gorcunov case KCMP_FILE: {
171d97b46a6SCyrill Gorcunov struct file *filp1, *filp2;
172d97b46a6SCyrill Gorcunov
173d97b46a6SCyrill Gorcunov filp1 = get_file_raw_ptr(task1, idx1);
174d97b46a6SCyrill Gorcunov filp2 = get_file_raw_ptr(task2, idx2);
175d97b46a6SCyrill Gorcunov
176d97b46a6SCyrill Gorcunov if (filp1 && filp2)
177d97b46a6SCyrill Gorcunov ret = kcmp_ptr(filp1, filp2, KCMP_FILE);
178d97b46a6SCyrill Gorcunov else
179d97b46a6SCyrill Gorcunov ret = -EBADF;
180d97b46a6SCyrill Gorcunov break;
181d97b46a6SCyrill Gorcunov }
182d97b46a6SCyrill Gorcunov case KCMP_VM:
183d97b46a6SCyrill Gorcunov ret = kcmp_ptr(task1->mm, task2->mm, KCMP_VM);
184d97b46a6SCyrill Gorcunov break;
185d97b46a6SCyrill Gorcunov case KCMP_FILES:
186d97b46a6SCyrill Gorcunov ret = kcmp_ptr(task1->files, task2->files, KCMP_FILES);
187d97b46a6SCyrill Gorcunov break;
188d97b46a6SCyrill Gorcunov case KCMP_FS:
189d97b46a6SCyrill Gorcunov ret = kcmp_ptr(task1->fs, task2->fs, KCMP_FS);
190d97b46a6SCyrill Gorcunov break;
191d97b46a6SCyrill Gorcunov case KCMP_SIGHAND:
192d97b46a6SCyrill Gorcunov ret = kcmp_ptr(task1->sighand, task2->sighand, KCMP_SIGHAND);
193d97b46a6SCyrill Gorcunov break;
194d97b46a6SCyrill Gorcunov case KCMP_IO:
195d97b46a6SCyrill Gorcunov ret = kcmp_ptr(task1->io_context, task2->io_context, KCMP_IO);
196d97b46a6SCyrill Gorcunov break;
197d97b46a6SCyrill Gorcunov case KCMP_SYSVSEM:
198d97b46a6SCyrill Gorcunov #ifdef CONFIG_SYSVIPC
199d97b46a6SCyrill Gorcunov ret = kcmp_ptr(task1->sysvsem.undo_list,
200d97b46a6SCyrill Gorcunov task2->sysvsem.undo_list,
201d97b46a6SCyrill Gorcunov KCMP_SYSVSEM);
202d97b46a6SCyrill Gorcunov #else
203d97b46a6SCyrill Gorcunov ret = -EOPNOTSUPP;
204d97b46a6SCyrill Gorcunov #endif
205d97b46a6SCyrill Gorcunov break;
2060791e364SCyrill Gorcunov case KCMP_EPOLL_TFD:
2070791e364SCyrill Gorcunov ret = kcmp_epoll_target(task1, task2, idx1, (void *)idx2);
2080791e364SCyrill Gorcunov break;
209d97b46a6SCyrill Gorcunov default:
210d97b46a6SCyrill Gorcunov ret = -EINVAL;
211d97b46a6SCyrill Gorcunov break;
212d97b46a6SCyrill Gorcunov }
213d97b46a6SCyrill Gorcunov
214d97b46a6SCyrill Gorcunov err_unlock:
215*f7cfd871SEric W. Biederman kcmp_unlock(&task1->signal->exec_update_lock,
216*f7cfd871SEric W. Biederman &task2->signal->exec_update_lock);
217d97b46a6SCyrill Gorcunov err:
218d97b46a6SCyrill Gorcunov put_task_struct(task1);
219d97b46a6SCyrill Gorcunov put_task_struct(task2);
220d97b46a6SCyrill Gorcunov
221d97b46a6SCyrill Gorcunov return ret;
222d97b46a6SCyrill Gorcunov
223d97b46a6SCyrill Gorcunov err_no_task:
224d97b46a6SCyrill Gorcunov rcu_read_unlock();
225d97b46a6SCyrill Gorcunov return -ESRCH;
226d97b46a6SCyrill Gorcunov }
227d97b46a6SCyrill Gorcunov
kcmp_cookies_init(void)228d97b46a6SCyrill Gorcunov static __init int kcmp_cookies_init(void)
229d97b46a6SCyrill Gorcunov {
230d97b46a6SCyrill Gorcunov int i;
231d97b46a6SCyrill Gorcunov
232d97b46a6SCyrill Gorcunov get_random_bytes(cookies, sizeof(cookies));
233d97b46a6SCyrill Gorcunov
234d97b46a6SCyrill Gorcunov for (i = 0; i < KCMP_TYPES; i++)
235d97b46a6SCyrill Gorcunov cookies[i][1] |= (~(~0UL >> 1) | 1);
236d97b46a6SCyrill Gorcunov
237d97b46a6SCyrill Gorcunov return 0;
238d97b46a6SCyrill Gorcunov }
239d97b46a6SCyrill Gorcunov arch_initcall(kcmp_cookies_init);
240