diff options
| author | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 |
|---|---|---|
| committer | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 |
| commit | a16f65c7d117419bd266c28a1901ef129a337569 (patch) | |
| tree | 2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /sys/kern/kern_execve.c | |
| parent | 8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff) | |
Diffstat (limited to 'sys/kern/kern_execve.c')
| -rw-r--r-- | sys/kern/kern_execve.c | 846 |
1 files changed, 418 insertions, 428 deletions
diff --git a/sys/kern/kern_execve.c b/sys/kern/kern_execve.c index 003b2f768b07..5e0aa6565d3d 100644 --- a/sys/kern/kern_execve.c +++ b/sys/kern/kern_execve.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1989, 1990, 1991, 1992 William F. Jolitz, TeleMuse + * Copyright (c) 1993, David Greenman * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -12,31 +12,14 @@ * 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 product includes software developed by David Greenman + * 4. The name of the developer 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 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS 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) @@ -45,22 +28,16 @@ * 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 $ + * $Id: kern_execve.c,v 1.15.2.2 1994/03/24 08:57:16 rgrimes Exp $ */ #include "param.h" #include "systm.h" #include "signalvar.h" #include "resourcevar.h" -#include "proc.h" +#include "imgact.h" +#include "kernel.h" #include "mount.h" -#include "namei.h" -#include "vnode.h" #include "file.h" #include "acct.h" #include "exec.h" @@ -68,492 +45,505 @@ #include "wait.h" #include "mman.h" #include "malloc.h" +#include "syslog.h" #include "vm/vm.h" #include "vm/vm_param.h" #include "vm/vm_map.h" #include "vm/vm_kern.h" +#include "vm/vm_user.h" #include "machine/reg.h" -extern int dostacklimits; -#define copyinoutstr copyinstr +int exec_extract_strings __P((struct image_params *)); +int *exec_copyout_strings __P((struct image_params *)); /* - * execve() system call. + * execsw_set is constructed for us by the linker. Each of the items + * is a pointer to a `const struct execsw', hence the double pointer here. */ +extern const struct linker_set execsw_set; +const struct execsw **execsw = (const struct execsw **)&execsw_set.ls_items[0]; -struct execve_args { - char *fname; - char **argp; - char **envp; -}; - -/* ARGSUSED */ +/* + * execve() system call. + */ +int 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 nameidata nd, *ndp; + char *stringbase, *stringp; + int *stack_base; + int error, resid, len, i; +#if 0 + char image_header[256]; +#endif + struct image_params image_params, *iparams; + struct vnode *vnodep; 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; + char *image_header; + + iparams = &image_params; + bzero((caddr_t)iparams, sizeof(struct image_params)); + image_header = (char *)0; + + /* + * Initialize a few constants in the common area + */ + iparams->proc = p; + iparams->uap = uap; + iparams->attr = &attr; /* - * Step 1. Lookup filename to see if we have something to execute. + * Allocate temporary demand zeroed space for argument and + * environment strings + */ + error = vm_allocate(kernel_map, (vm_offset_t *)&iparams->stringbase, + ARG_MAX, TRUE); + if (error) { + log(LOG_WARNING, "execve: failed to allocate string space\n"); + return (error); + } + + if (!iparams->stringbase) { + error = ENOMEM; + goto exec_fail; + } + iparams->stringp = iparams->stringbase; + iparams->stringspace = ARG_MAX; + + /* + * Translate the file name. namei() returns a vnode pointer + * in ni_vp amoung other things. */ ndp = &nd; + ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW | SAVENAME; 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); +interpret: - if (ndp->ni_vp->v_writecount) { /* don't exec if file is busy */ - rv = EBUSY; - goto exec_fail; + error = namei(ndp, p); + if (error) { + vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase, + ARG_MAX); + 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; - } + iparams->vnodep = vnodep = ndp->ni_vp; - /* 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; + if (vnodep == NULL) { + error = ENOEXEC; + goto exec_fail_dealloc; } /* - * 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. + * Check file permissions (also 'opens' file) */ - 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); + error = exec_check_permissions(iparams); + if (error) + goto exec_fail_dealloc; - /* 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; +#if 0 + /* + * Read the image header from the file. + */ + error = vn_rdwr(UIO_READ, + vnodep, + image_header, + sizeof(image_header), + 0, + UIO_SYSSPACE, IO_NODELOCKED, + p->p_ucred, + &resid, + p); + if (error) + goto exec_fail_dealloc; + + /* Clear out junk in image_header if a partial read (small file) */ + if (resid) + bzero(image_header + (sizeof(image_header) - resid), resid); +#endif + /* + * Map the image header (first page) of the file into + * kernel address space + */ + error = vm_mmap(kernel_map, /* map */ + (vm_offset_t *)&image_header, /* address */ + NBPG, /* size */ + VM_PROT_READ, /* protection */ + VM_PROT_READ, /* max protection */ + MAP_FILE, /* flags */ + (caddr_t)vnodep, /* vnode */ + 0); /* offset */ + if (error) { + uprintf("mmap failed: %d\n",error); + goto exec_fail_dealloc; } - -#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 */ + iparams->image_header = image_header; + + /* + * Loop through list of image activators, calling each one. + * If there is no match, the activator returns -1. If there + * is a match, but there was an error during the activation, + * the error is returned. Otherwise 0 means success. If the + * image is interpreted, loop back up and try activating + * the interpreter. + */ + for (i = 0; execsw[i]; ++i) { + if (execsw[i]->ex_imgact) + error = (*execsw[i]->ex_imgact)(iparams); + else + continue; + + if (error == -1) + continue; + if (error) + goto exec_fail_dealloc; + if (iparams->interpreted) { + /* free old vnode and name buffer */ vput(ndp->ni_vp); FREE(ndp->ni_pnbuf, M_NAMEI); + if (vm_deallocate(kernel_map, + (vm_offset_t)image_header, NBPG)) + panic("execve: header dealloc failed (1)"); - ndp->ni_dirp = shellname; /* find shell interpreter */ + /* set new name to that of the interpreter */ ndp->ni_segflg = UIO_SYSSPACE; - goto again; + ndp->ni_dirp = iparams->interpreter_name; + ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW | SAVENAME; + goto interpret; } - /* NOT REACHED */ + break; + } + /* If we made it through all the activators and none matched, exit. */ + if (error == -1) { + error = ENOEXEC; + goto exec_fail_dealloc; } - /* 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; + /* + * Copy out strings (args and env) and initialize stack base + */ + stack_base = exec_copyout_strings(iparams); + p->p_vmspace->vm_minsaddr = (char *)stack_base; - 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; + /* + * Stuff argument count as first item on stack + */ + *(--stack_base) = iparams->argc; - if (exdata.ex_hdr.a_data + exdata.ex_hdr.a_bss > p->p_rlimit[RLIMIT_DATA].rlim_cur) - goto exec_fail; + /* close files on exec, fixup signals */ + fdcloseexec(p); + execsigs(p); - if (exdata.ex_hdr.a_entry > exdata.ex_hdr.a_text + exdata.ex_hdr.a_data) - goto exec_fail; + /* 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; /* - * Step 3. File and header are valid. Now, dig out the strings - * out of the old process image. + * 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((caddr_t)p->p_pptr); + } + + /* implement set userid/groupid */ + p->p_flag &= ~SUGID; /* - * 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!] + * Turn off kernel tracing for set-id programs, except for + * root. */ - - /* 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 */ + if (p->p_tracep && (attr.va_mode & (VSUID | VSGID)) && + suser(p->p_ucred, &p->p_acflag)) { + p->p_traceflag = 0; + vrele(p->p_tracep); + p->p_tracep = 0; + } + if ((attr.va_mode&VSUID) && (p->p_flag & STRC) == 0) { + p->p_ucred = crcopy(p->p_ucred); + p->p_ucred->cr_uid = attr.va_uid; + p->p_flag |= SUGID; + } + if ((attr.va_mode&VSGID) && (p->p_flag & STRC) == 0) { + p->p_ucred = crcopy(p->p_ucred); + p->p_ucred->cr_groups[0] = attr.va_gid; + p->p_flag |= SUGID; } -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); + /* + * Implement correct POSIX saved uid behavior. + */ + p->p_cred->p_svuid = p->p_ucred->cr_uid; + p->p_cred->p_svgid = p->p_ucred->cr_gid; -dont_bother: - if (limitonargs <= 0) { - rv = E2BIG; - goto exec_dealloc; - } + /* mark vnode pure text */ + ndp->ni_vp->v_flag |= VTEXT; - /* 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. + /* + * If tracing the process, trap to debugger so breakpoints + * can be set before the program executes. */ + if (p->p_flag & STRC) + psignal(p, SIGTRAP); - /* stuff arg count on top of "new" stack */ - /* argbuf[-1] = (char *)argc;*/ - suword(argbuf-1,argc); + /* clear "fork but no exec" flag, as we _are_ execing */ + p->p_acflag &= ~AFORK; + + /* Set entry address */ + setregs(p, iparams->entry_addr, stack_base); /* - * Step 4. Build the new processes image. - * - * At this point, we are committed -- destroy old executable! + * free various allocated resources */ + if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase, + ARG_MAX)) + panic("execve: string buffer dealloc failed (1)"); + if (vm_deallocate(kernel_map, (vm_offset_t)image_header, NBPG)) + panic("execve: header dealloc failed (2)"); + vput(ndp->ni_vp); + FREE(ndp->ni_pnbuf, M_NAMEI); - /* blow away all address space, except the stack */ - rv = vm_deallocate(&vs->vm_map, 0, USRSTACK - 2*MAXSSIZ); - if (rv) - goto exec_abort; + return (0); - /* destroy "old" stack */ - if ((unsigned)newframe < USRSTACK - MAXSSIZ) { - rv = vm_deallocate(&vs->vm_map, USRSTACK - MAXSSIZ, MAXSSIZ); - if (rv) - goto exec_abort; +exec_fail_dealloc: + if (iparams->stringbase && iparams->stringbase != (char *)-1) + if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase, + ARG_MAX)) + panic("execve: string buffer dealloc failed (2)"); + if (iparams->image_header && iparams->image_header != (char *)-1) + if (vm_deallocate(kernel_map, + (vm_offset_t)iparams->image_header, NBPG)) + panic("execve: header dealloc failed (3)"); + vput(ndp->ni_vp); + FREE(ndp->ni_pnbuf, M_NAMEI); + +exec_fail: + if (iparams->vmspace_destroyed) { + /* sorry, no more process anymore. exit gracefully */ +#if 0 /* XXX */ + vm_deallocate(&vs->vm_map, USRSTACK - MAXSSIZ, MAXSSIZ); +#endif + kexit(p, W_EXITCODE(0, SIGABRT)); + /* NOT REACHED */ + return(0); } else { - rv = vm_deallocate(&vs->vm_map, USRSTACK - 2*MAXSSIZ, MAXSSIZ); - if (rv) - goto exec_abort; + return(error); } +} - /* build a new address space */ +/* + * Destroy old address space, and allocate a new stack + * The new stack is only SGROWSIZ large because it is grown + * automatically in trap.c. + */ +int +exec_new_vmspace(iparams) + struct image_params *iparams; +{ + int error; + struct vmspace *vmspace = iparams->proc->p_vmspace; + caddr_t stack_addr = (caddr_t) (USRSTACK - SGROWSIZ); + iparams->vmspace_destroyed = 1; + /* Blow away entire process VM */ + vm_deallocate(&vmspace->vm_map, 0, USRSTACK); - /* 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); + /* Allocate a new stack */ + error = vm_allocate(&vmspace->vm_map, (vm_offset_t *)&stack_addr, + SGROWSIZ, FALSE); + if (error) + return(error); - addr = virtual_offset; + vmspace->vm_ssize = SGROWSIZ >> PAGE_SHIFT; - /* 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; + /* Initialize maximum stack address */ + vmspace->vm_maxsaddr = (char *)USRSTACK - MAXSSIZ; - addr = virtual_offset + tsize; + return(0); +} - /* 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; +/* + * Copy out argument and environment strings from the old process + * address space into the temporary string buffer. + */ +int +exec_extract_strings(iparams) + struct image_params *iparams; +{ + char **argv, **envv; + char *argp, *envp; + int length; - /* 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; + /* + * extract arguments first + */ + + argv = iparams->uap->argv; + + if (argv) + while (argp = (caddr_t) fuword(argv++)) { + if (argp == (caddr_t) -1) + return (EFAULT); + if (copyinstr(argp, iparams->stringp, iparams->stringspace, + &length) == ENAMETOOLONG) + return(E2BIG); + iparams->stringspace -= length; + iparams->stringp += length; + iparams->argc++; + } /* - * Step 5. Prepare process for execution. + * extract environment strings */ - /* 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 */ + envv = iparams->uap->envv; + + if (envv) + while (envp = (caddr_t) fuword(envv++)) { + if (envp == (caddr_t) -1) + return (EFAULT); + if (copyinstr(envp, iparams->stringp, iparams->stringspace, + &length) == ENAMETOOLONG) + return(E2BIG); + iparams->stringspace -= length; + iparams->stringp += length; + iparams->envc++; + } - /* close files on exec, fixup signals */ - fdcloseexec(p); - execsigs(p); + return (0); +} - /* 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; +/* + * Copy strings out to the new process address space, constructing + * new arg and env vector tables. Return a pointer to the base + * so that it can be used as the initial stack pointer. + */ +int * +exec_copyout_strings(iparams) + struct image_params *iparams; +{ + int argc, envc; + char **vectp; + char *stringp, *destp; + int *stack_base; + int vect_table_size, string_table_size; + + /* + * Calculate string base and vector table pointers. + */ + destp = (caddr_t) ((caddr_t)USRSTACK - + roundup((ARG_MAX - iparams->stringspace), sizeof(char *))); + /* + * The '+ 2' is for the null pointers at the end of each of the + * arg and env vector sets + */ + vectp = (char **) (destp - + (iparams->argc + iparams->envc + 2) * sizeof(char *)); + + /* + * vectp also becomes our initial stack base + */ + stack_base = (int *)vectp; + + stringp = iparams->stringbase; + argc = iparams->argc; + envc = iparams->envc; + + for (; argc > 0; --argc) { + *(vectp++) = destp; + while (*destp++ = *stringp++); } - 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; + + /* a null vector table pointer seperates the argp's from the envp's */ + *(vectp++) = NULL; + + for (; envc > 0; --envc) { + *(vectp++) = destp; + while (*destp++ = *stringp++); } - /* setup initial register state */ - p->p_regs[SP] = (unsigned) (argbuf - 1); - setregs(p, exdata.ex_hdr.a_entry); + /* end of vector table is a null pointer */ + *vectp = NULL; - ndp->ni_vp->v_flag |= VTEXT; /* mark vnode pure text */ + return (stack_base); +} - vput(ndp->ni_vp); - FREE(ndp->ni_pnbuf, M_NAMEI); +/* + * Check permissions of file to execute. + * Return 0 for success or error code on failure. + */ +int +exec_check_permissions(iparams) + struct image_params *iparams; +{ + struct proc *p = iparams->proc; + struct vnode *vnodep = iparams->vnodep; + struct vattr *attr = iparams->attr; + int error; - /* 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 */ + /* + * Check number of open-for-writes on the file and deny execution + * if there are any. + */ + if (vnodep->v_writecount) { + return (ETXTBSY); + } - return (0); + /* Get file attributes */ + error = VOP_GETATTR(vnodep, attr, p->p_ucred, p); + if (error) + return (error); -exec_dealloc: - /* remove interim "new" stack frame we were building */ - vm_deallocate(&vs->vm_map, newframe, MAXSSIZ); + /* + * 1) Check if file execution is disabled for the filesystem that this + * file resides on. + * 2) Insure that at least one execute bit is on - otherwise root + * will always succeed, and we don't want to happen unless the + * file really is executable. + * 3) Insure that the file is a regular file. + */ + if ((vnodep->v_mount->mnt_flag & MNT_NOEXEC) || + ((attr->va_mode & 0111) == 0) || + (attr->va_type != VREG)) { + return (EACCES); + } -exec_fail: - dostacklimits = 1; - vput(ndp->ni_vp); - FREE(ndp->ni_pnbuf, M_NAMEI); + /* + * Zero length files can't be exec'd + */ + if (attr->va_size == 0) + return (ENOEXEC); - return(rv); + /* + * Disable setuid/setgid if the filesystem prohibits it or if + * the process is being traced. + */ + if ((vnodep->v_mount->mnt_flag & MNT_NOSUID) || (p->p_flag & STRC)) + attr->va_mode &= ~(VSUID | VSGID); -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)); + /* + * Check for execute permission to file based on current credentials. + * Then call filesystem specific open routine (which does nothing + * in the general case). + */ + error = VOP_ACCESS(vnodep, VEXEC, p->p_ucred, p); + if (error) + return (error); - /* NOTREACHED */ - return(0); + error = VOP_OPEN(vnodep, FREAD, p->p_ucred, p); + if (error) + return (error); + + return (0); } |
