NetBSD/sys/kern/kern_exec.c
fvdl 366ba9b889 Use sigcode fields in package structure. This seems to be the cleanest
way to deal with seperate trampoline code for emulation of other OSs,
it avoids having to clutter up kern_exec.c any further.
1995-04-07 22:33:23 +00:00

580 lines
15 KiB
C

/* $NetBSD: kern_exec.c,v 1.65 1995/04/07 22:33:23 fvdl Exp $ */
/*-
* Copyright (C) 1993, 1994 Christopher G. Demetriou
* Copyright (C) 1992 Wolfgang Solfrank.
* Copyright (C) 1992 TooLs GmbH.
* 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 product includes software developed by TooLs GmbH.
* 4. The name of TooLs GmbH may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/filedesc.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/acct.h>
#include <sys/exec.h>
#include <sys/ktrace.h>
#include <sys/resourcevar.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/signalvar.h>
#include <sys/stat.h>
#include <sys/syscallargs.h>
#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <machine/cpu.h>
#include <machine/reg.h>
#ifdef COPY_SIGCODE
extern char sigcode[], esigcode[];
#endif
/*
* check exec:
* given an "executable" described in the exec package's namei info,
* see what we can do with it.
*
* ON ENTRY:
* exec package with appropriate namei info
* proc pointer of exec'ing proc
* NO SELF-LOCKED VNODES
*
* ON EXIT:
* error: nothing held, etc. exec header still allocated.
* ok: filled exec package, one locked vnode.
*
* EXEC SWITCH ENTRY:
* Locked vnode to check, exec package, proc.
*
* EXEC SWITCH EXIT:
* ok: return 0, filled exec package, one locked vnode.
* error: destructive:
* everything deallocated execept exec header.
* non-descructive:
* error code, locked vnode, exec header unmodified
*/
int
check_exec(p, epp)
struct proc *p;
struct exec_package *epp;
{
int error, i;
struct vnode *vp;
char *cp, *ep, *name;
struct nameidata *ndp;
int resid;
ndp = epp->ep_ndp;
ndp->ni_cnd.cn_nameiop = LOOKUP;
ndp->ni_cnd.cn_flags = FOLLOW | LOCKLEAF | SAVENAME;
/* first get the vnode */
if (error = namei(ndp))
return error;
epp->ep_vp = vp = ndp->ni_vp;
/* check for regular file */
if (vp->v_type != VREG) {
error = EACCES;
goto bad1;
}
/* get attributes */
if (error = VOP_GETATTR(vp, epp->ep_vap, p->p_ucred, p))
goto bad1;
/* Check mount point */
if (vp->v_mount->mnt_flag & MNT_NOEXEC) {
error = EACCES;
goto bad1;
}
if ((vp->v_mount->mnt_flag & MNT_NOSUID) || (p->p_flag & P_TRACED))
epp->ep_vap->va_mode &= ~(VSUID | VSGID);
/* check access. for root we have to see if any exec bit on */
if (error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p))
goto bad1;
if ((epp->ep_vap->va_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) {
error = EACCES;
goto bad1;
}
/* try to open it */
if (error = VOP_OPEN(vp, FREAD, p->p_ucred, p))
goto bad1;
/* now we have the file, get the exec header */
if (error = vn_rdwr(UIO_READ, vp, epp->ep_hdr, epp->ep_hdrlen, 0,
UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p))
goto bad2;
epp->ep_hdrvalid = epp->ep_hdrlen - resid;
/*
* set up the vmcmds for creation of the process
* address space
*/
error = ENOEXEC;
for (i = 0; i < nexecs && error != 0; i++) {
if (execsw[i].es_check != NULL)
error = (*execsw[i].es_check)(p, epp);
if (epp->ep_flags & EXEC_DESTR && error != 0)
return error;
}
if (!error) {
/* check that entry point is sane */
if (epp->ep_entry > VM_MAXUSER_ADDRESS)
error = ENOEXEC;
/* check limits */
if ((epp->ep_tsize > MAXTSIZ) ||
(epp->ep_dsize > p->p_rlimit[RLIMIT_DATA].rlim_cur))
error = ENOMEM;
if (!error)
return (0);
}
/*
* free any vmspace-creation commands,
* and release their references
*/
kill_vmcmds(&epp->ep_vmcmds);
bad2:
/*
* unlock and close the vnode, restore the old one, free the
* pathname buf, and punt.
*/
VOP_UNLOCK(vp);
vn_close(vp, FREAD, p->p_ucred, p);
FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI);
return error;
bad1:
/*
* free the namei pathname buffer, and put the vnode
* (which we don't yet have open).
*/
FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI);
vput(vp);
return error;
}
/*
* exec system call
*/
/* ARGSUSED */
execve(p, uap, retval)
register struct proc *p;
register struct execve_args /* {
syscallarg(char *) path;
syscallarg(char * *) argp;
syscallarg(char * *) envp;
} */ *uap;
register_t *retval;
{
int error, i;
struct exec_package pack;
struct nameidata nid;
struct vattr attr;
struct ucred *cred = p->p_ucred;
char *argp;
char **cpp, *dp, *sp, *np;
long argc, envc;
size_t len;
char *stack;
struct ps_strings arginfo;
struct vmspace *vm = p->p_vmspace;
char **tmpfap;
int szsigcode;
/*
* figure out the maximum size of an exec header, if necessary.
* XXX should be able to keep LKM code from modifying exec switch
* when we're still using it, but...
*/
if (exec_maxhdrsz == 0) {
for (i = 0; i < nexecs; i++)
if (execsw[i].es_check != NULL
&& execsw[i].es_hdrsz > exec_maxhdrsz)
exec_maxhdrsz = execsw[i].es_hdrsz;
}
/* init the namei data to point the file user's program name */
NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
/*
* initialize the fields of the exec package.
*/
pack.ep_name = SCARG(uap, path);
MALLOC(pack.ep_hdr, void *, exec_maxhdrsz, M_EXEC, M_WAITOK);
pack.ep_hdrlen = exec_maxhdrsz;
pack.ep_hdrvalid = 0;
pack.ep_ndp = &nid;
pack.ep_setup = NULL; /* assume no setup function */
pack.ep_setup_arg = NULL;
pack.ep_setup_arglen = 0;
pack.ep_vmcmds.evs_cnt = 0;
pack.ep_vmcmds.evs_used = 0;
pack.ep_vap = &attr;
pack.ep_emul = EMUL_NETBSD;
pack.ep_flags = 0;
#ifdef COPY_SIGCODE
pack.ep_sigcode = sigcode;
pack.ep_esigcode = esigcode;
#endif
/* see if we can run it. */
if (error = check_exec(p, &pack))
goto freehdr;
/* XXX -- THE FOLLOWING SECTION NEEDS MAJOR CLEANUP */
/* allocate an argument buffer */
argp = (char *) kmem_alloc_wait(exec_map, NCARGS);
#ifdef DIAGNOSTIC
if (argp == (vm_offset_t) 0)
panic("execve: argp == NULL");
#endif
dp = argp;
argc = 0;
/* copy the fake args list, if there's one, freeing it as we go */
if (pack.ep_flags & EXEC_HASARGL) {
tmpfap = pack.ep_fa;
while (*tmpfap != NULL) {
char *cp;
cp = *tmpfap;
while (*cp)
*dp++ = *cp++;
*dp++;
FREE(*tmpfap, M_EXEC);
tmpfap++; argc++;
}
FREE(pack.ep_fa, M_EXEC);
pack.ep_flags &= ~EXEC_HASARGL;
}
/* Now get argv & environment */
if (!(cpp = SCARG(uap, argp))) {
error = EINVAL;
goto bad;
}
if (pack.ep_flags & EXEC_SKIPARG)
cpp++;
while (1) {
len = argp + ARG_MAX - dp;
if (error = copyin(cpp, &sp, sizeof(sp)))
goto bad;
if (!sp)
break;
if (error = copyinstr(sp, dp, len, &len)) {
if (error == ENAMETOOLONG)
error = E2BIG;
goto bad;
}
dp += len;
cpp++;
argc++;
}
envc = 0;
if (cpp = SCARG(uap, envp)) { /* environment need not be there */
while (1) {
len = argp + ARG_MAX - dp;
if (error = copyin(cpp, &sp, sizeof(sp)))
goto bad;
if (!sp)
break;
if (error = copyinstr(sp, dp, len, &len)) {
if (error == ENAMETOOLONG)
error = E2BIG;
goto bad;
}
dp += len;
cpp++;
envc++;
}
}
dp = (char *) ALIGN(dp);
#ifdef COPY_SIGCODE
szsigcode = pack.ep_esigcode - pack.ep_sigcode;
#else
szsigcode = 0;
#endif
/* Now check if args & environ fit into new stack */
len = ((argc + envc + 2 + pack.ep_setup_arglen) * sizeof(char *) +
sizeof(long) + dp + STACKGAPLEN + szsigcode +
sizeof(struct ps_strings)) - argp;
#ifdef COMPAT_LINUX
/* XXXX need this for envp and argv on stack, and sigcode */
if (pack.ep_emul == EMUL_LINUX)
len += 2 * sizeof (char *);
#endif
len = ALIGN(len); /* make the stack "safely" aligned */
if (len > pack.ep_ssize) { /* in effect, compare to initial limit */
error = ENOMEM;
goto bad;
}
/* adjust "active stack depth" for process VSZ */
pack.ep_ssize = len; /* maybe should go elsewhere, but... */
/* Unmap old program */
#ifdef sparc
kill_user_windows(p); /* before stack addresses go away */
#endif
/* Kill shared memory and unmap old program */
#ifdef SYSVSHM
if (vm->vm_shm)
shmexit(p);
#endif
vm_deallocate(&vm->vm_map, VM_MIN_ADDRESS,
VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS);
/* Now map address space */
vm->vm_taddr = (char *) pack.ep_taddr;
vm->vm_tsize = btoc(pack.ep_tsize);
vm->vm_daddr = (char *) pack.ep_daddr;
vm->vm_dsize = btoc(pack.ep_dsize);
vm->vm_ssize = btoc(pack.ep_ssize);
vm->vm_maxsaddr = (char *) pack.ep_maxsaddr;
/* create the new process's VM space by running the vmcmds */
#ifdef DIAGNOSTIC
if (pack.ep_vmcmds.evs_used == 0)
panic("execve: no vmcmds");
#endif
for (i = 0; i < pack.ep_vmcmds.evs_used && !error; i++) {
struct exec_vmcmd *vcp;
vcp = &pack.ep_vmcmds.evs_cmds[i];
error = (*vcp->ev_proc)(p, vcp);
}
/* free the vmspace-creation commands, and release their references */
kill_vmcmds(&pack.ep_vmcmds);
/* if an error happened, deallocate and punt */
if (error)
goto exec_abort;
/* remember information about the process */
arginfo.ps_nargvstr = argc;
arginfo.ps_nenvstr = envc;
/* Now copy argc, args & environ to new stack */
stack = (char *) (USRSTACK - len);
cpp = (char **) stack;
if (copyout(&argc, cpp++, sizeof(argc)))
goto exec_abort;
#ifdef COMPAT_LINUX
/* XXXX Linux puts argv and envp on stack too, store argv now */
if (pack.ep_emul == EMUL_LINUX) {
char **argv_loc = cpp + 2, **stk = (char **) stack;
if (copyout(&argv_loc, &stk[1], sizeof (argv_loc)))
goto exec_abort;
/* leave room for envp and argv */
cpp += 2;
}
#endif
dp = (char *) (cpp + argc + envc + 2 + pack.ep_setup_arglen);
sp = argp;
np = 0;
/* XXX don't copy them out, remap them! */
arginfo.ps_argvstr = dp; /* remember location of argv for later */
for (; --argc >= 0; sp += len, dp += len) {
if (copyout(&dp, cpp++, sizeof(dp)) ||
copyoutstr(sp, dp, ARG_MAX, &len))
goto exec_abort;
}
if (copyout(&np, cpp++, sizeof(np)))
goto exec_abort;
#ifdef COMPAT_LINUX
/* XXXX Linux puts argv and envp on stack too, store envp now */
if (pack.ep_emul == EMUL_LINUX) {
char **envp_loc = cpp, **stk = (char **) stack;
if (copyout(&envp_loc, &stk[2], sizeof (envp_loc)))
goto exec_abort;
}
#endif
arginfo.ps_envstr = dp; /* remember location of envp for later */
for (; --envc >= 0; sp += len, dp += len) {
if (copyout(&dp, cpp++, sizeof(dp)) ||
copyoutstr(sp, dp, ARG_MAX, &len))
goto exec_abort;
}
if (copyout(&np, cpp++, sizeof(np)))
goto exec_abort;
if (pack.ep_setup != NULL)
(*pack.ep_setup)(EXEC_SETUP_ADDARGS, p, &pack, cpp);
/* copy out the process's ps_strings structure */
if (copyout(&arginfo, (char *) PS_STRINGS, sizeof(arginfo)))
goto exec_abort;
#ifdef COPY_SIGCODE
/* copy out the process's signal trapoline code */
if (copyout((char *) pack.ep_sigcode, ((char *) PS_STRINGS) - szsigcode,
szsigcode)) {
goto exec_abort;
}
#endif
fdcloseexec(p); /* handle close on exec */
execsigs(p); /* reset catched signals */
/* set command name & other accounting info */
len = min(nid.ni_cnd.cn_namelen, MAXCOMLEN);
bcopy(nid.ni_cnd.cn_nameptr, p->p_comm, len);
p->p_comm[len] = 0;
p->p_acflag &= ~AFORK;
/* record proc's vnode, for use by procfs and others */
if (p->p_textvp)
vrele(p->p_textvp);
VREF(pack.ep_vp);
p->p_textvp = pack.ep_vp;
p->p_flag |= P_EXEC;
if (p->p_flag & P_PPWAIT) {
p->p_flag &= ~P_PPWAIT;
wakeup((caddr_t) p->p_pptr);
}
/*
* deal with set[ug]id.
* MNT_NOEXEC and P_TRACED have already been used to disable s[ug]id.
*/
p->p_flag &= ~P_SUGID;
if (((attr.va_mode & VSUID) != 0 &&
p->p_ucred->cr_uid != attr.va_uid)
|| (attr.va_mode & VSGID) != 0 &&
p->p_ucred->cr_gid != attr.va_gid) {
p->p_ucred = crcopy(cred);
#ifdef KTRACE
/*
* If process is being ktraced, turn off - unless
* root set it.
*/
if (p->p_tracep && !(p->p_traceflag & KTRFAC_ROOT)) {
vrele(p->p_tracep);
p->p_tracep = NULL;
p->p_traceflag = 0;
}
#endif
if (attr.va_mode & VSUID)
p->p_ucred->cr_uid = attr.va_uid;
if (attr.va_mode & VSGID)
p->p_ucred->cr_gid = attr.va_gid;
p->p_flag |= P_SUGID;
}
p->p_cred->p_svuid = p->p_ucred->cr_uid;
p->p_cred->p_svgid = p->p_ucred->cr_gid;
kmem_free_wakeup(exec_map, (vm_offset_t) argp, NCARGS);
FREE(nid.ni_cnd.cn_pnbuf, M_NAMEI);
VOP_CLOSE(pack.ep_vp, FREAD, cred, p);
vput(pack.ep_vp);
/* setup new registers and do misc. setup. */
setregs(p, pack.ep_entry, (u_long) stack, retval);
if (pack.ep_setup != NULL)
(*pack.ep_setup)(EXEC_SETUP_FINISH, p, &pack, NULL);
if (p->p_flag & P_TRACED)
psignal(p, SIGTRAP);
p->p_emul = pack.ep_emul;
FREE(pack.ep_hdr, M_EXEC);
return 0;
bad:
if (pack.ep_setup != NULL)
(*pack.ep_setup)(EXEC_SETUP_CLEANUP, p, &pack, dp);
/* free the vmspace-creation commands, and release their references */
kill_vmcmds(&pack.ep_vmcmds);
/* kill any opened file descriptor, if necessary */
if (pack.ep_flags & EXEC_HASFD) {
pack.ep_flags &= ~EXEC_HASFD;
(void) fdclose(p, pack.ep_fd);
}
/* close and put the exec'd file */
VOP_CLOSE(pack.ep_vp, FREAD, cred, p);
vput(pack.ep_vp);
FREE(nid.ni_cnd.cn_pnbuf, M_NAMEI);
kmem_free_wakeup(exec_map, (vm_offset_t) argp, NCARGS);
freehdr:
FREE(pack.ep_hdr, M_EXEC);
return error;
exec_abort:
/*
* the old process doesn't exist anymore. exit gracefully.
* get rid of the (new) address space we have created, if any, get rid
* of our namei data and vnode, and exit noting failure
*/
vm_deallocate(&vm->vm_map, VM_MIN_ADDRESS,
VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS);
FREE(nid.ni_cnd.cn_pnbuf, M_NAMEI);
VOP_CLOSE(pack.ep_vp, FREAD, cred, p);
vput(pack.ep_vp);
kmem_free_wakeup(exec_map, (vm_offset_t) argp, NCARGS);
FREE(pack.ep_hdr, M_EXEC);
exit1(p, W_EXITCODE(0, SIGABRT));
exit1(p, -1);
/* NOTREACHED */
return 0;
}