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フラグは、引数をインタープリタの引数とするか、起動スクリプトの引数とするかと言う事です。