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