Blame view

fs/binfmt_misc.c 17.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
2
   * binfmt_misc.c
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
   *
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
4
   * Copyright (C) 1997 Richard Günther
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
   *
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
6
7
   * binfmt_misc detects binaries via a magic or filename extension and invokes
   * a specified wrapper. See Documentation/binfmt_misc.txt for more details.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
   */
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
9
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
  #include <linux/module.h>
  #include <linux/init.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
12
  #include <linux/sched.h>
b502bd115   Muthu Kumar   magic.h: move som...
13
  #include <linux/magic.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
  #include <linux/binfmts.h>
  #include <linux/slab.h>
  #include <linux/ctype.h>
8d82e180b   Andy Shevchenko   binfmt_misc: reus...
17
  #include <linux/string_helpers.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
  #include <linux/file.h>
  #include <linux/pagemap.h>
  #include <linux/namei.h>
  #include <linux/mount.h>
  #include <linux/syscalls.h>
6e2c10a12   Akinobu Mita   binfmt_misc: use ...
23
  #include <linux/fs.h>
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
24
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25

6b899c4e9   Mike Frysinger   binfmt_misc: add ...
26
27
28
29
30
  #ifdef DEBUG
  # define USE_DEBUG 1
  #else
  # define USE_DEBUG 0
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
35
36
37
38
39
  
  enum {
  	VERBOSE_STATUS = 1 /* make it zero to save 400 bytes kernel memory */
  };
  
  static LIST_HEAD(entries);
  static int enabled = 1;
  
  enum {Enabled, Magic};
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
40
41
42
  #define MISC_FMT_PRESERVE_ARGV0 (1 << 31)
  #define MISC_FMT_OPEN_BINARY (1 << 30)
  #define MISC_FMT_CREDENTIALS (1 << 29)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
46
47
48
49
50
51
52
53
54
55
56
  
  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 DEFINE_RWLOCK(entries_lock);
1f5ce9e93   Trond Myklebust   VFS: Unexport do_...
57
  static struct file_system_type bm_fs_type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
  static struct vfsmount *bm_mnt;
  static int entry_count;
bbaecc088   Mike Frysinger   binfmt_misc: expa...
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  /*
   * Max length of the register string.  Determined by:
   *  - 7 delimiters
   *  - name:   ~50 bytes
   *  - type:   1 byte
   *  - offset: 3 bytes (has to be smaller than BINPRM_BUF_SIZE)
   *  - magic:  128 bytes (512 in escaped form)
   *  - mask:   128 bytes (512 in escaped form)
   *  - interp: ~50 bytes
   *  - flags:  5 bytes
   * Round that up a bit, and then back off to hold the internal data
   * (like struct Node).
   */
  #define MAX_REGISTER_LENGTH 1920
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
78
79
80
81
82
83
   * Check if we support the binfmt
   * if we do, return the node, else NULL
   * locking is done in load_misc_binary
   */
  static Node *check_file(struct linux_binprm *bprm)
  {
  	char *p = strrchr(bprm->interp, '.');
  	struct list_head *l;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
84
  	/* Walk all the registered handlers. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
  	list_for_each(l, &entries) {
  		Node *e = list_entry(l, Node, list);
  		char *s;
  		int j;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
89
  		/* Make sure this one is currently enabled. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
  		if (!test_bit(Enabled, &e->flags))
  			continue;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
92
  		/* Do matching based on extension if applicable. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
97
  		if (!test_bit(Magic, &e->flags)) {
  			if (p && !strcmp(e->magic, p + 1))
  				return e;
  			continue;
  		}
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
98
  		/* Do matching based on magic & mask. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
  		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;
  }
  
  /*
   * the loader itself
   */
71613c3b8   Al Viro   get rid of pt_reg...
118
  static int load_misc_binary(struct linux_binprm *bprm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
  {
  	Node *fmt;
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
121
  	struct file *interp_file = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
  	char iname[BINPRM_BUF_SIZE];
d7627467b   David Howells   Make do_execve() ...
123
  	const char *iname_addr = iname;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
  	int retval;
  	int fd_binary = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
  
  	retval = -ENOEXEC;
  	if (!enabled)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
129
  		goto ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
133
134
135
136
137
  
  	/* to keep locking time low, we copy the interpreter string */
  	read_lock(&entries_lock);
  	fmt = check_file(bprm);
  	if (fmt)
  		strlcpy(iname, fmt->interpreter, BINPRM_BUF_SIZE);
  	read_unlock(&entries_lock);
  	if (!fmt)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
138
  		goto ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139

51f39a1f0   David Drysdale   syscalls: impleme...
140
141
142
  	/* Need to be able to load the file after exec */
  	if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
  		return -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
  	if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) {
b6a2fea39   Ollie Wild   mm: variable leng...
144
145
  		retval = remove_arg_zero(bprm);
  		if (retval)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
146
  			goto ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
  	}
  
  	if (fmt->flags & MISC_FMT_OPEN_BINARY) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
  		/* if the binary should be opened on behalf of the
  		 * interpreter than keep it open and assign descriptor
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
152
153
  		 * to it
  		 */
c6cb898b5   Yann Droneaud   binfmt_misc: repl...
154
  		fd_binary = get_unused_fd_flags(0);
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
155
156
157
158
159
  		if (fd_binary < 0) {
  			retval = fd_binary;
  			goto ret;
  		}
  		fd_install(fd_binary, bprm->file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
162
  
  		/* if the binary is not readable than enforce mm->dumpable=0
  		   regardless of the interpreter's permissions */
1b5d783c9   Al Viro   consolidate BINPR...
163
  		would_dump(bprm, bprm->file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
169
170
  
  		allow_write_access(bprm->file);
  		bprm->file = NULL;
  
  		/* mark the bprm that fd should be passed to interp */
  		bprm->interp_flags |= BINPRM_FLAGS_EXECFD;
  		bprm->interp_data = fd_binary;
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
171
172
173
174
175
  	} else {
  		allow_write_access(bprm->file);
  		fput(bprm->file);
  		bprm->file = NULL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  	/* make argv[1] be the path to the binary */
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
177
  	retval = copy_strings_kernel(1, &bprm->interp, bprm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
  	if (retval < 0)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
179
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
  	bprm->argc++;
  
  	/* add the interp as argv[0] */
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
183
  	retval = copy_strings_kernel(1, &iname_addr, bprm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  	if (retval < 0)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
185
186
  		goto error;
  	bprm->argc++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187

b66c59840   Kees Cook   exec: do not leav...
188
189
190
  	/* Update interp in case binfmt_script needs it. */
  	retval = bprm_change_interp(iname, bprm);
  	if (retval < 0)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
191
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192

e6084d4a0   Mike Frysinger   binfmt_misc: clea...
193
194
195
196
  	interp_file = open_exec(iname);
  	retval = PTR_ERR(interp_file);
  	if (IS_ERR(interp_file))
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
201
202
203
204
205
206
  
  	bprm->file = interp_file;
  	if (fmt->flags & MISC_FMT_CREDENTIALS) {
  		/*
  		 * No need to call prepare_binprm(), it's already been
  		 * done.  bprm->buf is stale, update from interp_file.
  		 */
  		memset(bprm->buf, 0, BINPRM_BUF_SIZE);
  		retval = kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
  	} else
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
207
  		retval = prepare_binprm(bprm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
  
  	if (retval < 0)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
210
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211

3c456bfc4   Al Viro   get rid of pt_reg...
212
  	retval = search_binary_handler(bprm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
  	if (retval < 0)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
214
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215

e6084d4a0   Mike Frysinger   binfmt_misc: clea...
216
  ret:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  	return retval;
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
218
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
221
222
  	if (fd_binary > 0)
  		sys_close(fd_binary);
  	bprm->interp_flags = 0;
  	bprm->interp_data = 0;
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
223
  	goto ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  }
  
  /* Command parsers */
  
  /*
   * parses and copies one argument enclosed in del from *sp to *dp,
   * recognising the \x special.
   * returns pointer to the copied argument or NULL in case of an
   * error (and sets err) or null argument length.
   */
  static char *scanarg(char *s, char del)
  {
  	char c;
  
  	while ((c = *s++) != del) {
  		if (c == '\\' && *s == 'x') {
  			s++;
  			if (!isxdigit(*s++))
  				return NULL;
  			if (!isxdigit(*s++))
  				return NULL;
  		}
  	}
7d65cf10e   Al Viro   unfuck binfmt_mis...
247
  	s[-1] ='\0';
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
  	return s;
  }
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
250
  static char *check_special_flags(char *sfs, Node *e)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
  {
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
252
  	char *p = sfs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
255
256
257
  	int cont = 1;
  
  	/* special flags */
  	while (cont) {
  		switch (*p) {
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
  		case 'P':
  			pr_debug("register: flag: P (preserve argv0)
  ");
  			p++;
  			e->flags |= MISC_FMT_PRESERVE_ARGV0;
  			break;
  		case 'O':
  			pr_debug("register: flag: O (open binary)
  ");
  			p++;
  			e->flags |= MISC_FMT_OPEN_BINARY;
  			break;
  		case 'C':
  			pr_debug("register: flag: C (preserve creds)
  ");
  			p++;
  			/* this flags also implies the
  			   open-binary flag */
  			e->flags |= (MISC_FMT_CREDENTIALS |
  					MISC_FMT_OPEN_BINARY);
  			break;
  		default:
  			cont = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
285
  		}
  	}
  
  	return p;
  }
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
286

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
291
292
293
294
295
296
297
  /*
   * This registers a new binary format, it recognises the syntax
   * ':name:type:offset:magic:mask:interpreter:flags'
   * where the ':' is the IFS, that can be chosen with the first char
   */
  static Node *create_entry(const char __user *buffer, size_t count)
  {
  	Node *e;
  	int memsize, err;
  	char *buf, *p;
  	char del;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
298
299
  	pr_debug("register: received %zu bytes
  ", count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
  	/* some sanity checks */
  	err = -EINVAL;
bbaecc088   Mike Frysinger   binfmt_misc: expa...
302
  	if ((count < 11) || (count > MAX_REGISTER_LENGTH))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
  		goto out;
  
  	err = -ENOMEM;
  	memsize = sizeof(Node) + count + 8;
f7e1ad1a1   Andrew Morton   fs/binfmt_misc.c:...
307
  	e = kmalloc(memsize, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
311
312
313
314
  	if (!e)
  		goto out;
  
  	p = buf = (char *)e + sizeof(Node);
  
  	memset(e, 0, sizeof(Node));
  	if (copy_from_user(buf, buffer, count))
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
315
  		goto efault;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
  
  	del = *p++;	/* delimeter */
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
318
319
320
321
  	pr_debug("register: delim: %#x {%c}
  ", del, del);
  
  	/* Pad the buffer with the delim to simplify parsing below. */
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
322
  	memset(buf + count, del, 8);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323

6b899c4e9   Mike Frysinger   binfmt_misc: add ...
324
  	/* Parse the 'name' field. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
327
  	e->name = p;
  	p = strchr(p, del);
  	if (!p)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
328
  		goto einval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
331
332
333
  	*p++ = '\0';
  	if (!e->name[0] ||
  	    !strcmp(e->name, ".") ||
  	    !strcmp(e->name, "..") ||
  	    strchr(e->name, '/'))
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
334
  		goto einval;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
335
336
337
338
339
  
  	pr_debug("register: name: {%s}
  ", e->name);
  
  	/* Parse the 'type' field. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  	switch (*p++) {
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
341
342
343
344
345
346
347
348
349
350
351
  	case 'E':
  		pr_debug("register: type: E (extension)
  ");
  		e->flags = 1 << Enabled;
  		break;
  	case 'M':
  		pr_debug("register: type: M (magic)
  ");
  		e->flags = (1 << Enabled) | (1 << Magic);
  		break;
  	default:
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
352
  		goto einval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
  	}
  	if (*p++ != del)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
355
  		goto einval;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
356

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  	if (test_bit(Magic, &e->flags)) {
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
358
359
360
361
362
  		/* Handle the 'M' (magic) format. */
  		char *s;
  
  		/* Parse the 'offset' field. */
  		s = strchr(p, del);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
  		if (!s)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
364
  			goto einval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
  		*s++ = '\0';
  		e->offset = simple_strtoul(p, &p, 10);
  		if (*p++)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
368
  			goto einval;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
369
370
371
372
  		pr_debug("register: offset: %#x
  ", e->offset);
  
  		/* Parse the 'magic' field. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
374
375
  		e->magic = p;
  		p = scanarg(p, del);
  		if (!p)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
376
  			goto einval;
7d65cf10e   Al Viro   unfuck binfmt_mis...
377
  		if (!e->magic[0])
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
378
  			goto einval;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
379
380
381
382
383
384
  		if (USE_DEBUG)
  			print_hex_dump_bytes(
  				KBUILD_MODNAME ": register: magic[raw]: ",
  				DUMP_PREFIX_NONE, e->magic, p - e->magic);
  
  		/* Parse the 'mask' field. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
387
  		e->mask = p;
  		p = scanarg(p, del);
  		if (!p)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
388
  			goto einval;
7d65cf10e   Al Viro   unfuck binfmt_mis...
389
  		if (!e->mask[0]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
  			e->mask = NULL;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
391
392
393
394
395
396
397
398
399
400
401
402
403
  			pr_debug("register:  mask[raw]: none
  ");
  		} else if (USE_DEBUG)
  			print_hex_dump_bytes(
  				KBUILD_MODNAME ": register:  mask[raw]: ",
  				DUMP_PREFIX_NONE, e->mask, p - e->mask);
  
  		/*
  		 * Decode the magic & mask fields.
  		 * Note: while we might have accepted embedded NUL bytes from
  		 * above, the unescape helpers here will stop at the first one
  		 * it encounters.
  		 */
8d82e180b   Andy Shevchenko   binfmt_misc: reus...
404
405
406
  		e->size = string_unescape_inplace(e->magic, UNESCAPE_HEX);
  		if (e->mask &&
  		    string_unescape_inplace(e->mask, UNESCAPE_HEX) != e->size)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
407
  			goto einval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
  		if (e->size + e->offset > BINPRM_BUF_SIZE)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
409
  			goto einval;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
410
411
412
413
414
415
416
417
418
  		pr_debug("register: magic/mask length: %i
  ", e->size);
  		if (USE_DEBUG) {
  			print_hex_dump_bytes(
  				KBUILD_MODNAME ": register: magic[decoded]: ",
  				DUMP_PREFIX_NONE, e->magic, e->size);
  
  			if (e->mask) {
  				int i;
f7e1ad1a1   Andrew Morton   fs/binfmt_misc.c:...
419
  				char *masked = kmalloc(e->size, GFP_KERNEL);
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
  
  				print_hex_dump_bytes(
  					KBUILD_MODNAME ": register:  mask[decoded]: ",
  					DUMP_PREFIX_NONE, e->mask, e->size);
  
  				if (masked) {
  					for (i = 0; i < e->size; ++i)
  						masked[i] = e->magic[i] & e->mask[i];
  					print_hex_dump_bytes(
  						KBUILD_MODNAME ": register:  magic[masked]: ",
  						DUMP_PREFIX_NONE, masked, e->size);
  
  					kfree(masked);
  				}
  			}
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
  	} else {
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
437
438
439
  		/* Handle the 'E' (extension) format. */
  
  		/* Skip the 'offset' field. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
  		p = strchr(p, del);
  		if (!p)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
442
  			goto einval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
  		*p++ = '\0';
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
444
445
  
  		/* Parse the 'magic' field. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
448
  		e->magic = p;
  		p = strchr(p, del);
  		if (!p)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
449
  			goto einval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
451
  		*p++ = '\0';
  		if (!e->magic[0] || strchr(e->magic, '/'))
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
452
  			goto einval;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
453
454
455
456
  		pr_debug("register: extension: {%s}
  ", e->magic);
  
  		/* Skip the 'mask' field. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
  		p = strchr(p, del);
  		if (!p)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
459
  			goto einval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
  		*p++ = '\0';
  	}
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
462
463
  
  	/* Parse the 'interpreter' field. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
466
  	e->interpreter = p;
  	p = strchr(p, del);
  	if (!p)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
467
  		goto einval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
  	*p++ = '\0';
  	if (!e->interpreter[0])
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
470
  		goto einval;
6b899c4e9   Mike Frysinger   binfmt_misc: add ...
471
472
  	pr_debug("register: interpreter: {%s}
  ", e->interpreter);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473

6b899c4e9   Mike Frysinger   binfmt_misc: add ...
474
  	/* Parse the 'flags' field. */
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
475
  	p = check_special_flags(p, e);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
479
  	if (*p == '
  ')
  		p++;
  	if (p != buf + count)
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
480
  		goto einval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
483
484
  	return e;
  
  out:
  	return ERR_PTR(err);
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
485
  efault:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
487
  	kfree(e);
  	return ERR_PTR(-EFAULT);
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
488
  einval:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
493
494
495
496
497
498
499
  	kfree(e);
  	return ERR_PTR(-EINVAL);
  }
  
  /*
   * Set status of entry/binfmt_misc:
   * '1' enables, '0' disables and '-1' clears entry/binfmt_misc
   */
  static int parse_command(const char __user *buffer, size_t count)
  {
  	char s[4];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
503
  	if (count > 3)
  		return -EINVAL;
  	if (copy_from_user(s, buffer, count))
  		return -EFAULT;
de8288b1f   Arnd Bergmann   binfmt_misc: work...
504
505
  	if (!count)
  		return 0;
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
506
507
  	if (s[count - 1] == '
  ')
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
  		count--;
  	if (count == 1 && s[0] == '0')
  		return 1;
  	if (count == 1 && s[0] == '1')
  		return 2;
  	if (count == 2 && s[0] == '-' && s[1] == '1')
  		return 3;
  	return -EINVAL;
  }
  
  /* generic stuff */
  
  static void entry_status(Node *e, char *page)
  {
  	char *dp;
  	char *status = "disabled";
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
524
  	const char *flags = "flags: ";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
  
  	if (test_bit(Enabled, &e->flags))
  		status = "enabled";
  
  	if (!VERBOSE_STATUS) {
  		sprintf(page, "%s
  ", status);
  		return;
  	}
  
  	sprintf(page, "%s
  interpreter %s
  ", status, e->interpreter);
  	dp = page + strlen(page);
  
  	/* print the special flags */
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
541
542
543
544
545
546
547
548
549
550
  	sprintf(dp, "%s", flags);
  	dp += strlen(flags);
  	if (e->flags & MISC_FMT_PRESERVE_ARGV0)
  		*dp++ = 'P';
  	if (e->flags & MISC_FMT_OPEN_BINARY)
  		*dp++ = 'O';
  	if (e->flags & MISC_FMT_CREDENTIALS)
  		*dp++ = 'C';
  	*dp++ = '
  ';
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
  
  	if (!test_bit(Magic, &e->flags)) {
  		sprintf(dp, "extension .%s
  ", e->magic);
  	} else {
  		int i;
  
  		sprintf(dp, "offset %i
  magic ", e->offset);
  		dp = page + strlen(page);
  		for (i = 0; i < e->size; i++) {
  			sprintf(dp, "%02x", 0xff & (int) (e->magic[i]));
  			dp += 2;
  		}
  		if (e->mask) {
  			sprintf(dp, "
  mask ");
  			dp += 6;
  			for (i = 0; i < e->size; i++) {
  				sprintf(dp, "%02x", 0xff & (int) (e->mask[i]));
  				dp += 2;
  			}
  		}
  		*dp++ = '
  ';
  		*dp = '\0';
  	}
  }
  
  static struct inode *bm_get_inode(struct super_block *sb, int mode)
  {
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
582
  	struct inode *inode = new_inode(sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
584
  
  	if (inode) {
85fe4025c   Christoph Hellwig   fs: do not assign...
585
  		inode->i_ino = get_next_ino();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
  		inode->i_mode = mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
588
589
590
591
  		inode->i_atime = inode->i_mtime = inode->i_ctime =
  			current_fs_time(inode->i_sb);
  	}
  	return inode;
  }
b57922d97   Al Viro   convert remaining...
592
  static void bm_evict_inode(struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
  {
dbd5768f8   Jan Kara   vfs: Rename end_w...
594
  	clear_inode(inode);
8e18e2941   Theodore Ts'o   [PATCH] inode_die...
595
  	kfree(inode->i_private);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
  }
  
  static void kill_node(Node *e)
  {
  	struct dentry *dentry;
  
  	write_lock(&entries_lock);
  	dentry = e->dentry;
  	if (dentry) {
  		list_del_init(&e->list);
  		e->dentry = NULL;
  	}
  	write_unlock(&entries_lock);
  
  	if (dentry) {
6d6b77f16   Miklos Szeredi   filesystems: add ...
611
  		drop_nlink(dentry->d_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
614
615
616
617
618
619
620
  		d_drop(dentry);
  		dput(dentry);
  		simple_release_fs(&bm_mnt, &entry_count);
  	}
  }
  
  /* /<entry> */
  
  static ssize_t
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
621
  bm_entry_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
  {
496ad9aa8   Al Viro   new helper: file_...
623
  	Node *e = file_inode(file)->i_private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
  	ssize_t res;
  	char *page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626

e6084d4a0   Mike Frysinger   binfmt_misc: clea...
627
628
  	page = (char *) __get_free_page(GFP_KERNEL);
  	if (!page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
630
631
  		return -ENOMEM;
  
  	entry_status(e, page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632

6e2c10a12   Akinobu Mita   binfmt_misc: use ...
633
  	res = simple_read_from_buffer(buf, nbytes, ppos, page, strlen(page));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
635
636
637
638
639
640
641
  	free_page((unsigned long) page);
  	return res;
  }
  
  static ssize_t bm_entry_write(struct file *file, const char __user *buffer,
  				size_t count, loff_t *ppos)
  {
  	struct dentry *root;
496ad9aa8   Al Viro   new helper: file_...
642
  	Node *e = file_inode(file)->i_private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
644
645
  	int res = parse_command(buffer, count);
  
  	switch (res) {
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
646
647
648
649
650
651
652
653
654
655
656
657
  	case 1:
  		/* Disable this handler. */
  		clear_bit(Enabled, &e->flags);
  		break;
  	case 2:
  		/* Enable this handler. */
  		set_bit(Enabled, &e->flags);
  		break;
  	case 3:
  		/* Delete this handler. */
  		root = dget(file->f_path.dentry->d_sb->s_root);
  		mutex_lock(&root->d_inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658

e6084d4a0   Mike Frysinger   binfmt_misc: clea...
659
  		kill_node(e);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660

e6084d4a0   Mike Frysinger   binfmt_misc: clea...
661
662
663
664
665
  		mutex_unlock(&root->d_inode->i_mutex);
  		dput(root);
  		break;
  	default:
  		return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
  	}
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
667

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
669
  	return count;
  }
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
670
  static const struct file_operations bm_entry_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671
672
  	.read		= bm_entry_read,
  	.write		= bm_entry_write,
6038f373a   Arnd Bergmann   llseek: automatic...
673
  	.llseek		= default_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
676
677
678
679
680
681
682
683
  };
  
  /* /register */
  
  static ssize_t bm_register_write(struct file *file, const char __user *buffer,
  			       size_t count, loff_t *ppos)
  {
  	Node *e;
  	struct inode *inode;
  	struct dentry *root, *dentry;
d8c9584ea   Al Viro   vfs: prefer ->den...
684
  	struct super_block *sb = file->f_path.dentry->d_sb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
686
687
688
689
690
691
692
  	int err = 0;
  
  	e = create_entry(buffer, count);
  
  	if (IS_ERR(e))
  		return PTR_ERR(e);
  
  	root = dget(sb->s_root);
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
693
  	mutex_lock(&root->d_inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
695
696
697
698
699
700
701
702
703
704
705
706
707
  	dentry = lookup_one_len(e->name, root, strlen(e->name));
  	err = PTR_ERR(dentry);
  	if (IS_ERR(dentry))
  		goto out;
  
  	err = -EEXIST;
  	if (dentry->d_inode)
  		goto out2;
  
  	inode = bm_get_inode(sb, S_IFREG | 0644);
  
  	err = -ENOMEM;
  	if (!inode)
  		goto out2;
1f5ce9e93   Trond Myklebust   VFS: Unexport do_...
708
  	err = simple_pin_fs(&bm_fs_type, &bm_mnt, &entry_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
710
711
712
713
714
715
  	if (err) {
  		iput(inode);
  		inode = NULL;
  		goto out2;
  	}
  
  	e->dentry = dget(dentry);
8e18e2941   Theodore Ts'o   [PATCH] inode_die...
716
  	inode->i_private = e;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
719
720
721
722
723
724
725
726
727
  	inode->i_fop = &bm_entry_operations;
  
  	d_instantiate(dentry, inode);
  	write_lock(&entries_lock);
  	list_add(&e->list, &entries);
  	write_unlock(&entries_lock);
  
  	err = 0;
  out2:
  	dput(dentry);
  out:
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
728
  	mutex_unlock(&root->d_inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
729
730
731
732
733
734
735
736
  	dput(root);
  
  	if (err) {
  		kfree(e);
  		return -EINVAL;
  	}
  	return count;
  }
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
737
  static const struct file_operations bm_register_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738
  	.write		= bm_register_write,
6038f373a   Arnd Bergmann   llseek: automatic...
739
  	.llseek		= noop_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
741
742
743
744
745
746
  };
  
  /* /status */
  
  static ssize_t
  bm_status_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
  {
87113e806   Qinghuang Feng   fs/binfmt_misc.c:...
747
748
749
  	char *s = enabled ? "enabled
  " : "disabled
  ";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750

92f4c701a   Akinobu Mita   use simple_read_f...
751
  	return simple_read_from_buffer(buf, nbytes, ppos, s, strlen(s));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
  }
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
753
  static ssize_t bm_status_write(struct file *file, const char __user *buffer,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
755
756
757
758
759
  		size_t count, loff_t *ppos)
  {
  	int res = parse_command(buffer, count);
  	struct dentry *root;
  
  	switch (res) {
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
760
761
762
763
764
765
766
767
768
769
770
771
  	case 1:
  		/* Disable all handlers. */
  		enabled = 0;
  		break;
  	case 2:
  		/* Enable all handlers. */
  		enabled = 1;
  		break;
  	case 3:
  		/* Delete all handlers. */
  		root = dget(file->f_path.dentry->d_sb->s_root);
  		mutex_lock(&root->d_inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772

e6084d4a0   Mike Frysinger   binfmt_misc: clea...
773
774
  		while (!list_empty(&entries))
  			kill_node(list_entry(entries.next, Node, list));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
775

e6084d4a0   Mike Frysinger   binfmt_misc: clea...
776
777
778
779
780
  		mutex_unlock(&root->d_inode->i_mutex);
  		dput(root);
  		break;
  	default:
  		return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
  	}
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
782

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783
784
  	return count;
  }
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
785
  static const struct file_operations bm_status_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
787
  	.read		= bm_status_read,
  	.write		= bm_status_write,
6038f373a   Arnd Bergmann   llseek: automatic...
788
  	.llseek		= default_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
789
790
791
  };
  
  /* Superblock handling */
ee9b6d61a   Josef 'Jeff' Sipek   [PATCH] Mark stru...
792
  static const struct super_operations s_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
  	.statfs		= simple_statfs,
b57922d97   Al Viro   convert remaining...
794
  	.evict_inode	= bm_evict_inode,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
  };
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
796
  static int bm_fill_super(struct super_block *sb, void *data, int silent)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
797
  {
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
798
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
  	static struct tree_descr bm_files[] = {
1a1c9bb43   Jeff Layton   inode numbering: ...
800
801
  		[2] = {"status", &bm_status_operations, S_IWUSR|S_IRUGO},
  		[3] = {"register", &bm_register_operations, S_IWUSR},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
803
  		/* last one */ {""}
  	};
e6084d4a0   Mike Frysinger   binfmt_misc: clea...
804
805
  
  	err = simple_fill_super(sb, BINFMTFS_MAGIC, bm_files);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
806
807
808
809
  	if (!err)
  		sb->s_op = &s_ops;
  	return err;
  }
fc14f2fef   Al Viro   convert get_sb_si...
810
811
  static struct dentry *bm_mount(struct file_system_type *fs_type,
  	int flags, const char *dev_name, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
  {
fc14f2fef   Al Viro   convert get_sb_si...
813
  	return mount_single(fs_type, flags, data, bm_fill_super);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
815
816
817
818
819
820
821
822
823
  }
  
  static struct linux_binfmt misc_format = {
  	.module = THIS_MODULE,
  	.load_binary = load_misc_binary,
  };
  
  static struct file_system_type bm_fs_type = {
  	.owner		= THIS_MODULE,
  	.name		= "binfmt_misc",
fc14f2fef   Al Viro   convert get_sb_si...
824
  	.mount		= bm_mount,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825
826
  	.kill_sb	= kill_litter_super,
  };
7f78e0351   Eric W. Biederman   fs: Limit sys_mou...
827
  MODULE_ALIAS_FS("binfmt_misc");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
829
830
831
  
  static int __init init_misc_binfmt(void)
  {
  	int err = register_filesystem(&bm_fs_type);
8fc3dc5a3   Al Viro   __register_binfmt...
832
833
  	if (!err)
  		insert_binfmt(&misc_format);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
835
836
837
838
839
840
841
842
843
844
845
  	return err;
  }
  
  static void __exit exit_misc_binfmt(void)
  {
  	unregister_binfmt(&misc_format);
  	unregister_filesystem(&bm_fs_type);
  }
  
  core_initcall(init_misc_binfmt);
  module_exit(exit_misc_binfmt);
  MODULE_LICENSE("GPL");