Split sys_execve() and add execve1() that does most of the work, and takes

as an argument a function that will retrieve an element of the pointer
arrays in user space.  This allows COMPAT_NETBSD32 to share the code for
the emulated version of execve(2), and fixes various issues that came from
the slow drift between the two implementations.

Note:  when splitting up a syscall function, I'll use two different ways
       of naming the resulting helper function.  If it stills does
       copyin/out operations, it will be named <syscall>1().  If it does
       not (as it was the case for get/setitimer), it will be named
       do<syscall>.
This commit is contained in:
cube 2005-07-11 20:15:26 +00:00
parent 08fd92bac5
commit 355bcba766
3 changed files with 53 additions and 493 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: netbsd32_execve.c,v 1.22 2005/05/31 00:41:09 christos Exp $ */
/* $NetBSD: netbsd32_execve.c,v 1.23 2005/07/11 20:15:26 cube Exp $ */
/*
* Copyright (c) 1998, 2001 Matthew R. Green
@ -29,508 +29,48 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: netbsd32_execve.c,v 1.22 2005/05/31 00:41:09 christos Exp $");
#if defined(_KERNEL_OPT)
#include "opt_ktrace.h"
#endif
__KERNEL_RCSID(0, "$NetBSD: netbsd32_execve.c,v 1.23 2005/07/11 20:15:26 cube Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ktrace.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/namei.h>
#include <uvm/uvm_extern.h>
#include <sys/sa.h>
#include <sys/syscallargs.h>
#include <sys/proc.h>
#include <sys/acct.h>
#include <sys/exec.h>
#include <compat/netbsd32/netbsd32.h>
#include <compat/netbsd32/netbsd32_syscall.h>
#include <compat/netbsd32/netbsd32_syscallargs.h>
#include <sys/verified_exec.h>
static int
netbsd32_execve_fetch_element(char * const *array, size_t index, char **value)
{
int error;
netbsd32_charp const *a32 = (void const *)array;
netbsd32_charp e;
/* this is provided by kern/kern_exec.c */
extern u_int exec_maxhdrsz;
#if defined(LKM) || defined(_LKM)
extern struct lock exec_lock;
#endif
error = copyin(a32 + index, &e, sizeof(e));
if (error)
return error;
*value = (char *)NETBSD32PTR64(e);
return 0;
}
/*
* Need to completly reimplement this syscall due to argument copying.
*/
/* ARGSUSED */
int
netbsd32_execve(l, v, retval)
struct lwp *l;
void *v;
register_t *retval;
netbsd32_execve(struct lwp *l, void *v, register_t *retval)
{
struct netbsd32_execve_args /* {
syscallarg(const netbsd32_charp) path;
syscallarg(netbsd32_charpp) argp;
syscallarg(netbsd32_charpp) envp;
} */ *uap = v;
struct sys_execve_args ua;
caddr_t sg;
struct proc *p = l->l_proc;
NETBSD32TOP_UAP(path, const char);
NETBSD32TOP_UAP(argp, char *);
NETBSD32TOP_UAP(envp, char *);
sg = stackgap_init(p, 0);
CHECK_ALT_EXIST(p, &sg, SCARG(&ua, path));
return netbsd32_execve2(l, &ua, retval);
}
int
netbsd32_execve2(l, uap, retval)
struct lwp *l;
struct sys_execve_args *uap;
register_t *retval;
{
/* Function args */
struct proc *p = l->l_proc;
int error, i;
struct exec_package pack;
struct nameidata nid;
struct vattr attr;
struct ucred *cred = p->p_ucred;
char *argp;
netbsd32_charp const *cpp;
char *dp;
netbsd32_charp sp;
long argc, envc;
size_t len;
char *stack;
struct ps_strings arginfo;
struct vmspace *vm;
char **tmpfap;
int szsigcode;
struct exec_vmcmd *base_vcp = NULL;
/*
* Init the namei data to point the file user's program name.
* This is done here rather than in check_exec(), so that it's
* possible to override this settings if any of makecmd/probe
* functions call check_exec() recursively - for example,
* see exec_script_makecmds().
*/
NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
/*
* initialize the fields of the exec package.
*/
pack.ep_name = SCARG(uap, path);
pack.ep_hdr = malloc(exec_maxhdrsz, M_EXEC, M_WAITOK);
pack.ep_hdrlen = exec_maxhdrsz;
pack.ep_hdrvalid = 0;
pack.ep_ndp = &nid;
pack.ep_emul_arg = NULL;
pack.ep_vmcmds.evs_cnt = 0;
pack.ep_vmcmds.evs_used = 0;
pack.ep_vap = &attr;
pack.ep_flags = 0;
#if defined(LKM) || defined(_LKM)
lockmgr(&exec_lock, LK_SHARED, NULL);
#endif
/* see if we can run it. */
#ifdef VERIFIED_EXEC
if ((error = check_exec(p, &pack, VERIEXEC_DIRECT)) != 0)
#else
if ((error = check_exec(p, &pack)) != 0)
#endif
goto freehdr;
/* XXX -- THE FOLLOWING SECTION NEEDS MAJOR CLEANUP */
/* allocate an argument buffer */
argp = (char *) uvm_km_alloc(exec_map, NCARGS, 0,
UVM_KMF_PAGEABLE|UVM_KMF_WAITVA);
#ifdef DIAGNOSTIC
if (argp == (vaddr_t) 0)
panic("netbsd32_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 = (const netbsd32_charp *)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))) != 0)
goto bad;
if (!sp)
break;
if ((error = copyinstr((char *)(u_long)sp, dp,
len, &len)) != 0) {
if (error == ENAMETOOLONG)
error = E2BIG;
goto bad;
}
dp += len;
cpp++;
argc++;
}
envc = 0;
/* environment need not be there */
if ((cpp = (const netbsd32_charp *)SCARG(uap, envp)) != NULL ) {
while (1) {
len = argp + ARG_MAX - dp;
if ((error = copyin(cpp, &sp, sizeof(sp))) != 0)
goto bad;
if (!sp)
break;
if ((error = copyinstr((char *)(u_long)sp,
dp, len, &len)) != 0) {
if (error == ENAMETOOLONG)
error = E2BIG;
goto bad;
}
dp += len;
cpp++;
envc++;
}
}
dp = (char *) ALIGN(dp);
szsigcode = pack.ep_es->es_emul->e_esigcode -
pack.ep_es->es_emul->e_sigcode;
/* Now check if args & environ fit into new stack */
if (pack.ep_flags & EXEC_32)
len = ((argc + envc + 2 + pack.ep_es->es_arglen) *
sizeof(int) + sizeof(int) + dp + STACKGAPLEN +
szsigcode + sizeof(struct ps_strings)) - argp;
else
len = ((argc + envc + 2 + pack.ep_es->es_arglen) *
sizeof(char *) + sizeof(int) + dp + STACKGAPLEN +
szsigcode + sizeof(struct ps_strings)) - argp;
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... */
/*
* Do whatever is necessary to prepare the address space
* for remapping. Note that this might replace the current
* vmspace with another!
*/
uvmspace_exec(l, VM_MIN_ADDRESS, (vaddr_t)pack.ep_minsaddr);
/* Now map address space */
vm = p->p_vmspace;
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;
vm->vm_minsaddr = (char *) pack.ep_minsaddr;
/* create the new process's VM space by running the vmcmds */
#ifdef DIAGNOSTIC
if (pack.ep_vmcmds.evs_used == 0)
panic("netbsd32_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];
if (vcp->ev_flags & VMCMD_RELATIVE) {
#ifdef DIAGNOSTIC
if (base_vcp == NULL)
panic("netbsd32_execve: relative vmcmd with no base");
if (vcp->ev_flags & VMCMD_BASE)
panic("netbsd32_execve: illegal base & relative vmcmd");
#endif
vcp->ev_addr += base_vcp->ev_addr;
}
error = (*vcp->ev_proc)(p, vcp);
#ifdef DEBUG
if (error) {
int j;
for (j = 0; j <= i; j++)
printf("vmcmd[%d] = %#lx/%#lx @ %#lx\n", j,
vcp[j-i].ev_addr, vcp[j-i].ev_len,
vcp[j-i].ev_offset);
}
#endif
if (vcp->ev_flags & VMCMD_BASE)
base_vcp = vcp;
}
/* free the vmspace-creation commands, and release their references */
kill_vmcmds(&pack.ep_vmcmds);
/* if an error happened, deallocate and punt */
if (error) {
#ifdef DEBUG
printf("netbsd32_execve: vmcmd %i failed: %d\n", i-1, error);
#endif
goto exec_abort;
}
/* remember information about the process */
arginfo.ps_nargvstr = argc;
arginfo.ps_nenvstr = envc;
stack = (char *) (vm->vm_minsaddr - len);
/* Now copy argc, args & environ to new stack */
error = (*pack.ep_es->es_copyargs)(p, &pack, &arginfo,
&stack, argp);
if (error) {
#ifdef DEBUG
printf("netbsd32_execve: copyargs failed\n");
#endif
goto exec_abort;
}
/* restore the stack back to its original point */
stack = (char *) (vm->vm_minsaddr - len);
/* fill process ps_strings info */
p->p_psstr = (struct ps_strings *)(vm->vm_minsaddr -
sizeof(struct ps_strings));
p->p_psargv = offsetof(struct ps_strings, ps_argvstr);
p->p_psnargv = offsetof(struct ps_strings, ps_nargvstr);
p->p_psenv = offsetof(struct ps_strings, ps_envstr);
p->p_psnenv = offsetof(struct ps_strings, ps_nenvstr);
/* copy out the process's ps_strings structure */
if (copyout(&arginfo, (char *)p->p_psstr, sizeof(arginfo))) {
#ifdef DEBUG
printf("netbsd32_execve: ps_strings copyout failed\n");
#endif
goto exec_abort;
}
/* copy out the process's signal trapoline code */
if (szsigcode) {
if (copyout((char *)pack.ep_es->es_emul->e_sigcode,
p->p_sigctx.ps_sigcode = (char *)p->p_psstr - szsigcode,
szsigcode)) {
#ifdef DEBUG
printf("netbsd32_execve: sig trampoline copyout failed\n");
#endif
goto exec_abort;
}
#ifdef PMAP_NEED_PROCWR
/* This is code. Let the pmap do what is needed. */
pmap_procwr(p, (vaddr_t)p->p_sigctx.ps_sigcode, szsigcode);
#endif
}
stopprofclock(p); /* stop profiling */
fdcloseexec(p); /* handle close on exec */
execsigs(p); /* reset catched signals */
l->l_ctxlink = NULL; /* reset ucontext link */
/* set command name & other accounting info */
len = min(nid.ni_cnd.cn_namelen, MAXCOMLEN);
memcpy(p->p_comm, nid.ni_cnd.cn_nameptr, 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_NOSUID has already been used to disable s[ug]id.
*/
if ((p->p_flag & P_TRACED) == 0 &&
(((attr.va_mode & S_ISUID) != 0 &&
p->p_ucred->cr_uid != attr.va_uid) ||
((attr.va_mode & S_ISGID) != 0 &&
p->p_ucred->cr_gid != attr.va_gid))) {
/*
* Mark the process as SUGID before we do
* anything that might block.
*/
p_sugid(p);
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))
ktrderef(p);
#endif
if (attr.va_mode & S_ISUID)
p->p_ucred->cr_uid = attr.va_uid;
if (attr.va_mode & S_ISGID)
p->p_ucred->cr_gid = attr.va_gid;
} else
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;
doexechooks(p);
uvm_km_free(exec_map, (vaddr_t) argp, NCARGS, UVM_KMF_PAGEABLE);
PNBUF_PUT(nid.ni_cnd.cn_pnbuf);
vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY);
VOP_CLOSE(pack.ep_vp, FREAD, cred, p);
vput(pack.ep_vp);
/* setup new registers and do misc. setup. */
(*pack.ep_es->es_emul->e_setregs)(l, &pack, (u_long) stack);
if (pack.ep_es->es_setregs)
(*pack.ep_es->es_setregs)(l, &pack, (u_long) stack);
if (p->p_flag & P_TRACED)
psignal(p, SIGTRAP);
free(pack.ep_hdr, M_EXEC);
/*
* Call emulation specific exec hook. This can setup setup per-process
* p->p_emuldata or do any other per-process stuff an emulation needs.
*
* If we are executing process of different emulation than the
* original forked process, call e_proc_exit() of the old emulation
* first, then e_proc_exec() of new emulation. If the emulation is
* same, the exec hook code should deallocate any old emulation
* resources held previously by this process.
*/
if (p->p_emul && p->p_emul->e_proc_exit
&& p->p_emul != pack.ep_es->es_emul)
(*p->p_emul->e_proc_exit)(p);
/*
* Call exec hook. Emulation code may NOT store reference to anything
* from &pack.
*/
if (pack.ep_es->es_emul->e_proc_exec)
(*pack.ep_es->es_emul->e_proc_exec)(p, &pack);
/* update p_emul, the old value is no longer needed */
p->p_emul = pack.ep_es->es_emul;
/* ...and the same for p_execsw */
p->p_execsw = pack.ep_es;
#ifdef __HAVE_SYSCALL_INTERN
(*p->p_emul->e_syscall_intern)(p);
#endif
#ifdef KTRACE
if (KTRPOINT(p, KTR_EMUL))
ktremul(p);
#endif
#if defined(LKM) || defined(_LKM)
lockmgr(&exec_lock, LK_RELEASE, NULL);
#endif
return (EJUSTRETURN);
bad:
/* 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) fdrelease(p, pack.ep_fd);
}
/* close and put the exec'd file */
vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY);
VOP_CLOSE(pack.ep_vp, FREAD, cred, p);
vput(pack.ep_vp);
PNBUF_PUT(nid.ni_cnd.cn_pnbuf);
uvm_km_free(exec_map, (vaddr_t) argp, NCARGS, UVM_KMF_PAGEABLE);
freehdr:
#if defined(LKM) || defined(_LKM)
lockmgr(&exec_lock, LK_RELEASE, NULL);
#endif
free(pack.ep_hdr, M_EXEC);
return error;
exec_abort:
#if defined(LKM) || defined(_LKM)
lockmgr(&exec_lock, LK_RELEASE, NULL);
#endif
/*
* 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
*/
uvm_deallocate(&vm->vm_map, VM_MIN_ADDRESS,
VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS);
if (pack.ep_emul_arg)
FREE(pack.ep_emul_arg, M_TEMP);
PNBUF_PUT(nid.ni_cnd.cn_pnbuf);
vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY);
VOP_CLOSE(pack.ep_vp, FREAD, cred, p);
vput(pack.ep_vp);
uvm_km_free(exec_map, (vaddr_t) argp, NCARGS, UVM_KMF_PAGEABLE);
free(pack.ep_hdr, M_EXEC);
exit1(l, W_EXITCODE(error, SIGABRT));
/* NOTREACHED */
return 0;
return execve1(l, NETBSD32PTR64(SCARG(uap, path)),
NETBSD32PTR64(SCARG(uap, argp)), NETBSD32PTR64(SCARG(uap, envp)),
netbsd32_execve_fetch_element);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_exec.c,v 1.203 2005/07/10 04:20:34 christos Exp $ */
/* $NetBSD: kern_exec.c,v 1.204 2005/07/11 20:15:26 cube Exp $ */
/*-
* Copyright (C) 1993, 1994, 1996 Christopher G. Demetriou
@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.203 2005/07/10 04:20:34 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.204 2005/07/11 20:15:26 cube Exp $");
#include "opt_ktrace.h"
#include "opt_syscall_debug.h"
@ -363,6 +363,12 @@ bad1:
#define STACK_PTHREADSPACE 0
#endif
static int
execve_fetch_element(char * const *array, size_t index, char **value)
{
return copyin(array + index, value, sizeof(*value));
}
/*
* exec system call
*/
@ -375,6 +381,15 @@ sys_execve(struct lwp *l, void *v, register_t *retval)
syscallarg(char * const *) argp;
syscallarg(char * const *) envp;
} */ *uap = v;
return execve1(l, SCARG(uap, path), SCARG(uap, argp),
SCARG(uap, envp), execve_fetch_element);
}
int
execve1(struct lwp *l, const char *path, char * const *args,
char * const *envs, execve_fetch_element_t fetch_element)
{
int error;
u_int i;
struct exec_package pack;
@ -383,7 +398,6 @@ sys_execve(struct lwp *l, void *v, register_t *retval)
struct proc *p;
struct ucred *cred;
char *argp;
char * const *cpp;
char *dp, *sp;
long argc, envc;
size_t len;
@ -428,14 +442,14 @@ sys_execve(struct lwp *l, void *v, register_t *retval)
if (ISSET(p->p_flag, P_SYSTRACE))
systrace_execve0(p);
error = copyinstr(SCARG(uap, path), pathbuf, sizeof(pathbuf),
error = copyinstr(path, pathbuf, sizeof(pathbuf),
&pathbuflen);
if (error)
goto clrflg;
NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_SYSSPACE, pathbuf, p);
#else
NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_USERSPACE, path, p);
#endif /* SYSTRACE */
/*
@ -444,7 +458,7 @@ sys_execve(struct lwp *l, void *v, register_t *retval)
#ifdef SYSTRACE
pack.ep_name = pathbuf;
#else
pack.ep_name = SCARG(uap, path);
pack.ep_name = path;
#endif /* SYSTRACE */
pack.ep_hdr = malloc(exec_maxhdrsz, M_EXEC, M_WAITOK);
pack.ep_hdrlen = exec_maxhdrsz;
@ -499,17 +513,18 @@ sys_execve(struct lwp *l, void *v, register_t *retval)
}
/* Now get argv & environment */
if (!(cpp = SCARG(uap, argp))) {
if (args == NULL) {
error = EINVAL;
goto bad;
}
/* 'i' will index the argp/envp element to be retrieved */
i = 0;
if (pack.ep_flags & EXEC_SKIPARG)
cpp++;
i++;
while (1) {
len = argp + ARG_MAX - dp;
if ((error = copyin(cpp, &sp, sizeof(sp))) != 0)
if ((error = (*fetch_element)(args, i, &sp)) != 0)
goto bad;
if (!sp)
break;
@ -523,16 +538,17 @@ sys_execve(struct lwp *l, void *v, register_t *retval)
ktrkmem(p, KTR_EXEC_ARG, dp, len - 1);
#endif
dp += len;
cpp++;
i++;
argc++;
}
envc = 0;
/* environment need not be there */
if ((cpp = SCARG(uap, envp)) != NULL ) {
if (envs != NULL) {
i = 0;
while (1) {
len = argp + ARG_MAX - dp;
if ((error = copyin(cpp, &sp, sizeof(sp))) != 0)
if ((error = (*fetch_element)(envs, i, &sp)) != 0)
goto bad;
if (!sp)
break;
@ -546,7 +562,7 @@ sys_execve(struct lwp *l, void *v, register_t *retval)
ktrkmem(p, KTR_EXEC_ENV, dp, len - 1);
#endif
dp += len;
cpp++;
i++;
envc++;
}
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: exec.h,v 1.109 2005/06/10 11:36:38 tron Exp $ */
/* $NetBSD: exec.h,v 1.110 2005/07/11 20:15:26 cube Exp $ */
/*-
* Copyright (c) 1992, 1993
@ -284,6 +284,10 @@ void new_vmcmd(struct exec_vmcmd_set *,
#define NEW_VMCMD2(evsp,proc,len,addr,vp,offset,prot,flags) \
new_vmcmd(evsp,proc,len,addr,vp,offset,prot,flags)
typedef int (*execve_fetch_element_t)(char * const *, size_t, char **);
int execve1(struct lwp *, const char *, char * const *, char * const *,
execve_fetch_element_t);
#endif /* _KERNEL */
#include <sys/exec_aout.h>