imgact_gzip.c revision 3417
1/*
2 * Parts of this file are not covered by:
3 * ----------------------------------------------------------------------------
4 * "THE BEER-WARE LICENSE" (Revision 42):
5 * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
6 * can do whatever you want with this stuff. If we meet some day, and you think
7 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
8 * ----------------------------------------------------------------------------
9 *
10 * $Id: imgact_gzip.c,v 1.4 1994/10/04 06:51:42 phk Exp $
11 *
12 * This module handles execution of a.out files which have been run through
13 * "gzip -9".
14 *
15 * For now you need to use exactly this command to compress the binaries:
16 *
17 *		gzip -9 -v < /bin/sh > /tmp/sh
18 *
19 * TODO:
20 *	text-segments should be made R/O after being filled
21 *	is the vm-stuff safe ?
22 * 	should handle the entire header of gzip'ed stuff.
23 *	inflate isn't quite reentrant yet...
24 *	error-handling is a mess...
25 *	so is the rest...
26 *	tidy up unnecesary includes
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/resourcevar.h>
32#include <sys/exec.h>
33#include <sys/inflate.h>
34#include <sys/mman.h>
35#include <sys/malloc.h>
36#include <sys/imgact.h>
37#include <sys/imgact_aout.h>
38#include <sys/kernel.h>
39#include <sys/sysent.h>
40
41#include <vm/vm.h>
42#include <vm/vm_kern.h>
43
44extern struct sysentvec aout_sysvec;
45
46int
47exec_gzip_imgact(iparams)
48	struct image_params *iparams;
49{
50	int error,error2=0;
51	u_char *p = (u_char *) iparams->image_header;
52	struct gzip *gz;
53	struct gz_global gz_glbl;
54
55	/* If these four are not OK, it isn't a gzip file */
56	if (p[0] != 0x1f)   return -1;      /* 0    Simply magic	*/
57	if (p[1] != 0x8b)   return -1;      /* 1    Simply magic	*/
58	if (p[2] != 0x08)   return -1;      /* 2    Compression method	*/
59	if (p[9] != 0x03)   return -1;      /* 9    OS compressed on	*/
60
61	/* If this one contains anything but a comment or a filename
62	 * marker, we don't want to chew on it
63	 */
64	if (p[3] & ~(0x18)) return ENOEXEC; /* 3    Flags		*/
65
66	/* These are of no use to us */
67					    /* 4-7  Timestamp		*/
68					    /* 8    Extra flags		*/
69
70	gz = malloc(sizeof *gz,M_GZIP,M_NOWAIT);
71	if (!gz)
72		return ENOMEM;
73	bzero(gz,sizeof *gz); /* waste of time ? */
74
75	gz->gz_slide = malloc(WSIZE,M_TEMP,M_NOWAIT);
76	if (!gz->gz_slide) {
77		free(gz,M_GZIP);
78		return ENOMEM;
79	}
80
81	gz->ip = iparams;
82	gz->error = ENOEXEC;
83	gz->idx = 10;
84
85	if (p[3] & 0x08) {  /* skip a filename */
86	    while (p[gz->idx++])
87		if (gz->idx >= PAGE_SIZE)
88		    goto done;
89	}
90
91	if (p[3] & 0x10) {  /* skip a comment */
92	    while (p[gz->idx++])
93		if (gz->idx >= PAGE_SIZE)
94		    goto done;
95	}
96
97	gz->len = gz->ip->attr->va_size;
98
99	gz->error = 0;
100
101	error = inflate(gz, &gz_glbl);
102
103	if (gz->inbuf) {
104	    error2 =
105		vm_deallocate(kernel_map, (vm_offset_t)gz->inbuf, PAGE_SIZE);
106	}
107
108	if (gz->error || error || error2) {
109	    printf("Output=%lu ",gz->output);
110	    printf("Inflate_error=%d gz->error=%d error2=%d where=%d\n",
111		error,gz->error,error2,gz->where);
112	    if (gz->error)
113		goto done;
114	    if (error) {
115		gz->error = ENOEXEC;
116		goto done;
117	    }
118	    if (error2) {
119		gz->error = error2;
120		goto done;
121	    }
122	}
123
124    done:
125	error = gz->error;
126	free(gz->gz_slide,M_TEMP);
127	free(gz,M_GZIP);
128	return error;
129}
130
131int
132do_aout_hdr(struct gzip *gz)
133{
134    int error;
135    struct vmspace *vmspace = gz->ip->proc->p_vmspace;
136    u_long vmaddr;
137
138    /*
139     * Set file/virtual offset based on a.out variant.
140     *	We do two cases: host byte order and network byte order
141     *	(for NetBSD compatibility)
142     */
143    switch ((int)(gz->a_out.a_magic & 0xffff)) {
144    case ZMAGIC:
145	gz->virtual_offset = 0;
146	if (gz->a_out.a_text) {
147	    gz->file_offset = NBPG;
148	} else {
149	    /* Bill's "screwball mode" */
150	    gz->file_offset = 0;
151	}
152	break;
153    case QMAGIC:
154	gz->virtual_offset = NBPG;
155	gz->file_offset = 0;
156	break;
157    default:
158	/* NetBSD compatibility */
159	switch ((int)(ntohl(gz->a_out.a_magic) & 0xffff)) {
160	case ZMAGIC:
161	case QMAGIC:
162	    gz->virtual_offset = NBPG;
163	    gz->file_offset = 0;
164	    break;
165	default:
166	    gz->where = __LINE__;
167	    return (-1);
168	}
169    }
170
171    gz->bss_size = roundup(gz->a_out.a_bss, NBPG);
172
173    /*
174     * Check various fields in header for validity/bounds.
175     */
176    if (/* entry point must lay with text region */
177	gz->a_out.a_entry < gz->virtual_offset ||
178	gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text ||
179
180	/* text and data size must each be page rounded */
181	gz->a_out.a_text % NBPG ||
182	gz->a_out.a_data % NBPG) {
183	    gz->where = __LINE__;
184	    return (-1);
185    }
186
187    /*
188     * text/data/bss must not exceed limits
189     */
190    if (/* text can't exceed maximum text size */
191	gz->a_out.a_text > MAXTSIZ ||
192
193	/* data + bss can't exceed maximum data size */
194	gz->a_out.a_data + gz->bss_size > MAXDSIZ ||
195
196	/* data + bss can't exceed rlimit */
197	gz->a_out.a_data + gz->bss_size >
198	    gz->ip->proc->p_rlimit[RLIMIT_DATA].rlim_cur) {
199		    gz->where = __LINE__;
200		    return (ENOMEM);
201    }
202
203    /* Find out how far we should go */
204    gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data;
205
206    /* copy in arguments and/or environment from old process */
207    error = exec_extract_strings(gz->ip);
208    if (error) {
209	gz->where = __LINE__;
210	return (error);
211    }
212
213    /*
214     * Destroy old process VM and create a new one (with a new stack)
215     */
216    exec_new_vmspace(gz->ip);
217
218    vmaddr = gz->virtual_offset;
219
220    error = vm_mmap(&vmspace->vm_map,           /* map */
221	&vmaddr,                                /* address */
222	gz->a_out.a_text,                      /* size */
223	VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE,  /* protection */
224	VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_WRITE,
225	MAP_ANON | MAP_FIXED,                /* flags */
226	0,				        /* vnode */
227	0);                                     /* offset */
228
229    if (error) {
230	gz->where = __LINE__;
231	return (error);
232    }
233
234    vmaddr = gz->virtual_offset + gz->a_out.a_text;
235
236    /*
237     * Map data read/write (if text is 0, assume text is in data area
238     *      [Bill's screwball mode])
239     */
240
241    error = vm_mmap(&vmspace->vm_map,
242	&vmaddr,
243	gz->a_out.a_data,
244	VM_PROT_READ | VM_PROT_WRITE | (gz->a_out.a_text ? 0 : VM_PROT_EXECUTE),
245	VM_PROT_ALL, MAP_ANON | MAP_FIXED,
246	0,
247	0);
248
249    if (error) {
250	gz->where = __LINE__;
251	return (error);
252    }
253
254    /*
255     * Allocate demand-zeroed area for uninitialized data
256     * "bss" = 'block started by symbol' - named after the IBM 7090
257     *      instruction of the same name.
258     */
259    vmaddr = gz->virtual_offset + gz->a_out.a_text + gz->a_out.a_data;
260    error = vm_allocate(&vmspace->vm_map, &vmaddr, gz->bss_size, FALSE);
261    if (error) {
262	gz->where = __LINE__;
263	return (error);
264    }
265
266    /* Fill in process VM information */
267    vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT;
268    vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT;
269    vmspace->vm_taddr = (caddr_t) gz->virtual_offset;
270    vmspace->vm_daddr = (caddr_t) gz->virtual_offset + gz->a_out.a_text;
271
272    /* Fill in image_params */
273    gz->ip->interpreted = 0;
274    gz->ip->entry_addr = gz->a_out.a_entry;
275
276    gz->ip->proc->p_sysent = &aout_sysvec;
277
278    return 0;
279}
280
281/*
282 * Tell kern_execve.c about it, with a little help from the linker.
283 * Since `const' objects end up in the text segment, TEXT_SET is the
284 * correct directive to use.
285 */
286static const struct execsw gzip_execsw = { exec_gzip_imgact, "gzip" };
287TEXT_SET(execsw_set, gzip_execsw);
288
289