openssl: Adjust manual pages for 1.0.1l.
[dragonfly.git] / sys / kern / imgact_gzip.c
1 /*
2  * ----------------------------------------------------------------------------
3  * "THE BEER-WARE LICENSE" (Revision 42):
4  * <phk@login.dkuug.dk> wrote this file.  As long as you retain this notice you
5  * can do whatever you want with this stuff. If we meet some day, and you think
6  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7  * ----------------------------------------------------------------------------
8  *
9  * $FreeBSD: src/sys/kern/imgact_gzip.c,v 1.40.2.1 2001/11/03 01:41:08 ps Exp $
10  * $DragonFly: src/sys/kern/imgact_gzip.c,v 1.9 2006/12/28 21:24:01 dillon Exp $
11  *
12  * This module handles execution of a.out files which have been run through
13  * "gzip".  This saves diskspace, but wastes cpu-cycles and VM.
14  *
15  * TODO:
16  *      text-segments should be made R/O after being filled
17  *      is the vm-stuff safe ?
18  *      should handle the entire header of gzip'ed stuff.
19  *      inflate isn't quite reentrant yet...
20  *      error-handling is a mess...
21  *      so is the rest...
22  *      tidy up unnecesary includes
23  */
24
25 #include <sys/param.h>
26 #include <sys/exec.h>
27 #include <sys/imgact.h>
28 #include <sys/imgact_aout.h>
29 #include <sys/kernel.h>
30 #include <sys/mman.h>
31 #include <sys/proc.h>
32 #include <sys/resourcevar.h>
33 #include <sys/sysent.h>
34 #include <sys/systm.h>
35 #include <sys/vnode.h>
36 #include <sys/inflate.h>
37
38 #include <vm/vm.h>
39 #include <vm/vm_param.h>
40 #include <sys/lock.h>
41 #include <vm/pmap.h>
42 #include <vm/vm_map.h>
43 #include <vm/vm_kern.h>
44 #include <vm/vm_extern.h>
45
46 struct imgact_gzip {
47         struct image_params *ip;
48         struct exec     a_out;
49         int             error;
50         int             gotheader;
51         int             where;
52         u_char         *inbuf;
53         u_long          offset;
54         u_long          output;
55         u_long          len;
56         int             idx;
57         u_long          virtual_offset, file_offset, file_end, bss_size;
58 };
59
60 static int exec_gzip_imgact (struct image_params *imgp);
61 static int NextByte (void *vp);
62 static int do_aout_hdr (struct imgact_gzip *);
63 static int Flush (void *vp, u_char *, u_long siz);
64
65 static int
66 exec_gzip_imgact(struct image_params *imgp)
67 {
68         int             error, error2 = 0;
69         const u_char   *p = (const u_char *) imgp->image_header;
70         struct imgact_gzip igz;
71         struct inflate  infl;
72         struct vmspace *vmspace;
73
74         /* If these four are not OK, it isn't a gzip file */
75         if (p[0] != 0x1f)
76                 return -1;      /* 0    Simply magic     */
77         if (p[1] != 0x8b)
78                 return -1;      /* 1    Simply magic     */
79         if (p[2] != 0x08)
80                 return -1;      /* 2    Compression method       */
81         if (p[9] != 0x03)
82                 return -1;      /* 9    OS compressed on         */
83
84         /*
85          * If this one contains anything but a comment or a filename marker,
86          * we don't want to chew on it
87          */
88         if (p[3] & ~(0x18))
89                 return ENOEXEC; /* 3    Flags            */
90
91         /* These are of no use to us */
92         /* 4-7  Timestamp                */
93         /* 8    Extra flags              */
94
95         bzero(&igz, sizeof igz);
96         bzero(&infl, sizeof infl);
97         infl.gz_private = (void *) &igz;
98         infl.gz_input = NextByte;
99         infl.gz_output = Flush;
100
101         igz.ip = imgp;
102         igz.idx = 10;
103
104         if (p[3] & 0x08) {      /* skip a filename */
105                 while (p[igz.idx++])
106                         if (igz.idx >= PAGE_SIZE)
107                                 return ENOEXEC;
108         }
109         if (p[3] & 0x10) {      /* skip a comment */
110                 while (p[igz.idx++])
111                         if (igz.idx >= PAGE_SIZE)
112                                 return ENOEXEC;
113         }
114         igz.len = imgp->attr->va_size;
115
116         error = inflate(&infl);
117
118         /*
119          * The unzipped file may not even have been long enough to contain
120          * a header giving Flush() a chance to return error.  Check for this.
121          */
122         if ( !igz.gotheader )
123                 return ENOEXEC;
124
125         if ( !error ) {
126                 vmspace = imgp->proc->p_vmspace;
127                 error = vm_map_protect(&vmspace->vm_map,
128                         (vm_offset_t) vmspace->vm_taddr,
129                         (vm_offset_t) (vmspace->vm_taddr + 
130                                       (vmspace->vm_tsize << PAGE_SHIFT)) ,
131                         VM_PROT_READ|VM_PROT_EXECUTE,0);
132         }
133
134         if (igz.inbuf) {
135                 error2 = vm_map_remove(&kernel_map, (vm_offset_t)igz.inbuf,
136                                        (vm_offset_t)igz.inbuf + PAGE_SIZE);
137         }
138         if (igz.error || error || error2) {
139                 kprintf("Output=%lu ", igz.output);
140                 kprintf("Inflate_error=%d igz.error=%d error2=%d where=%d\n",
141                        error, igz.error, error2, igz.where);
142         }
143         if (igz.error)
144                 return igz.error;
145         if (error)
146                 return ENOEXEC;
147         if (error2)
148                 return error2;
149         return 0;
150 }
151
152 static int
153 do_aout_hdr(struct imgact_gzip * gz)
154 {
155         int             error;
156         struct vmspace *vmspace;
157         vm_offset_t     vmaddr;
158
159         /*
160          * Set file/virtual offset based on a.out variant. We do two cases:
161          * host byte order and network byte order (for NetBSD compatibility)
162          */
163         switch ((int) (gz->a_out.a_magic & 0xffff)) {
164         case ZMAGIC:
165                 gz->virtual_offset = 0;
166                 if (gz->a_out.a_text) {
167                         gz->file_offset = PAGE_SIZE;
168                 } else {
169                         /* Bill's "screwball mode" */
170                         gz->file_offset = 0;
171                 }
172                 break;
173         case QMAGIC:
174                 gz->virtual_offset = PAGE_SIZE;
175                 gz->file_offset = 0;
176                 break;
177         default:
178                 /* NetBSD compatibility */
179                 switch ((int) (ntohl(gz->a_out.a_magic) & 0xffff)) {
180                 case ZMAGIC:
181                 case QMAGIC:
182                         gz->virtual_offset = PAGE_SIZE;
183                         gz->file_offset = 0;
184                         break;
185                 default:
186                         gz->where = __LINE__;
187                         return (-1);
188                 }
189         }
190
191         gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE);
192
193         /*
194          * Check various fields in header for validity/bounds.
195          */
196         if (                    /* entry point must lay with text region */
197             gz->a_out.a_entry < gz->virtual_offset ||
198             gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text ||
199
200         /* text and data size must each be page rounded */
201             gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) {
202                 gz->where = __LINE__;
203                 return (-1);
204         }
205         /*
206          * text/data/bss must not exceed limits
207          */
208         if (                    /* text can't exceed maximum text size */
209             gz->a_out.a_text > maxtsiz ||
210
211         /* data + bss can't exceed rlimit */
212             gz->a_out.a_data + gz->bss_size >
213             gz->ip->proc->p_rlimit[RLIMIT_DATA].rlim_cur) {
214                 gz->where = __LINE__;
215                 return (ENOMEM);
216         }
217         /* Find out how far we should go */
218         gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data;
219
220         /*
221          * Destroy old process VM and create a new one (with a new stack)
222          */
223         exec_new_vmspace(gz->ip, NULL);
224
225         vmspace = gz->ip->proc->p_vmspace;
226
227         vmaddr = gz->virtual_offset;
228
229         error = vm_mmap(&vmspace->vm_map,
230                         &vmaddr,
231                         gz->a_out.a_text + gz->a_out.a_data,
232                         VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED,
233                         0,
234                         0);
235
236         if (error) {
237                 gz->where = __LINE__;
238                 return (error);
239         }
240
241         if (gz->bss_size != 0) {
242                 /*
243                  * Allocate demand-zeroed area for uninitialized data.
244                  * "bss" = 'block started by symbol' - named after the 
245                  * IBM 7090 instruction of the same name.
246                  */
247                 vmaddr = gz->virtual_offset + gz->a_out.a_text + 
248                         gz->a_out.a_data;
249                 error = vm_map_find(&vmspace->vm_map,
250                                     NULL, NULL,
251                                     0, &vmaddr, gz->bss_size, PAGE_SIZE,
252                                     FALSE, VM_MAPTYPE_NORMAL,
253                                     VM_PROT_ALL, VM_PROT_ALL, 0);
254                 if (error) {
255                         gz->where = __LINE__;
256                         return (error);
257                 }
258         }
259         /* Fill in process VM information */
260         vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT;
261         vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT;
262         vmspace->vm_taddr = (caddr_t) (uintptr_t) gz->virtual_offset;
263         vmspace->vm_daddr = (caddr_t) (uintptr_t)
264                             (gz->virtual_offset + gz->a_out.a_text);
265
266         /* Fill in image_params */
267         gz->ip->interpreted = 0;
268         gz->ip->entry_addr = gz->a_out.a_entry;
269
270         gz->ip->proc->p_sysent = &aout_sysvec;
271
272         return 0;
273 }
274
275 static int
276 NextByte(void *vp)
277 {
278         int             error;
279         struct imgact_gzip *igz = (struct imgact_gzip *) vp;
280
281         if (igz->idx >= igz->len) {
282                 igz->where = __LINE__;
283                 return GZ_EOF;
284         }
285         if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) {
286                 return igz->inbuf[(igz->idx++) - igz->offset];
287         }
288         if (igz->inbuf) {
289                 error = vm_map_remove(&kernel_map, (vm_offset_t)igz->inbuf,
290                                       (vm_offset_t)igz->inbuf + PAGE_SIZE);
291                 if (error) {
292                         igz->where = __LINE__;
293                         igz->error = error;
294                         return GZ_EOF;
295                 }
296         }
297         igz->offset = igz->idx & ~PAGE_MASK;
298
299         error = vm_mmap(&kernel_map,    /* map */
300                         (vm_offset_t *) & igz->inbuf,   /* address */
301                         PAGE_SIZE,      /* size */
302                         VM_PROT_READ,   /* protection */
303                         VM_PROT_READ,   /* max protection */
304                         0,      /* flags */
305                         (caddr_t) igz->ip->vp,  /* vnode */
306                         igz->offset);   /* offset */
307         if (error) {
308                 igz->where = __LINE__;
309                 igz->error = error;
310                 return GZ_EOF;
311         }
312         return igz->inbuf[(igz->idx++) - igz->offset];
313 }
314
315 static int
316 Flush(void *vp, u_char * ptr, u_long siz)
317 {
318         struct imgact_gzip *gz = (struct imgact_gzip *) vp;
319         u_char         *p = ptr, *q;
320         int             i;
321
322         /* First, find a a.out-header */
323         if (gz->output < sizeof gz->a_out) {
324                 q = (u_char *) & gz->a_out;
325                 i = min(siz, sizeof gz->a_out - gz->output);
326                 bcopy(p, q + gz->output, i);
327                 gz->output += i;
328                 p += i;
329                 siz -= i;
330                 if (gz->output == sizeof gz->a_out) {
331                         gz->gotheader = 1;
332                         i = do_aout_hdr(gz);
333                         if (i == -1) {
334                                 if (!gz->where)
335                                         gz->where = __LINE__;
336                                 gz->error = ENOEXEC;
337                                 return ENOEXEC;
338                         } else if (i) {
339                                 gz->where = __LINE__;
340                                 gz->error = i;
341                                 return ENOEXEC;
342                         }
343                         if (gz->file_offset == 0) {
344                                 q = (u_char *) (uintptr_t) gz->virtual_offset;
345                                 copyout(&gz->a_out, q, sizeof gz->a_out);
346                         }
347                 }
348         }
349         /* Skip over zero-padded first PAGE if needed */
350         if (gz->output < gz->file_offset &&
351             gz->output + siz > gz->file_offset) {
352                 i = min(siz, gz->file_offset - gz->output);
353                 gz->output += i;
354                 p += i;
355                 siz -= i;
356         }
357         if (gz->output >= gz->file_offset && gz->output < gz->file_end) {
358                 i = min(siz, gz->file_end - gz->output);
359                 q = (u_char *) (uintptr_t)
360                     (gz->virtual_offset + gz->output - gz->file_offset);
361                 copyout(p, q, i);
362                 gz->output += i;
363                 p += i;
364                 siz -= i;
365         }
366         gz->output += siz;
367         return 0;
368 }
369
370
371 /*
372  * Tell kern_execve.c about it, with a little help from the linker.
373  */
374 static struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"};
375 EXEC_SET(execgzip, gzip_execsw);