linux/kernel/exec_domain.c
<<
>>
Prefs
   1/*
   2 * Handling of different ABIs (personalities).
   3 *
   4 * We group personalities into execution domains which have their
   5 * own handlers for kernel entry points, signal mapping, etc...
   6 *
   7 * 2001-05-06   Complete rewrite,  Christoph Hellwig (hch@infradead.org)
   8 */
   9
  10#include <linux/init.h>
  11#include <linux/kernel.h>
  12#include <linux/kmod.h>
  13#include <linux/module.h>
  14#include <linux/personality.h>
  15#include <linux/sched.h>
  16#include <linux/syscalls.h>
  17#include <linux/sysctl.h>
  18#include <linux/types.h>
  19
  20
  21static void default_handler(int, struct pt_regs *);
  22
  23static struct exec_domain *exec_domains = &default_exec_domain;
  24static DEFINE_RWLOCK(exec_domains_lock);
  25
  26
  27static u_long ident_map[32] = {
  28        0,      1,      2,      3,      4,      5,      6,      7,
  29        8,      9,      10,     11,     12,     13,     14,     15,
  30        16,     17,     18,     19,     20,     21,     22,     23,
  31        24,     25,     26,     27,     28,     29,     30,     31
  32};
  33
  34struct exec_domain default_exec_domain = {
  35        .name           = "Linux",              /* name */
  36        .handler        = default_handler,      /* lcall7 causes a seg fault. */
  37        .pers_low       = 0,                    /* PER_LINUX personality. */
  38        .pers_high      = 0,                    /* PER_LINUX personality. */
  39        .signal_map     = ident_map,            /* Identity map signals. */
  40        .signal_invmap  = ident_map,            /*  - both ways. */
  41};
  42
  43
  44static void
  45default_handler(int segment, struct pt_regs *regp)
  46{
  47        set_personality(0);
  48
  49        if (current_thread_info()->exec_domain->handler != default_handler)
  50                current_thread_info()->exec_domain->handler(segment, regp);
  51        else
  52                send_sig(SIGSEGV, current, 1);
  53}
  54
  55static struct exec_domain *
  56lookup_exec_domain(u_long personality)
  57{
  58        struct exec_domain *    ep;
  59        u_long                  pers = personality(personality);
  60                
  61        read_lock(&exec_domains_lock);
  62        for (ep = exec_domains; ep; ep = ep->next) {
  63                if (pers >= ep->pers_low && pers <= ep->pers_high)
  64                        if (try_module_get(ep->module))
  65                                goto out;
  66        }
  67
  68#ifdef CONFIG_KMOD
  69        read_unlock(&exec_domains_lock);
  70        request_module("personality-%ld", pers);
  71        read_lock(&exec_domains_lock);
  72
  73        for (ep = exec_domains; ep; ep = ep->next) {
  74                if (pers >= ep->pers_low && pers <= ep->pers_high)
  75                        if (try_module_get(ep->module))
  76                                goto out;
  77        }
  78#endif
  79
  80        ep = &default_exec_domain;
  81out:
  82        read_unlock(&exec_domains_lock);
  83        return (ep);
  84}
  85
  86int
  87register_exec_domain(struct exec_domain *ep)
  88{
  89        struct exec_domain      *tmp;
  90        int                     err = -EBUSY;
  91
  92        if (ep == NULL)
  93                return -EINVAL;
  94
  95        if (ep->next != NULL)
  96                return -EBUSY;
  97
  98        write_lock(&exec_domains_lock);
  99        for (tmp = exec_domains; tmp; tmp = tmp->next) {
 100                if (tmp == ep)
 101                        goto out;
 102        }
 103
 104        ep->next = exec_domains;
 105        exec_domains = ep;
 106        err = 0;
 107
 108out:
 109        write_unlock(&exec_domains_lock);
 110        return (err);
 111}
 112
 113int
 114unregister_exec_domain(struct exec_domain *ep)
 115{
 116        struct exec_domain      **epp;
 117
 118        epp = &exec_domains;
 119        write_lock(&exec_domains_lock);
 120        for (epp = &exec_domains; *epp; epp = &(*epp)->next) {
 121                if (ep == *epp)
 122                        goto unregister;
 123        }
 124        write_unlock(&exec_domains_lock);
 125        return -EINVAL;
 126
 127unregister:
 128        *epp = ep->next;
 129        ep->next = NULL;
 130        write_unlock(&exec_domains_lock);
 131        return 0;
 132}
 133
 134int
 135__set_personality(u_long personality)
 136{
 137        struct exec_domain      *ep, *oep;
 138
 139        ep = lookup_exec_domain(personality);
 140        if (ep == current_thread_info()->exec_domain) {
 141                current->personality = personality;
 142                module_put(ep->module);
 143                return 0;
 144        }
 145
 146        if (atomic_read(&current->fs->count) != 1) {
 147                struct fs_struct *fsp, *ofsp;
 148
 149                fsp = copy_fs_struct(current->fs);
 150                if (fsp == NULL) {
 151                        module_put(ep->module);
 152                        return -ENOMEM;
 153                }
 154
 155                task_lock(current);
 156                ofsp = current->fs;
 157                current->fs = fsp;
 158                task_unlock(current);
 159
 160                put_fs_struct(ofsp);
 161        }
 162
 163        /*
 164         * At that point we are guaranteed to be the sole owner of
 165         * current->fs.
 166         */
 167
 168        current->personality = personality;
 169        oep = current_thread_info()->exec_domain;
 170        current_thread_info()->exec_domain = ep;
 171        set_fs_altroot();
 172
 173        module_put(oep->module);
 174        return 0;
 175}
 176
 177int
 178get_exec_domain_list(char *page)
 179{
 180        struct exec_domain      *ep;
 181        int                     len = 0;
 182
 183        read_lock(&exec_domains_lock);
 184        for (ep = exec_domains; ep && len < PAGE_SIZE - 80; ep = ep->next)
 185                len += sprintf(page + len, "%d-%d\t%-16s\t[%s]\n",
 186                               ep->pers_low, ep->pers_high, ep->name,
 187                               module_name(ep->module));
 188        read_unlock(&exec_domains_lock);
 189        return (len);
 190}
 191
 192asmlinkage long
 193sys_personality(u_long personality)
 194{
 195        u_long old = current->personality;
 196
 197        if (personality != 0xffffffff) {
 198                set_personality(personality);
 199                if (current->personality != personality)
 200                        return -EINVAL;
 201        }
 202
 203        return (long)old;
 204}
 205
 206
 207EXPORT_SYMBOL(register_exec_domain);
 208EXPORT_SYMBOL(unregister_exec_domain);
 209EXPORT_SYMBOL(__set_personality);
 210