linux-old/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/config.h>
  11#include <linux/init.h>
  12#include <linux/kernel.h>
  13#include <linux/kmod.h>
  14#include <linux/module.h>
  15#include <linux/personality.h>
  16#include <linux/sched.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 rwlock_t exec_domains_lock = RW_LOCK_UNLOCKED;
  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        "Linux",                /* name */
  36        default_handler,        /* lcall7 causes a seg fault. */
  37        0, 0,                   /* PER_LINUX personality. */
  38        ident_map,              /* Identity map signals. */
  39        ident_map,              /*  - both ways. */
  40};
  41
  42
  43static void
  44default_handler(int segment, struct pt_regs *regp)
  45{
  46        u_long                  pers = 0;
  47
  48        /*
  49         * This may have been a static linked SVr4 binary, so we would
  50         * have the personality set incorrectly. Or it might have been
  51         * a Solaris/x86 binary. We can tell which because the former
  52         * uses lcall7, while the latter used lcall 0x27.
  53         * Try to find or load the appropriate personality, and fall back
  54         * to just forcing a SEGV.
  55         *
  56         * XXX: this is IA32-specific and should be moved to the MD-tree.
  57         */
  58        switch (segment) {
  59#ifdef __i386__
  60        case 0x07:
  61                pers = abi_defhandler_lcall7;
  62                break;
  63        case 0x27:
  64                pers = PER_SOLARIS;
  65                break;
  66#endif
  67        }
  68        set_personality(pers);
  69
  70        if (current->exec_domain->handler != default_handler)
  71                current->exec_domain->handler(segment, regp);
  72        else
  73                send_sig(SIGSEGV, current, 1);
  74}
  75
  76static struct exec_domain *
  77lookup_exec_domain(u_long personality)
  78{
  79        struct exec_domain *    ep;
  80        u_long                  pers = personality(personality);
  81                
  82        read_lock(&exec_domains_lock);
  83        for (ep = exec_domains; ep; ep = ep->next) {
  84                if (pers >= ep->pers_low && pers <= ep->pers_high)
  85                        if (try_inc_mod_count(ep->module))
  86                                goto out;
  87        }
  88
  89#ifdef CONFIG_KMOD
  90        read_unlock(&exec_domains_lock);
  91        {
  92                char buffer[30];
  93                sprintf(buffer, "personality-%ld", pers);
  94                request_module(buffer);
  95        }
  96        read_lock(&exec_domains_lock);
  97
  98        for (ep = exec_domains; ep; ep = ep->next) {
  99                if (pers >= ep->pers_low && pers <= ep->pers_high)
 100                        if (try_inc_mod_count(ep->module))
 101                                goto out;
 102        }
 103#endif
 104
 105        ep = &default_exec_domain;
 106out:
 107        read_unlock(&exec_domains_lock);
 108        return (ep);
 109}
 110
 111int
 112register_exec_domain(struct exec_domain *ep)
 113{
 114        struct exec_domain      *tmp;
 115        int                     err = -EBUSY;
 116
 117        if (ep == NULL)
 118                return -EINVAL;
 119
 120        if (ep->next != NULL)
 121                return -EBUSY;
 122
 123        write_lock(&exec_domains_lock);
 124        for (tmp = exec_domains; tmp; tmp = tmp->next) {
 125                if (tmp == ep)
 126                        goto out;
 127        }
 128
 129        ep->next = exec_domains;
 130        exec_domains = ep;
 131        err = 0;
 132
 133out:
 134        write_unlock(&exec_domains_lock);
 135        return (err);
 136}
 137
 138int
 139unregister_exec_domain(struct exec_domain *ep)
 140{
 141        struct exec_domain      **epp;
 142
 143        epp = &exec_domains;
 144        write_lock(&exec_domains_lock);
 145        for (epp = &exec_domains; *epp; epp = &(*epp)->next) {
 146                if (ep == *epp)
 147                        goto unregister;
 148        }
 149        write_unlock(&exec_domains_lock);
 150        return -EINVAL;
 151
 152unregister:
 153        *epp = ep->next;
 154        ep->next = NULL;
 155        write_unlock(&exec_domains_lock);
 156        return 0;
 157}
 158
 159int
 160__set_personality(u_long personality)
 161{
 162        struct exec_domain      *ep, *oep;
 163
 164        ep = lookup_exec_domain(personality);
 165        if (ep == current->exec_domain) {
 166                current->personality = personality;
 167                return 0;
 168        }
 169
 170        if (atomic_read(&current->fs->count) != 1) {
 171                struct fs_struct *fsp, *ofsp;
 172
 173                fsp = copy_fs_struct(current->fs);
 174                if (fsp == NULL) {
 175                        put_exec_domain(ep);
 176                        return -ENOMEM;;
 177                }
 178
 179                task_lock(current);
 180                ofsp = current->fs;
 181                current->fs = fsp;
 182                task_unlock(current);
 183
 184                put_fs_struct(ofsp);
 185        }
 186
 187        /*
 188         * At that point we are guaranteed to be the sole owner of
 189         * current->fs.
 190         */
 191
 192        current->personality = personality;
 193        oep = current->exec_domain;
 194        current->exec_domain = ep;
 195        set_fs_altroot();
 196
 197        put_exec_domain(oep);
 198
 199        return 0;
 200}
 201
 202int
 203get_exec_domain_list(char *page)
 204{
 205        struct exec_domain      *ep;
 206        int                     len = 0;
 207
 208        read_lock(&exec_domains_lock);
 209        for (ep = exec_domains; ep && len < PAGE_SIZE - 80; ep = ep->next)
 210                len += sprintf(page + len, "%d-%d\t%-16s\t[%s]\n",
 211                        ep->pers_low, ep->pers_high, ep->name,
 212                        ep->module ? ep->module->name : "kernel");
 213        read_unlock(&exec_domains_lock);
 214        return (len);
 215}
 216
 217asmlinkage long
 218sys_personality(u_long personality)
 219{
 220        u_long old = current->personality;;
 221
 222        if (personality != 0xffffffff) {
 223                set_personality(personality);
 224                if (current->personality != personality)
 225                        return -EINVAL;
 226        }
 227
 228        return (long)old;
 229}
 230
 231
 232EXPORT_SYMBOL(register_exec_domain);
 233EXPORT_SYMBOL(unregister_exec_domain);
 234EXPORT_SYMBOL(__set_personality);
 235
 236/*
 237 * We have to have all sysctl handling for the Linux-ABI
 238 * in one place as the dynamic registration of sysctls is
 239 * horribly crufty in Linux <= 2.4.
 240 *
 241 * I hope the new sysctl schemes discussed for future versions
 242 * will obsolete this.
 243 *
 244 *                              --hch
 245 */
 246
 247u_long abi_defhandler_coff = PER_SCOSVR3;
 248u_long abi_defhandler_elf = PER_LINUX;
 249u_long abi_defhandler_lcall7 = PER_SVR4;
 250u_long abi_defhandler_libcso = PER_SVR4;
 251u_int abi_traceflg;
 252int abi_fake_utsname;
 253
 254static struct ctl_table abi_table[] = {
 255        {ABI_DEFHANDLER_COFF, "defhandler_coff", &abi_defhandler_coff,
 256                sizeof(int), 0644, NULL, &proc_doulongvec_minmax},
 257        {ABI_DEFHANDLER_ELF, "defhandler_elf", &abi_defhandler_elf,
 258                sizeof(int), 0644, NULL, &proc_doulongvec_minmax},
 259        {ABI_DEFHANDLER_LCALL7, "defhandler_lcall7", &abi_defhandler_lcall7,
 260                sizeof(int), 0644, NULL, &proc_doulongvec_minmax},
 261        {ABI_DEFHANDLER_LIBCSO, "defhandler_libcso", &abi_defhandler_libcso,
 262                sizeof(int), 0644, NULL, &proc_doulongvec_minmax},
 263        {ABI_TRACE, "trace", &abi_traceflg,
 264                sizeof(u_int), 0644, NULL, &proc_dointvec},
 265        {ABI_FAKE_UTSNAME, "fake_utsname", &abi_fake_utsname,
 266                sizeof(int), 0644, NULL, &proc_dointvec},
 267        {0}
 268};
 269
 270static struct ctl_table abi_root_table[] = {
 271        {CTL_ABI, "abi", NULL, 0, 0555, abi_table},
 272        {0}
 273};
 274
 275static int __init
 276abi_register_sysctl(void)
 277{
 278        register_sysctl_table(abi_root_table, 1);
 279        return 0;
 280}
 281
 282__initcall(abi_register_sysctl);
 283
 284
 285EXPORT_SYMBOL(abi_defhandler_coff);
 286EXPORT_SYMBOL(abi_defhandler_elf);
 287EXPORT_SYMBOL(abi_defhandler_lcall7);
 288EXPORT_SYMBOL(abi_defhandler_libcso);
 289EXPORT_SYMBOL(abi_traceflg);
 290EXPORT_SYMBOL(abi_fake_utsname);
 291
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.