binfmt_misc


miscは拡張子/マジックコードによって、設定したローダで起動させる機能です。
[root@localhost test]#  echo ":sh:E::sh::/bin/sh:" > /proc/sys/fs/binfmt_misc/register

[root@localhost test]# cat misc.sh 
echo "exec without #!"

[root@localhost test]# chmod u+x misc.sh

[root@localhost test]# ./misc.sh
exec without #!
/proc/sys/fs/binfmt_misc/registerに :名前:型:オフセット:マジック:マスク:インタープリタ:フラグ(:は任意でデリミタとなる。)を書き込むと、ファイルオペレーションの bm_register_operationsのコールバックbm_register_write()から、create_entry()がコールされます。

create_entry()は、/proc/sys/fs/binfmt_misc/registerに書き込まれたデータを、Nodeに設定することにあります。デリミタの先頭バイトでNodeを埋めた後、入力データをNodeに設定するだけですが、ユーザ空間からカーネル空間にデータを複写するためのバッファーとして、kmallocとしてmemsize = sizeof(Node) + count + 8で、Nodeの分と一緒に確保します。p = buf = (char *)e + sizeof(Node)で、このメモリー空間のオフセットsizeof(Node)として、入力データを設定する所です。たかが数十バイトのメモリーを2回もアロケートするのは無駄だよ。という事なのでしょうか?でも、技巧的やな〜。って思しまいます。

マニュアル的には、
・入力データは、12から255バイト。
・名前は.および..ではダメで、/を含んでもいけない。
・型がEの時、マジックは/を含んではいけない。
と言うところです。

unquote(e->magic)と変な処理がありますが、これは\xの16進数表記の処理で、そのマジック文字とマスク文字サイズのチェックです。なお、型およびフラグの情報は、e->flagsに設定されます。
(以前はフラグとして、OとCがありましたが、今はPしかサポートされていないようです。)
static struct file_operations bm_register_operations = {
       .write          = bm_register_write,
};

typedef struct {
       struct list_head list;
       unsigned long flags;            /* type, status, etc. */
       int offset;                     /* offset of magic */
       int size;                       /* size of magic/mask */
       char *magic;                    /* magic or filename extension */
       char *mask;                     /* mask, NULL for exact match */
       char *interpreter;              /* filename of interpreter */
       char *name;
       struct dentry *dentry;
} Node;

static Node *create_entry(const char *buffer, size_t count)
{
       Node *e;
       int memsize, err;
       char *buf, *p;
       char del;

       err = -EINVAL;
       if ((count < 11) || (count > 256))
               goto out;

       err = -ENOMEM;
       memsize = sizeof(Node) + count + 8;
       e = (Node *) kmalloc(memsize, GFP_USER);
       if (!e)
               goto out;

       p = buf = (char *)e + sizeof(Node);

       memset(e, 0, sizeof(Node));
       if (copy_from_user(buf, buffer, count))
               goto Efault;

       del = *p++;     /* delimeter */

       memset(buf+count, del, 8);

       e->name = p;
       p = strchr(p, del);
       if (!p)
               goto Einval;
       *p++ = '\0';
       if (!e->name[0] ||
           !strcmp(e->name, ".") ||
           !strcmp(e->name, "..") ||
           strchr(e->name, '/'))
               goto Einval;
       switch (*p++) {
               case 'E': e->flags = 1<<Enabled; break;
               case 'M': e->flags = (1<<Enabled) | (1<<Magic); break;
               default: goto Einval;
       }
       if (*p++ != del)
               goto Einval;
       if (test_bit(Magic, &e->flags)) {
               char *s = strchr(p, del);
               if (!s)
                       goto Einval;
               *s++ = '\0';
               e->offset = simple_strtoul(p, &p, 10);
               if (*p++)
                       goto Einval;
               e->magic = p;
               p = scanarg(p, del);
               if (!p)
                       goto Einval;
               p[-1] = '\0';
               if (!e->magic[0])
                       goto Einval;
               e->mask = p;
               p = scanarg(p, del);
               if (!p)
                       goto Einval;
               p[-1] = '\0';
               if (!e->mask[0])
                       e->mask = NULL;
               e->size = unquote(e->magic);
               if (e->mask && unquote(e->mask) != e->size)
                       goto Einval;
               if (e->size + e->offset > BINPRM_BUF_SIZE)
                       goto Einval;
       } else {
               p = strchr(p, del);
               if (!p)
                       goto Einval;
               *p++ = '\0';
               e->magic = p;
               p = strchr(p, del);
               if (!p)
                       goto Einval;
               *p++ = '\0';
               if (!e->magic[0] || strchr(e->magic, '/'))
                       goto Einval;
               p = strchr(p, del);
               if (!p)
                       goto Einval;
               *p++ = '\0';
       }
       e->interpreter = p;
       p = strchr(p, del);
       if (!p)
               goto Einval;
       *p++ = '\0';
       if (!e->interpreter[0])
               goto Einval;

       if (*p == 'P') {
               p++;
               e->flags |= MISC_FMT_PRESERVE_ARGV0;
       }

       if (*p == '\n')
               p++;
       if (p != buf + count)
               goto Einval;
       return e;

out:
       return ERR_PTR(err);

Efault:
       kfree(e);
       return ERR_PTR(-EFAULT);
Einval:
       kfree(e);
       return ERR_PTR(-EINVAL);
}
miscのプロセス起動は、load_misc_binary()がコールされます。まずcheck_file()で、entriesをヘッドとしてリストされている該当するNodeを取得し、その情報に基づいて、改めてstruct linux_binprm *bprmを設定して、search_binary_handler()をコールするだけです。

fmt->flags & MISC_FMT_PRESERVE_ARGV0は、Pフラグのチェックです。設定されていると、最初の起動ファイルが第1引数となります。
static Node *check_file(struct linux_binprm *bprm)
{
       char *p = strrchr(bprm->interp, '.');
       struct list_head *l;

       list_for_each(l, &entries) {
               Node *e = list_entry(l, Node, list);
               char *s;
               int j;

               if (!test_bit(Enabled, &e->flags))
                       continue;

               if (!test_bit(Magic, &e->flags)) {
                       if (p && !strcmp(e->magic, p + 1))
                               return e;
                       continue;
               }

               s = bprm->buf + e->offset;
               if (e->mask) {
                       for (j = 0; j < e->size; j++)
                               if ((*s++ ^ e->magic[j]) & e->mask[j])
                                       break;
               } else {
                       for (j = 0; j < e->size; j++)
                               if ((*s++ ^ e->magic[j]))
                                       break;
               }
               if (j == e->size)
                       return e;
       }
       return NULL;
}

static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
{
       Node *fmt;
       struct file * file;
       char iname[BINPRM_BUF_SIZE];
       char *iname_addr = iname;
       int retval;

       retval = -ENOEXEC;
       if (!enabled)
               goto _ret;

       read_lock(&entries_lock);
       fmt = check_file(bprm);
       if (fmt)
               strlcpy(iname, fmt->interpreter, BINPRM_BUF_SIZE);
       read_unlock(&entries_lock);
       if (!fmt)
               goto _ret;

       allow_write_access(bprm->file);
       fput(bprm->file);
       bprm->file = NULL;

       if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) {
               remove_arg_zero(bprm);
       }
       retval = copy_strings_kernel(1, &bprm->interp, bprm);
       if (retval < 0) goto _ret; 
       bprm->argc++;
       retval = copy_strings_kernel(1, &iname_addr, bprm);
       if (retval < 0) goto _ret; 
       bprm->argc++;
       bprm->interp = iname;   /* for binfmt_script */

       file = open_exec(iname);
       retval = PTR_ERR(file);
       if (IS_ERR(file))
               goto _ret;
       bprm->file = file;

       retval = prepare_binprm(bprm);
       if (retval >= 0)
               retval = search_binary_handler(bprm, regs);
_ret:
       return retval;
}

フラグPについて

[root@localhost test]# cat misc.sh
echo $*

[root@localhost test]# ./misc.sh without P
without P

[root@localhost test]# ./misc.sh with P
./misc.sh with P
./misc.sh paramと起動すれば、paramが引数ですが、miscの内部処理で、/proc/sys/fs/binfmt_misc/registerのインタープリタの引数として、./misc.sh paramとなります。Pフラグは、引数をインタープリタの引数とするか、起動スクリプトの引数とするかと言う事です。

最終更新 2013/05/02 18:27:36 - north
(2013/05/02 18:03:35 作成)


検索

アクセス数
3576111
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。