The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kernel/kcmp.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: c848bb46b35031e078bf482454a02ee1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.