Accounting patch:
[unix-history] / sys / kern / kern_execve.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1989, 1990, 1991, 1992 William F. Jolitz, TeleMuse
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This software is a component of "386BSD" developed by
16 * William F. Jolitz, TeleMuse.
17 * 4. Neither the name of the developer nor the name "386BSD"
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
22 * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
23 * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
24 * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
25 * NOT MAKE USE OF THIS WORK.
26 *
27 * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
28 * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
29 * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
30 * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
31 * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
32 * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
33 * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
34 * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 *
48 * This procedure implements a minimal program execution facility for
49 * 386BSD. It interfaces to the BSD kernel as the execve system call.
50 * Significant limitations and lack of compatiblity with POSIX are
51 * present with this version, to make its basic operation more clear.
52 *
600f7f07 53 * $Id$
15637ed4
RG
54 */
55
56#include "param.h"
57#include "systm.h"
58#include "signalvar.h"
59#include "resourcevar.h"
60#include "proc.h"
61#include "mount.h"
62#include "namei.h"
63#include "vnode.h"
64#include "file.h"
65#include "exec.h"
66#include "stat.h"
67#include "wait.h"
68#include "mman.h"
69#include "malloc.h"
70
71#include "vm/vm.h"
72#include "vm/vm_param.h"
73#include "vm/vm_map.h"
74#include "vm/vm_kern.h"
75
76#include "machine/reg.h"
77
78extern int dostacklimits;
79#define copyinoutstr copyinstr
80
81/*
82 * execve() system call.
83 */
84
3c7eb27c
DG
85struct execve_args {
86 char *fname;
87 char **argp;
88 char **envp;
89};
90
15637ed4
RG
91/* ARGSUSED */
92execve(p, uap, retval)
93 struct proc *p;
3c7eb27c 94 register struct execve_args *uap;
15637ed4
RG
95 int *retval;
96{
97 register struct nameidata *ndp;
98 struct nameidata nd;
99 char **argbuf, **argbufp, *stringbuf, *stringbufp;
100 char **vectp, *ep;
101 int needsenv, limitonargs, stringlen, addr, size, len,
102 rv, amt, argc, tsize, dsize, bsize, cnt, foff;
103 struct vattr attr;
104 struct vmspace *vs;
105 caddr_t newframe;
106 char shellname[MAXINTERP]; /* 05 Aug 92*/
fdc9cb92 107 char *shellargs;
15637ed4
RG
108 union {
109 char ex_shell[MAXINTERP]; /* #! and interpreter name */
110 struct exec ex_hdr;
111 } exdata;
112 int indir = 0;
113
114 /*
115 * Step 1. Lookup filename to see if we have something to execute.
116 */
117 ndp = &nd;
118 ndp->ni_segflg = UIO_USERSPACE;
119 ndp->ni_dirp = uap->fname;
120
121again: /* 05 Aug 92*/
122 ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW | SAVENAME;
123
124 /* is it there? */
125 if (rv = namei(ndp, p))
126 return (rv);
127
128 if (ndp->ni_vp->v_writecount) { /* don't exec if file is busy */
129 rv = EBUSY;
130 goto exec_fail;
131 }
132 /* does it have any attributes? */
133 rv = VOP_GETATTR(ndp->ni_vp, &attr, p->p_ucred, p);
134 if (rv)
135 goto exec_fail;
136
137 if (ndp->ni_vp->v_mount->mnt_flag & MNT_NOEXEC) { /* no exec on fs ?*/
138 rv = EACCES;
139 goto exec_fail;
140 }
141
142 /* is it executable, and a regular file? */
143 if ((ndp->ni_vp->v_mount->mnt_flag & MNT_NOEXEC) || /* 29 Jul 92*/
144 (VOP_ACCESS(ndp->ni_vp, VEXEC, p->p_ucred, p)) ||
145 ((attr.va_mode & 0111) == 0) ||
146 (attr.va_type != VREG)) {
147 rv = EACCES;
148 goto exec_fail;
149 }
150
151 /*
152 * Step 2. Does the file contain a format we can
153 * understand and execute
154 *
155 * XXX 05 Aug 92
156 * Read in first few bytes of file for segment sizes, magic number:
157 * ZMAGIC = demand paged RO text
158 * Also an ASCII line beginning with #! is
159 * the file name of a ``shell'' and arguments may be prepended
160 * to the argument list if given here.
161 */
162 exdata.ex_shell[0] = '\0'; /* for zero length files */
163
164 rv = vn_rdwr(UIO_READ, ndp->ni_vp, (caddr_t)&exdata, sizeof(exdata),
165 0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &amt, p);
166
167 /* big enough to hold a header? */
168 if (rv)
169 goto exec_fail;
170
171 if (exdata.ex_hdr.a_text != 0 && (ndp->ni_vp->v_flag & VTEXT) == 0 &&
172 ndp->ni_vp->v_writecount != 0) {
173 rv = ETXTBSY;
174 goto exec_fail;
175 }
176
177
178 /* ... that we recognize? */
179 rv = ENOEXEC;
180 if (exdata.ex_hdr.a_magic != ZMAGIC) {
181 char *cp, *sp;
182
183 if (exdata.ex_shell[0] != '#' ||
184 exdata.ex_shell[1] != '!' || indir) {
185 rv = ENOEXEC;
186 goto exec_fail;
187 }
188 for (cp = &exdata.ex_shell[2];; ++cp) {
189 if (cp >= &exdata.ex_shell[MAXINTERP]) {
190 rv = ENOEXEC;
191 goto exec_fail;
192 }
193 if (*cp == '\n') {
194 *cp = '\0';
195 break;
196 }
197 if (*cp == '\t')
198 *cp = ' ';
199 }
200 cp = &exdata.ex_shell[2]; /* get shell interpreter name */
201 while (*cp == ' ')
202 cp++;
203
204 sp = shellname;
205 while (*cp && *cp != ' ')
206 *sp++ = *cp++;
207 *sp = '\0';
208
fdc9cb92
NW
209 /* copy the args in the #! line */
210 while (*cp == ' ')
211 cp++;
212 if (*cp) {
213 sp++;
214 shellargs = sp;
215 while (*cp)
216 *sp++ = *cp++;
217 *sp = '\0';
218 } else {
219 shellargs = 0;
220 }
221
15637ed4
RG
222 indir = 1; /* indicate this is a script file */
223 vput(ndp->ni_vp);
224 FREE(ndp->ni_pnbuf, M_NAMEI);
225
226 ndp->ni_dirp = shellname; /* find shell interpreter */
227 ndp->ni_segflg = UIO_SYSSPACE;
228 goto again;
229 }
230
231 /* sanity check "ain't not such thing as a sanity clause" -groucho */
232 rv = ENOMEM;
233 if (/*exdata.ex_hdr.a_text == 0 || */ exdata.ex_hdr.a_text > MAXTSIZ ||
234 exdata.ex_hdr.a_text % NBPG || exdata.ex_hdr.a_text > attr.va_size)
235 goto exec_fail;
236
237 if (exdata.ex_hdr.a_data == 0 || exdata.ex_hdr.a_data > DFLDSIZ
238 || exdata.ex_hdr.a_data > attr.va_size
239 || exdata.ex_hdr.a_data + exdata.ex_hdr.a_text > attr.va_size)
240 goto exec_fail;
241
242 if (exdata.ex_hdr.a_bss > MAXDSIZ)
243 goto exec_fail;
244
245 if (exdata.ex_hdr.a_text + exdata.ex_hdr.a_data + exdata.ex_hdr.a_bss > MAXTSIZ + MAXDSIZ)
246 goto exec_fail;
247
248 if (exdata.ex_hdr.a_data + exdata.ex_hdr.a_bss > p->p_rlimit[RLIMIT_DATA].rlim_cur)
249 goto exec_fail;
250
251 if (exdata.ex_hdr.a_entry > exdata.ex_hdr.a_text + exdata.ex_hdr.a_data)
252 goto exec_fail;
253
254 /*
255 * Step 3. File and header are valid. Now, dig out the strings
256 * out of the old process image.
257 */
258
259 /*
260 * We implement a single-pass algorithm that builds a new stack
261 * frame within the address space of the "old" process image,
262 * avoiding the second pass entirely. Thus, the new frame is
263 * in position to be run. This consumes much virtual address space,
264 * and two pages more of 'real' memory, such are the costs.
265 * [Also, note the cache wipe that's avoided!]
266 */
267
268 /* create anonymous memory region for new stack */
269 vs = p->p_vmspace;
270 if ((unsigned)vs->vm_maxsaddr + MAXSSIZ < USRSTACK)
271 newframe = (caddr_t) USRSTACK - MAXSSIZ;
272 else
273 vs->vm_maxsaddr = newframe = (caddr_t) USRSTACK - 2*MAXSSIZ;
274
275 /* don't do stack limit checking on traps temporarily XXX*/
276 dostacklimits = 0;
277
278 rv = vm_allocate(&vs->vm_map, &newframe, MAXSSIZ, FALSE);
279 if (rv) goto exec_fail;
280
281 /* allocate string buffer and arg buffer */
282 argbuf = (char **) (newframe + MAXSSIZ - 3*ARG_MAX);
283 stringbuf = stringbufp = ((char *)argbuf) + 2*ARG_MAX;
284 argbufp = argbuf;
285
286 /* first, do args */
287 vectp = uap->argp;
288 needsenv = 1;
289 limitonargs = ARG_MAX;
290 cnt = 0;
291
292 /* first, do (shell name if any then) args */
293 if (indir) {
294 ep = shellname;
fdc9cb92 295thrice:
15637ed4
RG
296 if (ep) {
297 /* did we outgrow initial argbuf, if so, die */
298 if (argbufp >= (char **)stringbuf) {
299 rv = E2BIG;
300 goto exec_dealloc;
301 }
302
303 if (rv = copyoutstr(ep, stringbufp,
304 (u_int)limitonargs, (u_int *)&stringlen)) {
305 if (rv == ENAMETOOLONG)
306 rv = E2BIG;
307 goto exec_dealloc;
308 }
309 suword(argbufp++, (int)stringbufp);
310 cnt++;
311 stringbufp += stringlen;
312 limitonargs -= stringlen;
313 }
314
fdc9cb92
NW
315 if (shellargs) {
316 ep = shellargs;
317 shellargs = 0;
318 goto thrice;
319 }
320
15637ed4
RG
321 if (indir) {
322 indir = 0;
323 /* orginal executable is 1st argument with scripts */
324 ep = uap->fname;
fdc9cb92 325 goto thrice;
15637ed4
RG
326 }
327 /* terminate in case no more args to script */
328 suword(argbufp, 0);
329 if (vectp = uap->argp) vectp++; /* manually doing the first
330 argument with scripts */
331 }
332
333do_env_as_well:
334 if(vectp == 0) goto dont_bother;
335
336 /* for each envp, copy in string */
337 do {
338 /* did we outgrow initial argbuf, if so, die */
339 if (argbufp == (char **)stringbuf) {
340 rv = E2BIG;
341 goto exec_dealloc;
342 }
343
344 /* get an string pointer */
345 ep = (char *)fuword(vectp++);
346 if (ep == (char *)-1) {
347 rv = EFAULT;
348 goto exec_dealloc;
349 }
350
351 /* if not a null pointer, copy string */
352 if (ep) {
353 if (rv = copyinoutstr(ep, stringbufp,
354 (u_int)limitonargs, (u_int *) &stringlen)) {
355 if (rv == ENAMETOOLONG)
356 rv = E2BIG;
357 goto exec_dealloc;
358 }
359 suword(argbufp++, (int)stringbufp);
360 cnt++;
361 stringbufp += stringlen;
362 limitonargs -= stringlen;
363 } else {
364 suword(argbufp++, 0);
365 break;
366 }
367 } while (limitonargs > 0);
368
369dont_bother:
370 if (limitonargs <= 0) {
371 rv = E2BIG;
372 goto exec_dealloc;
373 }
374
375 /* have we done the environment yet ? */
376 if (needsenv) {
377 /* remember the arg count for later */
378 argc = cnt;
379 vectp = uap->envp;
380 needsenv = 0;
381 goto do_env_as_well;
382 }
383
384 /* At this point, one could optionally implement a
385 * second pass to condense the strings, arguement vectors,
386 * and stack to fit the fewest pages.
387 *
388 * One might selectively do this when copying was cheaper
389 * than leaving allocated two more pages per process.
390 */
391
392 /* stuff arg count on top of "new" stack */
393 /* argbuf[-1] = (char *)argc;*/
394 suword(argbuf-1,argc);
395
396 /*
397 * Step 4. Build the new processes image.
398 *
399 * At this point, we are committed -- destroy old executable!
400 */
401
402 /* blow away all address space, except the stack */
403 rv = vm_deallocate(&vs->vm_map, 0, USRSTACK - 2*MAXSSIZ);
404 if (rv)
405 goto exec_abort;
406
407 /* destroy "old" stack */
408 if ((unsigned)newframe < USRSTACK - MAXSSIZ) {
409 rv = vm_deallocate(&vs->vm_map, USRSTACK - MAXSSIZ, MAXSSIZ);
410 if (rv)
411 goto exec_abort;
412 } else {
413 rv = vm_deallocate(&vs->vm_map, USRSTACK - 2*MAXSSIZ, MAXSSIZ);
414 if (rv)
415 goto exec_abort;
416 }
417
418 /* build a new address space */
419 addr = 0;
420
421 /* screwball mode -- special case of 413 to save space for floppy */
422 if (exdata.ex_hdr.a_text == 0) {
423 foff = tsize = 0;
424 exdata.ex_hdr.a_data += exdata.ex_hdr.a_text;
425 } else {
426 tsize = roundup(exdata.ex_hdr.a_text, NBPG);
427 foff = NBPG;
428 }
429
430 /* treat text and data in terms of integral page size */
431 dsize = roundup(exdata.ex_hdr.a_data, NBPG);
432 bsize = roundup(exdata.ex_hdr.a_bss + dsize, NBPG);
433 bsize -= dsize;
434
435 /* map text & data in file, as being "paged in" on demand */
5af262ca 436 rv = vm_mmap(&vs->vm_map, &addr, tsize+dsize, VM_PROT_ALL, VM_PROT_DEFAULT,
15637ed4
RG
437 MAP_FILE|MAP_COPY|MAP_FIXED, (caddr_t)ndp->ni_vp, foff);
438 if (rv)
439 goto exec_abort;
440
441 /* mark pages r/w data, r/o text */
442 if (tsize) {
443 addr = 0;
444 rv = vm_protect(&vs->vm_map, addr, tsize, FALSE,
445 VM_PROT_READ|VM_PROT_EXECUTE);
446 if (rv)
447 goto exec_abort;
448 }
449
450 /* create anonymous memory region for bss */
451 addr = dsize + tsize;
452 rv = vm_allocate(&vs->vm_map, &addr, bsize, FALSE);
453 if (rv)
454 goto exec_abort;
455
456 /*
457 * Step 5. Prepare process for execution.
458 */
459
460 /* touchup process information -- vm system is unfinished! */
461 vs->vm_tsize = tsize/NBPG; /* text size (pages) XXX */
462 vs->vm_dsize = (dsize+bsize)/NBPG; /* data size (pages) XXX */
463 vs->vm_taddr = 0; /* user virtual address of text XXX */
464 vs->vm_daddr = (caddr_t)tsize; /* user virtual address of data XXX */
465 vs->vm_maxsaddr = newframe; /* user VA at max stack growth XXX */
466 vs->vm_ssize = ((unsigned)vs->vm_maxsaddr + MAXSSIZ
467 - (unsigned)argbuf)/ NBPG + 1; /* stack size (pages) */
468 dostacklimits = 1; /* allow stack limits to be enforced XXX */
469
470 /* close files on exec, fixup signals */
471 fdcloseexec(p);
472 execsigs(p);
473
474 /* name this process - nameiexec(p, ndp) */
475 len = MIN(ndp->ni_namelen,MAXCOMLEN);
476 bcopy(ndp->ni_ptr, p->p_comm, len);
477 p->p_comm[len] = 0;
478
479 /* mark as executable, wakeup any process that was vforked and tell
480 * it that it now has it's own resources back */
481 p->p_flag |= SEXEC;
482 if (p->p_pptr && (p->p_flag & SPPWAIT)) {
483 p->p_flag &= ~SPPWAIT;
484 wakeup(p->p_pptr);
485 }
486
487 /* implement set userid/groupid */
488 if ((attr.va_mode&VSUID) && (p->p_flag & STRC) == 0) {
489 p->p_ucred = crcopy(p->p_ucred);
490 p->p_cred->p_svuid = p->p_ucred->cr_uid = attr.va_uid;
491 }
492 if ((attr.va_mode&VSGID) && (p->p_flag & STRC) == 0) {
493 p->p_ucred = crcopy(p->p_ucred);
494 p->p_cred->p_svgid = p->p_ucred->cr_groups[0] = attr.va_gid;
495 }
496
497 /* setup initial register state */
498 p->p_regs[SP] = (unsigned) (argbuf - 1);
499 setregs(p, exdata.ex_hdr.a_entry);
500
501 ndp->ni_vp->v_flag |= VTEXT; /* mark vnode pure text */
502
503 vput(ndp->ni_vp);
504 FREE(ndp->ni_pnbuf, M_NAMEI);
505
506 /* if tracing process, pass control back to debugger so breakpoints
507 can be set before the program "runs" */
508 if (p->p_flag & STRC)
509 psignal(p, SIGTRAP);
510
511 return (0);
512
513exec_dealloc:
514 /* remove interim "new" stack frame we were building */
515 vm_deallocate(&vs->vm_map, newframe, MAXSSIZ);
516
517exec_fail:
518 dostacklimits = 1;
519 vput(ndp->ni_vp);
520 FREE(ndp->ni_pnbuf, M_NAMEI);
521
522 return(rv);
523
524exec_abort:
525 /* sorry, no more process anymore. exit gracefully */
526 vm_deallocate(&vs->vm_map, newframe, MAXSSIZ);
527 vput(ndp->ni_vp);
528 FREE(ndp->ni_pnbuf, M_NAMEI);
e5b1af89 529 kexit(p, W_EXITCODE(0, SIGABRT));
15637ed4
RG
530
531 /* NOTREACHED */
532 return(0);
533}