linux-bk/fs/binfmt_misc.c
<<
>>
Prefs
   1/*
   2 *  binfmt_misc.c
   3 *
   4 *  Copyright (C) 1997 Richard Günther
   5 *
   6 *  binfmt_misc detects binaries via a magic or filename extension and invokes
   7 *  a specified wrapper. This should obsolete binfmt_java, binfmt_em86 and
   8 *  binfmt_mz.
   9 *
  10 *  1997-04-25 first version
  11 *  [...]
  12 *  1997-05-19 cleanup
  13 *  1997-06-26 hpa: pass the real filename rather than argv[0]
  14 *  1997-06-30 minor cleanup
  15 *  1997-08-09 removed extension stripping, locking cleanup
  16 *  2001-02-28 AV: rewritten into something that resembles C. Original didn't.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/init.h>
  21
  22#include <linux/binfmts.h>
  23#include <linux/slab.h>
  24#include <linux/ctype.h>
  25#include <linux/file.h>
  26#include <linux/pagemap.h>
  27#include <linux/namei.h>
  28#include <linux/mount.h>
  29
  30#include <asm/uaccess.h>
  31
  32enum {
  33        VERBOSE_STATUS = 1 /* make it zero to save 400 bytes kernel memory */
  34};
  35
  36static LIST_HEAD(entries);
  37static int enabled = 1;
  38
  39enum {Enabled, Magic};
  40#define MISC_FMT_PRESERVE_ARGV0 (1<<31)
  41
  42typedef struct {
  43        struct list_head list;
  44        unsigned long flags;            /* type, status, etc. */
  45        int offset;                     /* offset of magic */
  46        int size;                       /* size of magic/mask */
  47        char *magic;                    /* magic or filename extension */
  48        char *mask;                     /* mask, NULL for exact match */
  49        char *interpreter;              /* filename of interpreter */
  50        char *name;
  51        struct dentry *dentry;
  52} Node;
  53
  54static rwlock_t entries_lock __attribute__((unused)) = RW_LOCK_UNLOCKED;
  55static struct vfsmount *bm_mnt;
  56static int entry_count;
  57
  58/* 
  59 * Check if we support the binfmt
  60 * if we do, return the node, else NULL
  61 * locking is done in load_misc_binary
  62 */
  63static Node *check_file(struct linux_binprm *bprm)
  64{
  65        char *p = strrchr(bprm->interp, '.');
  66        struct list_head *l;
  67
  68        list_for_each(l, &entries) {
  69                Node *e = list_entry(l, Node, list);
  70                char *s;
  71                int j;
  72
  73                if (!test_bit(Enabled, &e->flags))
  74                        continue;
  75
  76                if (!test_bit(Magic, &e->flags)) {
  77                        if (p && !strcmp(e->magic, p + 1))
  78                                return e;
  79                        continue;
  80                }
  81
  82                s = bprm->buf + e->offset;
  83                if (e->mask) {
  84                        for (j = 0; j < e->size; j++)
  85                                if ((*s++ ^ e->magic[j]) & e->mask[j])
  86                                        break;
  87                } else {
  88                        for (j = 0; j < e->size; j++)
  89                                if ((*s++ ^ e->magic[j]))
  90                                        break;
  91                }
  92                if (j == e->size)
  93                        return e;
  94        }
  95        return NULL;
  96}
  97
  98/*
  99 * the loader itself
 100 */
 101static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 102{
 103        Node *fmt;
 104        struct file * file;
 105        char iname[BINPRM_BUF_SIZE];
 106        char *iname_addr = iname;
 107        int retval;
 108
 109        retval = -ENOEXEC;
 110        if (!enabled)
 111                goto _ret;
 112
 113        /* to keep locking time low, we copy the interpreter string */
 114        read_lock(&entries_lock);
 115        fmt = check_file(bprm);
 116        if (fmt)
 117                strlcpy(iname, fmt->interpreter, BINPRM_BUF_SIZE);
 118        read_unlock(&entries_lock);
 119        if (!fmt)
 120                goto _ret;
 121
 122        allow_write_access(bprm->file);
 123        fput(bprm->file);
 124        bprm->file = NULL;
 125
 126        /* Build args for interpreter */
 127        if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) {
 128                remove_arg_zero(bprm);
 129        }
 130        retval = copy_strings_kernel(1, &bprm->interp, bprm);
 131        if (retval < 0) goto _ret; 
 132        bprm->argc++;
 133        retval = copy_strings_kernel(1, &iname_addr, bprm);
 134        if (retval < 0) goto _ret; 
 135        bprm->argc++;
 136        bprm->interp = iname;   /* for binfmt_script */
 137
 138        file = open_exec(iname);
 139        retval = PTR_ERR(file);
 140        if (IS_ERR(file))
 141                goto _ret;
 142        bprm->file = file;
 143
 144        retval = prepare_binprm(bprm);
 145        if (retval >= 0)
 146                retval = search_binary_handler(bprm, regs);
 147_ret:
 148        return retval;
 149}
 150
 151/* Command parsers */
 152
 153/*
 154 * parses and copies one argument enclosed in del from *sp to *dp,
 155 * recognising the \x special.
 156 * returns pointer to the copied argument or NULL in case of an
 157 * error (and sets err) or null argument length.
 158 */
 159static char *scanarg(char *s, char del)
 160{
 161        char c;
 162
 163        while ((c = *s++) != del) {
 164                if (c == '\\' && *s == 'x') {
 165                        s++;
 166                        if (!isxdigit(*s++))
 167                                return NULL;
 168                        if (!isxdigit(*s++))
 169                                return NULL;
 170                }
 171        }
 172        return s;
 173}
 174
 175static int unquote(char *from)
 176{
 177        char c = 0, *s = from, *p = from;
 178
 179        while ((c = *s++) != '\0') {
 180                if (c == '\\' && *s == 'x') {
 181                        s++;
 182                        c = toupper(*s++);
 183                        *p = (c - (isdigit(c) ? '0' : 'A' - 10)) << 4;
 184                        c = toupper(*s++);
 185                        *p++ |= c - (isdigit(c) ? '0' : 'A' - 10);
 186                        continue;
 187                }
 188                *p++ = c;
 189        }
 190        return p - from;
 191}
 192
 193/*
 194 * This registers a new binary format, it recognises the syntax
 195 * ':name:type:offset:magic:mask:interpreter:'
 196 * where the ':' is the IFS, that can be chosen with the first char
 197 */
 198static Node *create_entry(const char *buffer, size_t count)
 199{
 200        Node *e;
 201        int memsize, err;
 202        char *buf, *p;
 203        char del;
 204
 205        /* some sanity checks */
 206        err = -EINVAL;
 207        if ((count < 11) || (count > 256))
 208                goto out;
 209
 210        err = -ENOMEM;
 211        memsize = sizeof(Node) + count + 8;
 212        e = (Node *) kmalloc(memsize, GFP_USER);
 213        if (!e)
 214                goto out;
 215
 216        p = buf = (char *)e + sizeof(Node);
 217
 218        memset(e, 0, sizeof(Node));
 219        if (copy_from_user(buf, buffer, count))
 220                goto Efault;
 221
 222        del = *p++;     /* delimeter */
 223
 224        memset(buf+count, del, 8);
 225
 226        e->name = p;
 227        p = strchr(p, del);
 228        if (!p)
 229                goto Einval;
 230        *p++ = '\0';
 231        if (!e->name[0] ||
 232            !strcmp(e->name, ".") ||
 233            !strcmp(e->name, "..") ||
 234            strchr(e->name, '/'))
 235                goto Einval;
 236        switch (*p++) {
 237                case 'E': e->flags = 1<<Enabled; break;
 238                case 'M': e->flags = (1<<Enabled) | (1<<Magic); break;
 239                default: goto Einval;
 240        }
 241        if (*p++ != del)
 242                goto Einval;
 243        if (test_bit(Magic, &e->flags)) {
 244                char *s = strchr(p, del);
 245                if (!s)
 246                        goto Einval;
 247                *s++ = '\0';
 248                e->offset = simple_strtoul(p, &p, 10);
 249                if (*p++)
 250                        goto Einval;
 251                e->magic = p;
 252                p = scanarg(p, del);
 253                if (!p)
 254                        goto Einval;
 255                p[-1] = '\0';
 256                if (!e->magic[0])
 257                        goto Einval;
 258                e->mask = p;
 259                p = scanarg(p, del);
 260                if (!p)
 261                        goto Einval;
 262                p[-1] = '\0';
 263                if (!e->mask[0])
 264                        e->mask = NULL;
 265                e->size = unquote(e->magic);
 266                if (e->mask && unquote(e->mask) != e->size)
 267                        goto Einval;
 268                if (e->size + e->offset > BINPRM_BUF_SIZE)
 269                        goto Einval;
 270        } else {
 271                p = strchr(p, del);
 272                if (!p)
 273                        goto Einval;
 274                *p++ = '\0';
 275                e->magic = p;
 276                p = strchr(p, del);
 277                if (!p)
 278                        goto Einval;
 279                *p++ = '\0';
 280                if (!e->magic[0] || strchr(e->magic, '/'))
 281                        goto Einval;
 282                p = strchr(p, del);
 283                if (!p)
 284                        goto Einval;
 285                *p++ = '\0';
 286        }
 287        e->interpreter = p;
 288        p = strchr(p, del);
 289        if (!p)
 290                goto Einval;
 291        *p++ = '\0';
 292        if (!e->interpreter[0])
 293                goto Einval;
 294
 295        if (*p == 'P') {
 296                p++;
 297                e->flags |= MISC_FMT_PRESERVE_ARGV0;
 298        }
 299
 300        if (*p == '\n')
 301                p++;
 302        if (p != buf + count)
 303                goto Einval;
 304        return e;
 305
 306out:
 307        return ERR_PTR(err);
 308
 309Efault:
 310        kfree(e);
 311        return ERR_PTR(-EFAULT);
 312Einval:
 313        kfree(e);
 314        return ERR_PTR(-EINVAL);
 315}
 316
 317/*
 318 * Set status of entry/binfmt_misc:
 319 * '1' enables, '0' disables and '-1' clears entry/binfmt_misc
 320 */
 321static int parse_command(const char *buffer, size_t count)
 322{
 323        char s[4];
 324
 325        if (!count)
 326                return 0;
 327        if (count > 3)
 328                return -EINVAL;
 329        if (copy_from_user(s, buffer, count))
 330                return -EFAULT;
 331        if (s[count-1] == '\n')
 332                count--;
 333        if (count == 1 && s[0] == '0')
 334                return 1;
 335        if (count == 1 && s[0] == '1')
 336                return 2;
 337        if (count == 2 && s[0] == '-' && s[1] == '1')
 338                return 3;
 339        return -EINVAL;
 340}
 341
 342/* generic stuff */
 343
 344static void entry_status(Node *e, char *page)
 345{
 346        char *dp;
 347        char *status = "disabled";
 348
 349        if (test_bit(Enabled, &e->flags))
 350                status = "enabled";
 351
 352        if (!VERBOSE_STATUS) {
 353                sprintf(page, "%s\n", status);
 354                return;
 355        }
 356
 357        sprintf(page, "%s\ninterpreter %s\n", status, e->interpreter);
 358        dp = page + strlen(page);
 359        if (!test_bit(Magic, &e->flags)) {
 360                sprintf(dp, "extension .%s\n", e->magic);
 361        } else {
 362                int i;
 363
 364                sprintf(dp, "offset %i\nmagic ", e->offset);
 365                dp = page + strlen(page);
 366                for (i = 0; i < e->size; i++) {
 367                        sprintf(dp, "%02x", 0xff & (int) (e->magic[i]));
 368                        dp += 2;
 369                }
 370                if (e->mask) {
 371                        sprintf(dp, "\nmask ");
 372                        dp += 6;
 373                        for (i = 0; i < e->size; i++) {
 374                                sprintf(dp, "%02x", 0xff & (int) (e->mask[i]));
 375                                dp += 2;
 376                        }
 377                }
 378                *dp++ = '\n';
 379                *dp = '\0';
 380        }
 381}
 382
 383static struct inode *bm_get_inode(struct super_block *sb, int mode)
 384{
 385        struct inode * inode = new_inode(sb);
 386
 387        if (inode) {
 388                inode->i_mode = mode;
 389                inode->i_uid = 0;
 390                inode->i_gid = 0;
 391                inode->i_blksize = PAGE_CACHE_SIZE;
 392                inode->i_blocks = 0;
 393                inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 394        }
 395        return inode;
 396}
 397
 398static void bm_clear_inode(struct inode *inode)
 399{
 400        kfree(inode->u.generic_ip);
 401}
 402
 403static void kill_node(Node *e)
 404{
 405        struct dentry *dentry;
 406
 407        write_lock(&entries_lock);
 408        dentry = e->dentry;
 409        if (dentry) {
 410                list_del_init(&e->list);
 411                e->dentry = NULL;
 412        }
 413        write_unlock(&entries_lock);
 414
 415        if (dentry) {
 416                dentry->d_inode->i_nlink--;
 417                d_drop(dentry);
 418                dput(dentry);
 419                simple_release_fs(&bm_mnt, &entry_count);
 420        }
 421}
 422
 423/* /<entry> */
 424
 425static ssize_t
 426bm_entry_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
 427{
 428        Node *e = file->f_dentry->d_inode->u.generic_ip;
 429        loff_t pos = *ppos;
 430        ssize_t res;
 431        char *page;
 432        int len;
 433
 434        if (!(page = (char*) __get_free_page(GFP_KERNEL)))
 435                return -ENOMEM;
 436
 437        entry_status(e, page);
 438        len = strlen(page);
 439
 440        res = -EINVAL;
 441        if (pos < 0)
 442                goto out;
 443        res = 0;
 444        if (pos >= len)
 445                goto out;
 446        if (len < pos + nbytes)
 447                nbytes = len - pos;
 448        res = -EFAULT;
 449        if (copy_to_user(buf, page + pos, nbytes))
 450                goto out;
 451        *ppos = pos + nbytes;
 452        res = nbytes;
 453out:
 454        free_page((unsigned long) page);
 455        return res;
 456}
 457
 458static ssize_t bm_entry_write(struct file *file, const char *buffer,
 459                                size_t count, loff_t *ppos)
 460{
 461        struct dentry *root;
 462        Node *e = file->f_dentry->d_inode->u.generic_ip;
 463        int res = parse_command(buffer, count);
 464
 465        switch (res) {
 466                case 1: clear_bit(Enabled, &e->flags);
 467                        break;
 468                case 2: set_bit(Enabled, &e->flags);
 469                        break;
 470                case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root);
 471                        down(&root->d_inode->i_sem);
 472
 473                        kill_node(e);
 474
 475                        up(&root->d_inode->i_sem);
 476                        dput(root);
 477                        break;
 478                default: return res;
 479        }
 480        return count;
 481}
 482
 483static struct file_operations bm_entry_operations = {
 484        .read           = bm_entry_read,
 485        .write          = bm_entry_write,
 486};
 487
 488/* /register */
 489
 490static ssize_t bm_register_write(struct file *file, const char *buffer,
 491                               size_t count, loff_t *ppos)
 492{
 493        Node *e;
 494        struct inode *inode;
 495        struct dentry *root, *dentry;
 496        struct super_block *sb = file->f_vfsmnt->mnt_sb;
 497        int err = 0;
 498
 499        e = create_entry(buffer, count);
 500
 501        if (IS_ERR(e))
 502                return PTR_ERR(e);
 503
 504        root = dget(sb->s_root);
 505        down(&root->d_inode->i_sem);
 506        dentry = lookup_one_len(e->name, root, strlen(e->name));
 507        err = PTR_ERR(dentry);
 508        if (IS_ERR(dentry))
 509                goto out;
 510
 511        err = -EEXIST;
 512        if (dentry->d_inode)
 513                goto out2;
 514
 515        inode = bm_get_inode(sb, S_IFREG | 0644);
 516
 517        err = -ENOMEM;
 518        if (!inode)
 519                goto out2;
 520
 521        err = simple_pin_fs("binfmt_misc", &bm_mnt, &entry_count);
 522        if (err) {
 523                iput(inode);
 524                inode = NULL;
 525                goto out2;
 526        }
 527
 528        e->dentry = dget(dentry);
 529        inode->u.generic_ip = e;
 530        inode->i_fop = &bm_entry_operations;
 531
 532        d_instantiate(dentry, inode);
 533        write_lock(&entries_lock);
 534        list_add(&e->list, &entries);
 535        write_unlock(&entries_lock);
 536
 537        err = 0;
 538out2:
 539        dput(dentry);
 540out:
 541        up(&root->d_inode->i_sem);
 542        dput(root);
 543
 544        if (err) {
 545                kfree(e);
 546                return -EINVAL;
 547        }
 548        return count;
 549}
 550
 551static struct file_operations bm_register_operations = {
 552        .write          = bm_register_write,
 553};
 554
 555/* /status */
 556
 557static ssize_t
 558bm_status_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
 559{
 560        char *s = enabled ? "enabled" : "disabled";
 561        int len = strlen(s);
 562        loff_t pos = *ppos;
 563
 564        if (pos < 0)
 565                return -EINVAL;
 566        if (pos >= len)
 567                return 0;
 568        if (len < pos + nbytes)
 569                nbytes = len - pos;
 570        if (copy_to_user(buf, s + pos, nbytes))
 571                return -EFAULT;
 572        *ppos = pos + nbytes;
 573        return nbytes;
 574}
 575
 576static ssize_t bm_status_write(struct file * file, const char * buffer,
 577                size_t count, loff_t *ppos)
 578{
 579        int res = parse_command(buffer, count);
 580        struct dentry *root;
 581
 582        switch (res) {
 583                case 1: enabled = 0; break;
 584                case 2: enabled = 1; break;
 585                case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root);
 586                        down(&root->d_inode->i_sem);
 587
 588                        while (!list_empty(&entries))
 589                                kill_node(list_entry(entries.next, Node, list));
 590
 591                        up(&root->d_inode->i_sem);
 592                        dput(root);
 593                default: return res;
 594        }
 595        return count;
 596}
 597
 598static struct file_operations bm_status_operations = {
 599        .read           = bm_status_read,
 600        .write          = bm_status_write,
 601};
 602
 603/* Superblock handling */
 604
 605static struct super_operations s_ops = {
 606        .statfs         = simple_statfs,
 607        .clear_inode    = bm_clear_inode,
 608};
 609
 610static int bm_fill_super(struct super_block * sb, void * data, int silent)
 611{
 612        static struct tree_descr bm_files[] = {
 613                [1] = {"status", &bm_status_operations, S_IWUSR|S_IRUGO},
 614                [2] = {"register", &bm_register_operations, S_IWUSR},
 615                /* last one */ {""}
 616        };
 617        int err = simple_fill_super(sb, 0x42494e4d, bm_files);
 618        if (!err)
 619                sb->s_op = &s_ops;
 620        return err;
 621}
 622
 623static struct super_block *bm_get_sb(struct file_system_type *fs_type,
 624        int flags, const char *dev_name, void *data)
 625{
 626        return get_sb_single(fs_type, flags, data, bm_fill_super);
 627}
 628
 629static struct linux_binfmt misc_format = {
 630        .module = THIS_MODULE,
 631        .load_binary = load_misc_binary,
 632};
 633
 634static struct file_system_type bm_fs_type = {
 635        .owner          = THIS_MODULE,
 636        .name           = "binfmt_misc",
 637        .get_sb         = bm_get_sb,
 638        .kill_sb        = kill_litter_super,
 639};
 640
 641static int __init init_misc_binfmt(void)
 642{
 643        int err = register_filesystem(&bm_fs_type);
 644        if (!err) {
 645                err = register_binfmt(&misc_format);
 646                if (err)
 647                        unregister_filesystem(&bm_fs_type);
 648        }
 649        return err;
 650}
 651
 652static void __exit exit_misc_binfmt(void)
 653{
 654        unregister_binfmt(&misc_format);
 655        unregister_filesystem(&bm_fs_type);
 656}
 657
 658module_init(init_misc_binfmt);
 659module_exit(exit_misc_binfmt);
 660MODULE_LICENSE("GPL");
 661
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.