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