xref: /openbmc/linux/kernel/kcmp.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
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