cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

kcmp.c (5519B)


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