Fixed warning from gcc1 (but not gcc2 for some reason).
[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 *
53 * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
54 * -------------------- ----- ----------------------
55 * CURRENT PATCH LEVEL: 6 00165
56 * -------------------- ----- ----------------------
57 *
58 * 05 Aug 92 Paul Kranenburg Fixed #! as a magic number
59 * 29 Jul 92 Mark Tinguely Fixed execute permission enforcement
60 * 15 Aug 92 Terry Lambert Fixed CMOS RAM size bug
61 * 12 Dec 92 Julians Elischer Place argc into user address space
62 * correctly
63 * 10 Apr 93 Yoval Yarom Fix for busy text on executables
64 * 20 Apr 93 markie Stop execution of a file open for write
65 * Rodney W. Grimes Fix date on Yoval Yarom's patch
66 * 01 Jun 93 Chris Demetriou Completed markie's patch for VTEXT busy
67 */
68
69#include "param.h"
70#include "systm.h"
71#include "signalvar.h"
72#include "resourcevar.h"
73#include "proc.h"
74#include "mount.h"
75#include "namei.h"
76#include "vnode.h"
77#include "file.h"
78#include "exec.h"
79#include "stat.h"
80#include "wait.h"
81#include "mman.h"
82#include "malloc.h"
83
84#include "vm/vm.h"
85#include "vm/vm_param.h"
86#include "vm/vm_map.h"
87#include "vm/vm_kern.h"
88
89#include "machine/reg.h"
90
91extern int dostacklimits;
92#define copyinoutstr copyinstr
93
94/*
95 * execve() system call.
96 */
97
3c7eb27c
DG
98struct execve_args {
99 char *fname;
100 char **argp;
101 char **envp;
102};
103
15637ed4
RG
104/* ARGSUSED */
105execve(p, uap, retval)
106 struct proc *p;
3c7eb27c 107 register struct execve_args *uap;
15637ed4
RG
108 int *retval;
109{
110 register struct nameidata *ndp;
111 struct nameidata nd;
112 char **argbuf, **argbufp, *stringbuf, *stringbufp;
113 char **vectp, *ep;
114 int needsenv, limitonargs, stringlen, addr, size, len,
115 rv, amt, argc, tsize, dsize, bsize, cnt, foff;
116 struct vattr attr;
117 struct vmspace *vs;
118 caddr_t newframe;
119 char shellname[MAXINTERP]; /* 05 Aug 92*/
fdc9cb92 120 char *shellargs;
15637ed4
RG
121 union {
122 char ex_shell[MAXINTERP]; /* #! and interpreter name */
123 struct exec ex_hdr;
124 } exdata;
125 int indir = 0;
126
127 /*
128 * Step 1. Lookup filename to see if we have something to execute.
129 */
130 ndp = &nd;
131 ndp->ni_segflg = UIO_USERSPACE;
132 ndp->ni_dirp = uap->fname;
133
134again: /* 05 Aug 92*/
135 ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW | SAVENAME;
136
137 /* is it there? */
138 if (rv = namei(ndp, p))
139 return (rv);
140
141 if (ndp->ni_vp->v_writecount) { /* don't exec if file is busy */
142 rv = EBUSY;
143 goto exec_fail;
144 }
145 /* does it have any attributes? */
146 rv = VOP_GETATTR(ndp->ni_vp, &attr, p->p_ucred, p);
147 if (rv)
148 goto exec_fail;
149
150 if (ndp->ni_vp->v_mount->mnt_flag & MNT_NOEXEC) { /* no exec on fs ?*/
151 rv = EACCES;
152 goto exec_fail;
153 }
154
155 /* is it executable, and a regular file? */
156 if ((ndp->ni_vp->v_mount->mnt_flag & MNT_NOEXEC) || /* 29 Jul 92*/
157 (VOP_ACCESS(ndp->ni_vp, VEXEC, p->p_ucred, p)) ||
158 ((attr.va_mode & 0111) == 0) ||
159 (attr.va_type != VREG)) {
160 rv = EACCES;
161 goto exec_fail;
162 }
163
164 /*
165 * Step 2. Does the file contain a format we can
166 * understand and execute
167 *
168 * XXX 05 Aug 92
169 * Read in first few bytes of file for segment sizes, magic number:
170 * ZMAGIC = demand paged RO text
171 * Also an ASCII line beginning with #! is
172 * the file name of a ``shell'' and arguments may be prepended
173 * to the argument list if given here.
174 */
175 exdata.ex_shell[0] = '\0'; /* for zero length files */
176
177 rv = vn_rdwr(UIO_READ, ndp->ni_vp, (caddr_t)&exdata, sizeof(exdata),
178 0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &amt, p);
179
180 /* big enough to hold a header? */
181 if (rv)
182 goto exec_fail;
183
184 if (exdata.ex_hdr.a_text != 0 && (ndp->ni_vp->v_flag & VTEXT) == 0 &&
185 ndp->ni_vp->v_writecount != 0) {
186 rv = ETXTBSY;
187 goto exec_fail;
188 }
189
190
191 /* ... that we recognize? */
192 rv = ENOEXEC;
193 if (exdata.ex_hdr.a_magic != ZMAGIC) {
194 char *cp, *sp;
195
196 if (exdata.ex_shell[0] != '#' ||
197 exdata.ex_shell[1] != '!' || indir) {
198 rv = ENOEXEC;
199 goto exec_fail;
200 }
201 for (cp = &exdata.ex_shell[2];; ++cp) {
202 if (cp >= &exdata.ex_shell[MAXINTERP]) {
203 rv = ENOEXEC;
204 goto exec_fail;
205 }
206 if (*cp == '\n') {
207 *cp = '\0';
208 break;
209 }
210 if (*cp == '\t')
211 *cp = ' ';
212 }
213 cp = &exdata.ex_shell[2]; /* get shell interpreter name */
214 while (*cp == ' ')
215 cp++;
216
217 sp = shellname;
218 while (*cp && *cp != ' ')
219 *sp++ = *cp++;
220 *sp = '\0';
221
fdc9cb92
NW
222 /* copy the args in the #! line */
223 while (*cp == ' ')
224 cp++;
225 if (*cp) {
226 sp++;
227 shellargs = sp;
228 while (*cp)
229 *sp++ = *cp++;
230 *sp = '\0';
231 } else {
232 shellargs = 0;
233 }
234
15637ed4
RG
235 indir = 1; /* indicate this is a script file */
236 vput(ndp->ni_vp);
237 FREE(ndp->ni_pnbuf, M_NAMEI);
238
239 ndp->ni_dirp = shellname; /* find shell interpreter */
240 ndp->ni_segflg = UIO_SYSSPACE;
241 goto again;
242 }
243
244 /* sanity check "ain't not such thing as a sanity clause" -groucho */
245 rv = ENOMEM;
246 if (/*exdata.ex_hdr.a_text == 0 || */ exdata.ex_hdr.a_text > MAXTSIZ ||
247 exdata.ex_hdr.a_text % NBPG || exdata.ex_hdr.a_text > attr.va_size)
248 goto exec_fail;
249
250 if (exdata.ex_hdr.a_data == 0 || exdata.ex_hdr.a_data > DFLDSIZ
251 || exdata.ex_hdr.a_data > attr.va_size
252 || exdata.ex_hdr.a_data + exdata.ex_hdr.a_text > attr.va_size)
253 goto exec_fail;
254
255 if (exdata.ex_hdr.a_bss > MAXDSIZ)
256 goto exec_fail;
257
258 if (exdata.ex_hdr.a_text + exdata.ex_hdr.a_data + exdata.ex_hdr.a_bss > MAXTSIZ + MAXDSIZ)
259 goto exec_fail;
260
261 if (exdata.ex_hdr.a_data + exdata.ex_hdr.a_bss > p->p_rlimit[RLIMIT_DATA].rlim_cur)
262 goto exec_fail;
263
264 if (exdata.ex_hdr.a_entry > exdata.ex_hdr.a_text + exdata.ex_hdr.a_data)
265 goto exec_fail;
266
267 /*
268 * Step 3. File and header are valid. Now, dig out the strings
269 * out of the old process image.
270 */
271
272 /*
273 * We implement a single-pass algorithm that builds a new stack
274 * frame within the address space of the "old" process image,
275 * avoiding the second pass entirely. Thus, the new frame is
276 * in position to be run. This consumes much virtual address space,
277 * and two pages more of 'real' memory, such are the costs.
278 * [Also, note the cache wipe that's avoided!]
279 */
280
281 /* create anonymous memory region for new stack */
282 vs = p->p_vmspace;
283 if ((unsigned)vs->vm_maxsaddr + MAXSSIZ < USRSTACK)
284 newframe = (caddr_t) USRSTACK - MAXSSIZ;
285 else
286 vs->vm_maxsaddr = newframe = (caddr_t) USRSTACK - 2*MAXSSIZ;
287
288 /* don't do stack limit checking on traps temporarily XXX*/
289 dostacklimits = 0;
290
291 rv = vm_allocate(&vs->vm_map, &newframe, MAXSSIZ, FALSE);
292 if (rv) goto exec_fail;
293
294 /* allocate string buffer and arg buffer */
295 argbuf = (char **) (newframe + MAXSSIZ - 3*ARG_MAX);
296 stringbuf = stringbufp = ((char *)argbuf) + 2*ARG_MAX;
297 argbufp = argbuf;
298
299 /* first, do args */
300 vectp = uap->argp;
301 needsenv = 1;
302 limitonargs = ARG_MAX;
303 cnt = 0;
304
305 /* first, do (shell name if any then) args */
306 if (indir) {
307 ep = shellname;
fdc9cb92 308thrice:
15637ed4
RG
309 if (ep) {
310 /* did we outgrow initial argbuf, if so, die */
311 if (argbufp >= (char **)stringbuf) {
312 rv = E2BIG;
313 goto exec_dealloc;
314 }
315
316 if (rv = copyoutstr(ep, stringbufp,
317 (u_int)limitonargs, (u_int *)&stringlen)) {
318 if (rv == ENAMETOOLONG)
319 rv = E2BIG;
320 goto exec_dealloc;
321 }
322 suword(argbufp++, (int)stringbufp);
323 cnt++;
324 stringbufp += stringlen;
325 limitonargs -= stringlen;
326 }
327
fdc9cb92
NW
328 if (shellargs) {
329 ep = shellargs;
330 shellargs = 0;
331 goto thrice;
332 }
333
15637ed4
RG
334 if (indir) {
335 indir = 0;
336 /* orginal executable is 1st argument with scripts */
337 ep = uap->fname;
fdc9cb92 338 goto thrice;
15637ed4
RG
339 }
340 /* terminate in case no more args to script */
341 suword(argbufp, 0);
342 if (vectp = uap->argp) vectp++; /* manually doing the first
343 argument with scripts */
344 }
345
346do_env_as_well:
347 if(vectp == 0) goto dont_bother;
348
349 /* for each envp, copy in string */
350 do {
351 /* did we outgrow initial argbuf, if so, die */
352 if (argbufp == (char **)stringbuf) {
353 rv = E2BIG;
354 goto exec_dealloc;
355 }
356
357 /* get an string pointer */
358 ep = (char *)fuword(vectp++);
359 if (ep == (char *)-1) {
360 rv = EFAULT;
361 goto exec_dealloc;
362 }
363
364 /* if not a null pointer, copy string */
365 if (ep) {
366 if (rv = copyinoutstr(ep, stringbufp,
367 (u_int)limitonargs, (u_int *) &stringlen)) {
368 if (rv == ENAMETOOLONG)
369 rv = E2BIG;
370 goto exec_dealloc;
371 }
372 suword(argbufp++, (int)stringbufp);
373 cnt++;
374 stringbufp += stringlen;
375 limitonargs -= stringlen;
376 } else {
377 suword(argbufp++, 0);
378 break;
379 }
380 } while (limitonargs > 0);
381
382dont_bother:
383 if (limitonargs <= 0) {
384 rv = E2BIG;
385 goto exec_dealloc;
386 }
387
388 /* have we done the environment yet ? */
389 if (needsenv) {
390 /* remember the arg count for later */
391 argc = cnt;
392 vectp = uap->envp;
393 needsenv = 0;
394 goto do_env_as_well;
395 }
396
397 /* At this point, one could optionally implement a
398 * second pass to condense the strings, arguement vectors,
399 * and stack to fit the fewest pages.
400 *
401 * One might selectively do this when copying was cheaper
402 * than leaving allocated two more pages per process.
403 */
404
405 /* stuff arg count on top of "new" stack */
406 /* argbuf[-1] = (char *)argc;*/
407 suword(argbuf-1,argc);
408
409 /*
410 * Step 4. Build the new processes image.
411 *
412 * At this point, we are committed -- destroy old executable!
413 */
414
415 /* blow away all address space, except the stack */
416 rv = vm_deallocate(&vs->vm_map, 0, USRSTACK - 2*MAXSSIZ);
417 if (rv)
418 goto exec_abort;
419
420 /* destroy "old" stack */
421 if ((unsigned)newframe < USRSTACK - MAXSSIZ) {
422 rv = vm_deallocate(&vs->vm_map, USRSTACK - MAXSSIZ, MAXSSIZ);
423 if (rv)
424 goto exec_abort;
425 } else {
426 rv = vm_deallocate(&vs->vm_map, USRSTACK - 2*MAXSSIZ, MAXSSIZ);
427 if (rv)
428 goto exec_abort;
429 }
430
431 /* build a new address space */
432 addr = 0;
433
434 /* screwball mode -- special case of 413 to save space for floppy */
435 if (exdata.ex_hdr.a_text == 0) {
436 foff = tsize = 0;
437 exdata.ex_hdr.a_data += exdata.ex_hdr.a_text;
438 } else {
439 tsize = roundup(exdata.ex_hdr.a_text, NBPG);
440 foff = NBPG;
441 }
442
443 /* treat text and data in terms of integral page size */
444 dsize = roundup(exdata.ex_hdr.a_data, NBPG);
445 bsize = roundup(exdata.ex_hdr.a_bss + dsize, NBPG);
446 bsize -= dsize;
447
448 /* map text & data in file, as being "paged in" on demand */
449 rv = vm_mmap(&vs->vm_map, &addr, tsize+dsize, VM_PROT_ALL,
450 MAP_FILE|MAP_COPY|MAP_FIXED, (caddr_t)ndp->ni_vp, foff);
451 if (rv)
452 goto exec_abort;
453
454 /* mark pages r/w data, r/o text */
455 if (tsize) {
456 addr = 0;
457 rv = vm_protect(&vs->vm_map, addr, tsize, FALSE,
458 VM_PROT_READ|VM_PROT_EXECUTE);
459 if (rv)
460 goto exec_abort;
461 }
462
463 /* create anonymous memory region for bss */
464 addr = dsize + tsize;
465 rv = vm_allocate(&vs->vm_map, &addr, bsize, FALSE);
466 if (rv)
467 goto exec_abort;
468
469 /*
470 * Step 5. Prepare process for execution.
471 */
472
473 /* touchup process information -- vm system is unfinished! */
474 vs->vm_tsize = tsize/NBPG; /* text size (pages) XXX */
475 vs->vm_dsize = (dsize+bsize)/NBPG; /* data size (pages) XXX */
476 vs->vm_taddr = 0; /* user virtual address of text XXX */
477 vs->vm_daddr = (caddr_t)tsize; /* user virtual address of data XXX */
478 vs->vm_maxsaddr = newframe; /* user VA at max stack growth XXX */
479 vs->vm_ssize = ((unsigned)vs->vm_maxsaddr + MAXSSIZ
480 - (unsigned)argbuf)/ NBPG + 1; /* stack size (pages) */
481 dostacklimits = 1; /* allow stack limits to be enforced XXX */
482
483 /* close files on exec, fixup signals */
484 fdcloseexec(p);
485 execsigs(p);
486
487 /* name this process - nameiexec(p, ndp) */
488 len = MIN(ndp->ni_namelen,MAXCOMLEN);
489 bcopy(ndp->ni_ptr, p->p_comm, len);
490 p->p_comm[len] = 0;
491
492 /* mark as executable, wakeup any process that was vforked and tell
493 * it that it now has it's own resources back */
494 p->p_flag |= SEXEC;
495 if (p->p_pptr && (p->p_flag & SPPWAIT)) {
496 p->p_flag &= ~SPPWAIT;
497 wakeup(p->p_pptr);
498 }
499
500 /* implement set userid/groupid */
501 if ((attr.va_mode&VSUID) && (p->p_flag & STRC) == 0) {
502 p->p_ucred = crcopy(p->p_ucred);
503 p->p_cred->p_svuid = p->p_ucred->cr_uid = attr.va_uid;
504 }
505 if ((attr.va_mode&VSGID) && (p->p_flag & STRC) == 0) {
506 p->p_ucred = crcopy(p->p_ucred);
507 p->p_cred->p_svgid = p->p_ucred->cr_groups[0] = attr.va_gid;
508 }
509
510 /* setup initial register state */
511 p->p_regs[SP] = (unsigned) (argbuf - 1);
512 setregs(p, exdata.ex_hdr.a_entry);
513
514 ndp->ni_vp->v_flag |= VTEXT; /* mark vnode pure text */
515
516 vput(ndp->ni_vp);
517 FREE(ndp->ni_pnbuf, M_NAMEI);
518
519 /* if tracing process, pass control back to debugger so breakpoints
520 can be set before the program "runs" */
521 if (p->p_flag & STRC)
522 psignal(p, SIGTRAP);
523
524 return (0);
525
526exec_dealloc:
527 /* remove interim "new" stack frame we were building */
528 vm_deallocate(&vs->vm_map, newframe, MAXSSIZ);
529
530exec_fail:
531 dostacklimits = 1;
532 vput(ndp->ni_vp);
533 FREE(ndp->ni_pnbuf, M_NAMEI);
534
535 return(rv);
536
537exec_abort:
538 /* sorry, no more process anymore. exit gracefully */
539 vm_deallocate(&vs->vm_map, newframe, MAXSSIZ);
540 vput(ndp->ni_vp);
541 FREE(ndp->ni_pnbuf, M_NAMEI);
542 exit(p, W_EXITCODE(0, SIGABRT));
543
544 /* NOTREACHED */
545 return(0);
546}