diff options
Diffstat (limited to 'sys/kern/kern_execve.c')
| -rw-r--r-- | sys/kern/kern_execve.c | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/sys/kern/kern_execve.c b/sys/kern/kern_execve.c new file mode 100644 index 000000000000..003b2f768b07 --- /dev/null +++ b/sys/kern/kern_execve.c @@ -0,0 +1,559 @@ +/* + * Copyright (c) 1989, 1990, 1991, 1992 William F. Jolitz, TeleMuse + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This software is a component of "386BSD" developed by + * William F. Jolitz, TeleMuse. + * 4. Neither the name of the developer nor the name "386BSD" + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ + * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS + * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT. + * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT + * NOT MAKE USE OF THIS WORK. + * + * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED + * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN + * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES + * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING + * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND + * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE + * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS + * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This procedure implements a minimal program execution facility for + * 386BSD. It interfaces to the BSD kernel as the execve system call. + * Significant limitations and lack of compatiblity with POSIX are + * present with this version, to make its basic operation more clear. + * + * $Id: kern_execve.c,v 1.8 1993/10/25 17:26:01 davidg Exp $ + */ + +#include "param.h" +#include "systm.h" +#include "signalvar.h" +#include "resourcevar.h" +#include "proc.h" +#include "mount.h" +#include "namei.h" +#include "vnode.h" +#include "file.h" +#include "acct.h" +#include "exec.h" +#include "stat.h" +#include "wait.h" +#include "mman.h" +#include "malloc.h" + +#include "vm/vm.h" +#include "vm/vm_param.h" +#include "vm/vm_map.h" +#include "vm/vm_kern.h" + +#include "machine/reg.h" + +extern int dostacklimits; +#define copyinoutstr copyinstr + +/* + * execve() system call. + */ + +struct execve_args { + char *fname; + char **argp; + char **envp; +}; + +/* ARGSUSED */ +execve(p, uap, retval) + struct proc *p; + register struct execve_args *uap; + int *retval; +{ + register struct nameidata *ndp; + struct nameidata nd; + char **argbuf, **argbufp, *stringbuf, *stringbufp; + char **vectp, *ep; + int needsenv, limitonargs, stringlen, addr, size, len, + rv, amt, argc, tsize, dsize, bsize, cnt, file_offset, + virtual_offset; + struct vattr attr; + struct vmspace *vs; + caddr_t newframe; + char shellname[MAXINTERP]; /* 05 Aug 92*/ + char *shellargs; + union { + char ex_shell[MAXINTERP]; /* #! and interpreter name */ + struct exec ex_hdr; + } exdata; + int indir = 0; + + /* + * Step 1. Lookup filename to see if we have something to execute. + */ + ndp = &nd; + ndp->ni_segflg = UIO_USERSPACE; + ndp->ni_dirp = uap->fname; + +again: /* 05 Aug 92*/ + ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW | SAVENAME; + + /* is it there? */ + if (rv = namei(ndp, p)) + return (rv); + + if (ndp->ni_vp->v_writecount) { /* don't exec if file is busy */ + rv = EBUSY; + goto exec_fail; + } + /* does it have any attributes? */ + rv = VOP_GETATTR(ndp->ni_vp, &attr, p->p_ucred, p); + if (rv) + goto exec_fail; + + if (ndp->ni_vp->v_mount->mnt_flag & MNT_NOEXEC) { /* no exec on fs ?*/ + rv = EACCES; + goto exec_fail; + } + + /* is it executable, and a regular file? */ + if ((ndp->ni_vp->v_mount->mnt_flag & MNT_NOEXEC) || /* 29 Jul 92*/ + (VOP_ACCESS(ndp->ni_vp, VEXEC, p->p_ucred, p)) || + ((attr.va_mode & 0111) == 0) || + (attr.va_type != VREG)) { + rv = EACCES; + goto exec_fail; + } + + /* + * Step 2. Does the file contain a format we can + * understand and execute + * + * XXX 05 Aug 92 + * Read in first few bytes of file for segment sizes, magic number: + * ZMAGIC = demand paged RO text + * Also an ASCII line beginning with #! is + * the file name of a ``shell'' and arguments may be prepended + * to the argument list if given here. + */ + exdata.ex_shell[0] = '\0'; /* for zero length files */ + + rv = vn_rdwr(UIO_READ, ndp->ni_vp, (caddr_t)&exdata, sizeof(exdata), + 0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &amt, p); + + /* big enough to hold a header? */ + if (rv) + goto exec_fail; + + if (exdata.ex_hdr.a_text != 0 && (ndp->ni_vp->v_flag & VTEXT) == 0 && + ndp->ni_vp->v_writecount != 0) { + rv = ETXTBSY; + goto exec_fail; + } + +#define SHELLMAGIC 0x2123 /* #! */ + + switch (exdata.ex_hdr.a_magic) { + case ZMAGIC: + virtual_offset = 0; + if (exdata.ex_hdr.a_text) { + file_offset = NBPG; + } else { + /* Bill's "screwball mode" */ + file_offset = 0; + } + break; + case QMAGIC: + virtual_offset = NBPG; + file_offset = 0; + break; + default: + if ((exdata.ex_hdr.a_magic & 0xffff) != SHELLMAGIC) { + /* NetBSD compatibility */ + switch (ntohl(exdata.ex_hdr.a_magic) & 0xffff) { + case ZMAGIC: + case QMAGIC: + virtual_offset = NBPG; + file_offset = 0; + break; + default: + rv = ENOEXEC; + goto exec_fail; + } + } else { + char *cp, *sp; + + if (indir) { + rv = ENOEXEC; + goto exec_fail; + } + for (cp = &exdata.ex_shell[2];; ++cp) { + if (cp >= &exdata.ex_shell[MAXINTERP]) { + rv = ENOEXEC; + goto exec_fail; + } + if (*cp == '\n') { + *cp = '\0'; + break; + } + if (*cp == '\t') + *cp = ' '; + } + cp = &exdata.ex_shell[2]; /* get shell interpreter name */ + while (*cp == ' ') + cp++; + + sp = shellname; + while (*cp && *cp != ' ') + *sp++ = *cp++; + *sp = '\0'; + + /* copy the args in the #! line */ + while (*cp == ' ') + cp++; + if (*cp) { + sp++; + shellargs = sp; + while (*cp) + *sp++ = *cp++; + *sp = '\0'; + } else { + shellargs = 0; + } + + indir = 1; /* indicate this is a script file */ + vput(ndp->ni_vp); + FREE(ndp->ni_pnbuf, M_NAMEI); + + ndp->ni_dirp = shellname; /* find shell interpreter */ + ndp->ni_segflg = UIO_SYSSPACE; + goto again; + } + /* NOT REACHED */ + } + + /* sanity check "ain't not such thing as a sanity clause" -groucho */ + rv = ENOMEM; + if (/*exdata.ex_hdr.a_text == 0 || */ exdata.ex_hdr.a_text > MAXTSIZ || + exdata.ex_hdr.a_text % NBPG || exdata.ex_hdr.a_text > attr.va_size) + goto exec_fail; + + if (exdata.ex_hdr.a_data == 0 || exdata.ex_hdr.a_data > DFLDSIZ + || exdata.ex_hdr.a_data > attr.va_size + || exdata.ex_hdr.a_data + exdata.ex_hdr.a_text > attr.va_size) + goto exec_fail; + + if (exdata.ex_hdr.a_bss > MAXDSIZ) + goto exec_fail; + + if (exdata.ex_hdr.a_text + exdata.ex_hdr.a_data + exdata.ex_hdr.a_bss > MAXTSIZ + MAXDSIZ) + goto exec_fail; + + if (exdata.ex_hdr.a_data + exdata.ex_hdr.a_bss > p->p_rlimit[RLIMIT_DATA].rlim_cur) + goto exec_fail; + + if (exdata.ex_hdr.a_entry > exdata.ex_hdr.a_text + exdata.ex_hdr.a_data) + goto exec_fail; + + /* + * Step 3. File and header are valid. Now, dig out the strings + * out of the old process image. + */ + + /* + * We implement a single-pass algorithm that builds a new stack + * frame within the address space of the "old" process image, + * avoiding the second pass entirely. Thus, the new frame is + * in position to be run. This consumes much virtual address space, + * and two pages more of 'real' memory, such are the costs. + * [Also, note the cache wipe that's avoided!] + */ + + /* create anonymous memory region for new stack */ + vs = p->p_vmspace; + if ((unsigned)vs->vm_maxsaddr + MAXSSIZ < USRSTACK) + newframe = (caddr_t) USRSTACK - MAXSSIZ; + else + vs->vm_maxsaddr = newframe = (caddr_t) USRSTACK - 2*MAXSSIZ; + + /* don't do stack limit checking on traps temporarily XXX*/ + dostacklimits = 0; + + rv = vm_allocate(&vs->vm_map, &newframe, MAXSSIZ, FALSE); + if (rv) goto exec_fail; + + /* allocate string buffer and arg buffer */ + argbuf = (char **) (newframe + MAXSSIZ - 3*ARG_MAX); + stringbuf = stringbufp = ((char *)argbuf) + 2*ARG_MAX; + argbufp = argbuf; + + /* first, do args */ + vectp = uap->argp; + needsenv = 1; + limitonargs = ARG_MAX; + cnt = 0; + + /* first, do (shell name if any then) args */ + if (indir) { + ep = shellname; +thrice: + if (ep) { + /* did we outgrow initial argbuf, if so, die */ + if (argbufp >= (char **)stringbuf) { + rv = E2BIG; + goto exec_dealloc; + } + + if (rv = copyoutstr(ep, stringbufp, + (u_int)limitonargs, (u_int *)&stringlen)) { + if (rv == ENAMETOOLONG) + rv = E2BIG; + goto exec_dealloc; + } + suword(argbufp++, (int)stringbufp); + cnt++; + stringbufp += stringlen; + limitonargs -= stringlen; + } + + if (shellargs) { + ep = shellargs; + shellargs = 0; + goto thrice; + } + + if (indir) { + indir = 0; + /* orginal executable is 1st argument with scripts */ + ep = uap->fname; + goto thrice; + } + /* terminate in case no more args to script */ + suword(argbufp, 0); + if (vectp = uap->argp) vectp++; /* manually doing the first + argument with scripts */ + } + +do_env_as_well: + if(vectp == 0) goto dont_bother; + + /* for each envp, copy in string */ + do { + /* did we outgrow initial argbuf, if so, die */ + if (argbufp == (char **)stringbuf) { + rv = E2BIG; + goto exec_dealloc; + } + + /* get an string pointer */ + ep = (char *)fuword(vectp++); + if (ep == (char *)-1) { + rv = EFAULT; + goto exec_dealloc; + } + + /* if not a null pointer, copy string */ + if (ep) { + if (rv = copyinoutstr(ep, stringbufp, + (u_int)limitonargs, (u_int *) &stringlen)) { + if (rv == ENAMETOOLONG) + rv = E2BIG; + goto exec_dealloc; + } + suword(argbufp++, (int)stringbufp); + cnt++; + stringbufp += stringlen; + limitonargs -= stringlen; + } else { + suword(argbufp++, 0); + break; + } + } while (limitonargs > 0); + +dont_bother: + if (limitonargs <= 0) { + rv = E2BIG; + goto exec_dealloc; + } + + /* have we done the environment yet ? */ + if (needsenv) { + /* remember the arg count for later */ + argc = cnt; + vectp = uap->envp; + needsenv = 0; + goto do_env_as_well; + } + + /* At this point, one could optionally implement a + * second pass to condense the strings, arguement vectors, + * and stack to fit the fewest pages. + * + * One might selectively do this when copying was cheaper + * than leaving allocated two more pages per process. + */ + + /* stuff arg count on top of "new" stack */ + /* argbuf[-1] = (char *)argc;*/ + suword(argbuf-1,argc); + + /* + * Step 4. Build the new processes image. + * + * At this point, we are committed -- destroy old executable! + */ + + /* blow away all address space, except the stack */ + rv = vm_deallocate(&vs->vm_map, 0, USRSTACK - 2*MAXSSIZ); + if (rv) + goto exec_abort; + + /* destroy "old" stack */ + if ((unsigned)newframe < USRSTACK - MAXSSIZ) { + rv = vm_deallocate(&vs->vm_map, USRSTACK - MAXSSIZ, MAXSSIZ); + if (rv) + goto exec_abort; + } else { + rv = vm_deallocate(&vs->vm_map, USRSTACK - 2*MAXSSIZ, MAXSSIZ); + if (rv) + goto exec_abort; + } + + /* build a new address space */ + + + + /* treat text, data, and bss in terms of integral page size */ + tsize = roundup(exdata.ex_hdr.a_text, NBPG); + dsize = roundup(exdata.ex_hdr.a_data, NBPG); + bsize = roundup(exdata.ex_hdr.a_bss, NBPG); + + addr = virtual_offset; + + /* map text as being read/execute only and demand paged */ + rv = vm_mmap(&vs->vm_map, &addr, tsize, VM_PROT_READ|VM_PROT_EXECUTE, + VM_PROT_DEFAULT, MAP_FILE|MAP_PRIVATE|MAP_FIXED, + (caddr_t)ndp->ni_vp, file_offset); + if (rv) + goto exec_abort; + + addr = virtual_offset + tsize; + + /* map data as being read/write and demand paged */ + rv = vm_mmap(&vs->vm_map, &addr, dsize, + VM_PROT_READ | VM_PROT_WRITE | (tsize ? 0 : VM_PROT_EXECUTE), + VM_PROT_DEFAULT, MAP_FILE|MAP_PRIVATE|MAP_FIXED, + (caddr_t)ndp->ni_vp, file_offset + tsize); + if (rv) + goto exec_abort; + + /* create anonymous memory region for bss */ + addr = virtual_offset + tsize + dsize; + rv = vm_allocate(&vs->vm_map, &addr, bsize, FALSE); + if (rv) + goto exec_abort; + + /* + * Step 5. Prepare process for execution. + */ + + /* touchup process information -- vm system is unfinished! */ + vs->vm_tsize = tsize/NBPG; /* text size (pages) XXX */ + vs->vm_dsize = (dsize+bsize)/NBPG; /* data size (pages) XXX */ + vs->vm_taddr = (caddr_t) virtual_offset; /* virtual address of text */ + vs->vm_daddr = (caddr_t) virtual_offset + tsize; /* virtual address of data */ + vs->vm_maxsaddr = newframe; /* user VA at max stack growth XXX */ + vs->vm_ssize = ((unsigned)vs->vm_maxsaddr + MAXSSIZ + - (unsigned)argbuf)/ NBPG + 1; /* stack size (pages) */ + dostacklimits = 1; /* allow stack limits to be enforced XXX */ + + /* close files on exec, fixup signals */ + fdcloseexec(p); + execsigs(p); + + /* name this process - nameiexec(p, ndp) */ + len = MIN(ndp->ni_namelen,MAXCOMLEN); + bcopy(ndp->ni_ptr, p->p_comm, len); + p->p_comm[len] = 0; + + /* mark as executable, wakeup any process that was vforked and tell + * it that it now has it's own resources back */ + p->p_flag |= SEXEC; + if (p->p_pptr && (p->p_flag & SPPWAIT)) { + p->p_flag &= ~SPPWAIT; + wakeup(p->p_pptr); + } + + /* implement set userid/groupid */ + if ((attr.va_mode&VSUID) && (p->p_flag & STRC) == 0) { + p->p_ucred = crcopy(p->p_ucred); + p->p_cred->p_svuid = p->p_ucred->cr_uid = attr.va_uid; + } + if ((attr.va_mode&VSGID) && (p->p_flag & STRC) == 0) { + p->p_ucred = crcopy(p->p_ucred); + p->p_cred->p_svgid = p->p_ucred->cr_groups[0] = attr.va_gid; + } + + /* setup initial register state */ + p->p_regs[SP] = (unsigned) (argbuf - 1); + setregs(p, exdata.ex_hdr.a_entry); + + ndp->ni_vp->v_flag |= VTEXT; /* mark vnode pure text */ + + vput(ndp->ni_vp); + FREE(ndp->ni_pnbuf, M_NAMEI); + + /* if tracing process, pass control back to debugger so breakpoints + can be set before the program "runs" */ + if (p->p_flag & STRC) + psignal(p, SIGTRAP); + p->p_acflag &= ~AFORK; /* remove fork, but no exec flag */ + + return (0); + +exec_dealloc: + /* remove interim "new" stack frame we were building */ + vm_deallocate(&vs->vm_map, newframe, MAXSSIZ); + +exec_fail: + dostacklimits = 1; + vput(ndp->ni_vp); + FREE(ndp->ni_pnbuf, M_NAMEI); + + return(rv); + +exec_abort: + /* sorry, no more process anymore. exit gracefully */ + vm_deallocate(&vs->vm_map, newframe, MAXSSIZ); + vput(ndp->ni_vp); + FREE(ndp->ni_pnbuf, M_NAMEI); + kexit(p, W_EXITCODE(0, SIGABRT)); + + /* NOTREACHED */ + return(0); +} |
