binfmt_misc
miscは拡張子/マジックコードによって、設定したローダで起動させる機能です。
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しかサポートされていないようです。)
fmt->flags & MISC_FMT_PRESERVE_ARGV0は、Pフラグのチェックです。設定されていると、最初の起動ファイルが第1引数となります。
[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フラグは、引数をインタープリタの引数とするか、起動スクリプトの引数とするかと言う事です。





