imgact_gzip.c revision 139804
154558Sbp/*-
266479Sbp * ----------------------------------------------------------------------------
354558Sbp * "THE BEER-WARE LICENSE" (Revision 42):
454558Sbp * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
554558Sbp * can do whatever you want with this stuff. If we meet some day, and you think
654558Sbp * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
754558Sbp * ----------------------------------------------------------------------------
854558Sbp */
954558Sbp
1054558Sbp/*
1154558Sbp * This module handles execution of a.out files which have been run through
1254558Sbp * "gzip".  This saves diskspace, but wastes cpu-cycles and VM.
1354558Sbp *
1454558Sbp * TODO:
1554558Sbp *	text-segments should be made R/O after being filled
1654558Sbp *	is the vm-stuff safe ?
1754558Sbp * 	should handle the entire header of gzip'ed stuff.
1854558Sbp *	inflate isn't quite reentrant yet...
1954558Sbp *	error-handling is a mess...
2054558Sbp *	so is the rest...
2154558Sbp *	tidy up unnecesary includes
2254558Sbp */
2354558Sbp
2454558Sbp#include <sys/cdefs.h>
2554558Sbp__FBSDID("$FreeBSD: head/sys/kern/imgact_gzip.c 139804 2005-01-06 23:35:40Z imp $");
2654558Sbp
2754558Sbp#include <sys/param.h>
2854558Sbp#include <sys/exec.h>
2954558Sbp#include <sys/imgact.h>
3054558Sbp#include <sys/imgact_aout.h>
3156424Sbp#include <sys/kernel.h>
3254558Sbp#include <sys/lock.h>
3354558Sbp#include <sys/mman.h>
3454558Sbp#include <sys/mutex.h>
3554558Sbp#include <sys/proc.h>
3654558Sbp#include <sys/resourcevar.h>
3754558Sbp#include <sys/sysent.h>
3854558Sbp#include <sys/systm.h>
3954558Sbp#include <sys/vnode.h>
4054558Sbp#include <sys/inflate.h>
4154558Sbp
4254558Sbp#include <vm/vm.h>
4354558Sbp#include <vm/vm_param.h>
4454558Sbp#include <vm/pmap.h>
4554558Sbp#include <vm/vm_map.h>
4654558Sbp#include <vm/vm_kern.h>
4754558Sbp#include <vm/vm_extern.h>
4854558Sbp
4954558Sbpstruct imgact_gzip {
5054558Sbp	struct image_params *ip;
5154558Sbp	struct exec     a_out;
5254558Sbp	int             error;
5354558Sbp	int		gotheader;
5454558Sbp	int             where;
5554558Sbp	u_char         *inbuf;
5654558Sbp	u_long          offset;
5754558Sbp	u_long          output;
5854558Sbp	u_long          len;
5954558Sbp	int             idx;
6054558Sbp	u_long          virtual_offset, file_offset, file_end, bss_size;
6154558Sbp};
6254558Sbp
6354558Sbpstatic int exec_gzip_imgact(struct image_params *imgp);
6454558Sbpstatic int NextByte(void *vp);
6554558Sbpstatic int do_aout_hdr(struct imgact_gzip *);
6654558Sbpstatic int Flush(void *vp, u_char *, u_long siz);
6754558Sbp
6854558Sbpstatic int
6954558Sbpexec_gzip_imgact(imgp)
7054558Sbp	struct image_params *imgp;
7154558Sbp{
7287599Sobrien	int             error, error2 = 0;
7354558Sbp	const u_char   *p = (const u_char *) imgp->image_header;
7454558Sbp	struct imgact_gzip igz;
7554558Sbp	struct inflate  infl;
7654558Sbp	struct vmspace *vmspace;
7787599Sobrien
7854558Sbp	/* If these four are not OK, it isn't a gzip file */
7954558Sbp	if (p[0] != 0x1f)
8054558Sbp		return -1;	/* 0    Simply magic	 */
8154558Sbp	if (p[1] != 0x8b)
8254558Sbp		return -1;	/* 1    Simply magic	 */
8354558Sbp	if (p[2] != 0x08)
8454558Sbp		return -1;	/* 2    Compression method	 */
8560938Sjake	if (p[9] != 0x03)
8654558Sbp		return -1;	/* 9    OS compressed on	 */
8754558Sbp
8854558Sbp	/*
8954558Sbp	 * If this one contains anything but a comment or a filename marker,
9060938Sjake	 * we don't want to chew on it
9154558Sbp	 */
9254558Sbp	if (p[3] & ~(0x18))
9354558Sbp		return ENOEXEC;	/* 3    Flags		 */
9459681Sbp
9566479Sbp	/* These are of no use to us */
9654558Sbp	/* 4-7  Timestamp		 */
9754558Sbp	/* 8    Extra flags		 */
9854558Sbp
9954558Sbp	bzero(&igz, sizeof igz);
10054558Sbp	bzero(&infl, sizeof infl);
10154558Sbp	infl.gz_private = (void *) &igz;
10254558Sbp	infl.gz_input = NextByte;
10354558Sbp	infl.gz_output = Flush;
10454558Sbp
10554558Sbp	igz.ip = imgp;
10659681Sbp	igz.idx = 10;
10766479Sbp
10854558Sbp	if (p[3] & 0x08) {	/* skip a filename */
10954558Sbp		while (p[igz.idx++])
11054558Sbp			if (igz.idx >= PAGE_SIZE)
11154558Sbp				return ENOEXEC;
11254558Sbp	}
11354558Sbp	if (p[3] & 0x10) {	/* skip a comment */
11454558Sbp		while (p[igz.idx++])
11554558Sbp			if (igz.idx >= PAGE_SIZE)
11654558Sbp				return ENOEXEC;
11754558Sbp	}
11854558Sbp	igz.len = imgp->attr->va_size;
119106939Ssam
120106939Ssam	error = inflate(&infl);
12154558Sbp
12254558Sbp	/*
12354558Sbp	 * The unzipped file may not even have been long enough to contain
12454558Sbp	 * a header giving Flush() a chance to return error.  Check for this.
12554558Sbp	 */
12654558Sbp	if ( !igz.gotheader )
12754558Sbp		return ENOEXEC;
12854558Sbp
12954558Sbp	if ( !error ) {
13054558Sbp		vmspace = imgp->proc->p_vmspace;
131106939Ssam		error = vm_map_protect(&vmspace->vm_map,
132106939Ssam			(vm_offset_t) vmspace->vm_taddr,
133106939Ssam			(vm_offset_t) (vmspace->vm_taddr +
13454558Sbp				      (vmspace->vm_tsize << PAGE_SHIFT)) ,
13554558Sbp			VM_PROT_READ|VM_PROT_EXECUTE,0);
13654558Sbp	}
13754558Sbp
13854558Sbp	if (igz.inbuf) {
13954558Sbp		error2 =
14054558Sbp			vm_map_remove(kernel_map, (vm_offset_t) igz.inbuf,
14154558Sbp			    (vm_offset_t) igz.inbuf + PAGE_SIZE);
14254558Sbp	}
14354558Sbp	if (igz.error || error || error2) {
14454558Sbp		printf("Output=%lu ", igz.output);
14554558Sbp		printf("Inflate_error=%d igz.error=%d error2=%d where=%d\n",
14654558Sbp		       error, igz.error, error2, igz.where);
14754558Sbp	}
14854558Sbp	if (igz.error)
14954558Sbp		return igz.error;
15054558Sbp	if (error)
15154558Sbp		return ENOEXEC;
15254558Sbp	if (error2)
15354558Sbp		return error2;
15454558Sbp	return 0;
15554558Sbp}
15654558Sbp
15754558Sbpstatic int
15854558Sbpdo_aout_hdr(struct imgact_gzip * gz)
15954558Sbp{
16054558Sbp	int             error;
16172012Sphk	struct vmspace *vmspace;
16254558Sbp	vm_offset_t     vmaddr;
16354558Sbp
16454558Sbp	/*
16554558Sbp	 * Set file/virtual offset based on a.out variant. We do two cases:
166108172Shsu	 * host byte order and network byte order (for NetBSD compatibility)
16754558Sbp	 */
168108172Shsu	switch ((int) (gz->a_out.a_magic & 0xffff)) {
16954558Sbp	case ZMAGIC:
17054558Sbp		gz->virtual_offset = 0;
17154558Sbp		if (gz->a_out.a_text) {
17254558Sbp			gz->file_offset = PAGE_SIZE;
17354558Sbp		} else {
17454558Sbp			/* Bill's "screwball mode" */
17554558Sbp			gz->file_offset = 0;
17654558Sbp		}
17754558Sbp		break;
17854558Sbp	case QMAGIC:
17954558Sbp		gz->virtual_offset = PAGE_SIZE;
18054558Sbp		gz->file_offset = 0;
18154558Sbp		break;
18254558Sbp	default:
18354558Sbp		/* NetBSD compatibility */
18454558Sbp		switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) {
18554558Sbp		case ZMAGIC:
18654558Sbp		case QMAGIC:
18754558Sbp			gz->virtual_offset = PAGE_SIZE;
18854558Sbp			gz->file_offset = 0;
189106939Ssam			break;
190106939Ssam		default:
191106939Ssam			gz->where = __LINE__;
19254558Sbp			return (-1);
19354558Sbp		}
19454558Sbp	}
19554558Sbp
19654558Sbp	gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE);
19754558Sbp
19854558Sbp	/*
19954558Sbp	 * Check various fields in header for validity/bounds.
200106939Ssam	 */
20154558Sbp	if (			/* entry point must lay with text region */
20254558Sbp	    gz->a_out.a_entry < gz->virtual_offset ||
20354558Sbp	    gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text ||
20454558Sbp
20554558Sbp	/* text and data size must each be page rounded */
20654558Sbp	    gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) {
20754558Sbp		gz->where = __LINE__;
20854558Sbp		return (-1);
20954558Sbp	}
21054558Sbp	/*
21154558Sbp	 * text/data/bss must not exceed limits
21254558Sbp	 */
21354558Sbp	PROC_LOCK(gz->ip->proc);
21454558Sbp	if (			/* text can't exceed maximum text size */
21554558Sbp	    gz->a_out.a_text > maxtsiz ||
21654558Sbp
21754558Sbp	/* data + bss can't exceed rlimit */
21854558Sbp	    gz->a_out.a_data + gz->bss_size >
21954558Sbp	    lim_cur(gz->ip->proc, RLIMIT_DATA)) {
22054558Sbp		PROC_UNLOCK(gz->ip->proc);
22154558Sbp		gz->where = __LINE__;
22254558Sbp		return (ENOMEM);
22354558Sbp	}
22454558Sbp	PROC_UNLOCK(gz->ip->proc);
22554558Sbp	/* Find out how far we should go */
22654558Sbp	gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data;
227106939Ssam
22871891Sbp	/* copy in arguments and/or environment from old process */
22959681Sbp	error = exec_extract_strings(gz->ip);
23059681Sbp	if (error) {
23154558Sbp		gz->where = __LINE__;
23269152Sjlemon		return (error);
23354558Sbp	}
23454558Sbp	/*
23554558Sbp	 * Destroy old process VM and create a new one (with a new stack)
23654558Sbp	 */
23754558Sbp	exec_new_vmspace(gz->ip, &aout_sysvec);
23854558Sbp
23954558Sbp	vmspace = gz->ip->proc->p_vmspace;
24054558Sbp
24154558Sbp	vmaddr = gz->virtual_offset;
24254558Sbp
24397220Speter	error = vm_mmap(&vmspace->vm_map,
24454558Sbp			&vmaddr,
24554558Sbp			gz->a_out.a_text + gz->a_out.a_data,
24654558Sbp			VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED,
24754558Sbp			0,
24854558Sbp			0);
24954558Sbp
25054558Sbp	if (error) {
25154558Sbp		gz->where = __LINE__;
25254558Sbp		return (error);
25354558Sbp	}
25454558Sbp
25554558Sbp	if (gz->bss_size != 0) {
25654558Sbp		/*
25754558Sbp		 * Allocate demand-zeroed area for uninitialized data.
25854558Sbp		 * "bss" = 'block started by symbol' - named after the
25954558Sbp		 * IBM 7090 instruction of the same name.
26054558Sbp		 */
26154558Sbp		vmaddr = gz->virtual_offset + gz->a_out.a_text +
26254558Sbp			gz->a_out.a_data;
26354558Sbp		error = vm_map_find(&vmspace->vm_map,
26454558Sbp				NULL,
26554558Sbp				0,
26659681Sbp				&vmaddr,
26759681Sbp				gz->bss_size,
26854558Sbp				FALSE, VM_PROT_ALL, VM_PROT_ALL, 0);
26954558Sbp		if (error) {
27054558Sbp			gz->where = __LINE__;
27154558Sbp			return (error);
27254558Sbp		}
27354558Sbp	}
27454558Sbp	/* Fill in process VM information */
27554558Sbp	vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT;
27654558Sbp	vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT;
27754558Sbp	vmspace->vm_taddr = (caddr_t) (uintptr_t) gz->virtual_offset;
27854558Sbp	vmspace->vm_daddr = (caddr_t) (uintptr_t)
27954558Sbp			    (gz->virtual_offset + gz->a_out.a_text);
28054558Sbp
28154558Sbp	/* Fill in image_params */
28254558Sbp	gz->ip->interpreted = 0;
28354558Sbp	gz->ip->entry_addr = gz->a_out.a_entry;
28459681Sbp
28559681Sbp	gz->ip->proc->p_sysent = &aout_sysvec;
28654558Sbp
28754558Sbp	return 0;
28854558Sbp}
28954558Sbp
29054558Sbpstatic int
29154558SbpNextByte(void *vp)
29254558Sbp{
29354558Sbp	int             error;
29454558Sbp	struct imgact_gzip *igz = (struct imgact_gzip *) vp;
29554558Sbp
29654558Sbp	if (igz->idx >= igz->len) {
29754558Sbp		igz->where = __LINE__;
29854558Sbp		return GZ_EOF;
29954558Sbp	}
30054558Sbp	if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) {
30154558Sbp		return igz->inbuf[(igz->idx++) - igz->offset];
30259681Sbp	}
30359681Sbp	if (igz->inbuf) {
30454558Sbp		error = vm_map_remove(kernel_map, (vm_offset_t) igz->inbuf,
30554558Sbp			    (vm_offset_t) igz->inbuf + PAGE_SIZE);
30654558Sbp		if (error) {
30754558Sbp			igz->where = __LINE__;
30854558Sbp			igz->error = error;
30954558Sbp			return GZ_EOF;
31054558Sbp		}
31154558Sbp	}
31254558Sbp	igz->offset = igz->idx & ~PAGE_MASK;
31354558Sbp
31471891Sbp	error = vm_mmap(kernel_map,	/* map */
31554558Sbp			(vm_offset_t *) & igz->inbuf,	/* address */
31654558Sbp			PAGE_SIZE,	/* size */
31754558Sbp			VM_PROT_READ,	/* protection */
31854558Sbp			VM_PROT_READ,	/* max protection */
31954558Sbp			0,	/* flags */
32054558Sbp			(caddr_t) igz->ip->vp,	/* vnode */
32154558Sbp			igz->offset);	/* offset */
32254558Sbp	if (error) {
32354558Sbp		igz->where = __LINE__;
32454558Sbp		igz->error = error;
32554558Sbp		return GZ_EOF;
32654558Sbp	}
32754558Sbp	return igz->inbuf[(igz->idx++) - igz->offset];
32854558Sbp}
32954558Sbp
33054558Sbpstatic int
33154558SbpFlush(void *vp, u_char * ptr, u_long siz)
33254558Sbp{
33354558Sbp	struct imgact_gzip *gz = (struct imgact_gzip *) vp;
33454558Sbp	u_char         *p = ptr, *q;
33554558Sbp	int             i;
33654558Sbp
33754558Sbp	/* First, find an a.out-header. */
33854558Sbp	if (gz->output < sizeof gz->a_out) {
33954558Sbp		q = (u_char *) & gz->a_out;
34054558Sbp		i = min(siz, sizeof gz->a_out - gz->output);
34154558Sbp		bcopy(p, q + gz->output, i);
34254558Sbp		gz->output += i;
34354558Sbp		p += i;
34454558Sbp		siz -= i;
34554558Sbp		if (gz->output == sizeof gz->a_out) {
34654558Sbp			gz->gotheader = 1;
34759681Sbp			i = do_aout_hdr(gz);
34854558Sbp			if (i == -1) {
34954558Sbp				if (!gz->where)
35054558Sbp					gz->where = __LINE__;
35154558Sbp				gz->error = ENOEXEC;
35254558Sbp				return ENOEXEC;
35354558Sbp			} else if (i) {
35454558Sbp				gz->where = __LINE__;
35554558Sbp				gz->error = i;
35654558Sbp				return ENOEXEC;
35754558Sbp			}
35854558Sbp			if (gz->file_offset == 0) {
35954558Sbp				q = (u_char *) (uintptr_t) gz->virtual_offset;
36054558Sbp				copyout(&gz->a_out, q, sizeof gz->a_out);
36154558Sbp			}
36259681Sbp		}
36354558Sbp	}
36454558Sbp	/* Skip over zero-padded first PAGE if needed */
36554558Sbp	if (gz->output < gz->file_offset &&
36659681Sbp	    gz->output + siz > gz->file_offset) {
36754558Sbp		i = min(siz, gz->file_offset - gz->output);
36854558Sbp		gz->output += i;
36954558Sbp		p += i;
37054558Sbp		siz -= i;
37154558Sbp	}
37254558Sbp	if (gz->output >= gz->file_offset && gz->output < gz->file_end) {
37354558Sbp		i = min(siz, gz->file_end - gz->output);
37454558Sbp		q = (u_char *) (uintptr_t)
375106939Ssam		    (gz->virtual_offset + gz->output - gz->file_offset);
37654558Sbp		copyout(p, q, i);
37754558Sbp		gz->output += i;
37854558Sbp		p += i;
37954558Sbp		siz -= i;
38054558Sbp	}
38154558Sbp	gz->output += siz;
38254558Sbp	return 0;
38397220Speter}
38459681Sbp
38554558Sbp
38654558Sbp/*
38754558Sbp * Tell kern_execve.c about it, with a little help from the linker.
38854558Sbp */
38954558Sbpstatic struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"};
39054558SbpEXEC_SET(execgzip, gzip_execsw);
39154558Sbp