Hi,
The following diff tries to simplify a bit the entanglement of namei
data and execve(2) syscall.
Currently, when called, sys_execve() might doing recursive calls of
check_exec() with a shared `struct nameidata' (`ep_ndp' inside
`struct exec_package').
I would like to disassociate them, and make check_exec() to "own" the
`struct nameidata' data. execve(2) is complex enough, no needs to adds
namei() complexity inside.
check_exec() will initialize nameidata, call namei() (as now), extract
useful information for the caller (only the vnode and the
command-name, returned via exec_package struct), and free other namei
ressources.
To proceed, it only needs to know the wanted path (to properly init
`nd' with NDINIT).
As the call of check_exec() could be recursive (when scripts are
involved), the path could come from:
- directly from sys_execve() via SCARG(uap, path) (from userspace)
- from exec_script_makecmds() via the shellname (from systemspace)
In `struct exec_package', I opted to reuse `ep_name' for passing the
path: it is what sys_execve() is already doing at first call. Later it
is only used for construct the fake-args list in
exec_script_makecmds(), and it could being overrided after that (and
restored on error). I reordered them a bit to make it fit.
Eventually I could also introduce a new struct field for the wanted
path.
`ep_segflg' is introduced to mark if ep_name comes from UIO_USERSPACE
or UIO_SYSSPACE. it will be used by NDINIT() in check_exec().
`ep_comm' is the other information (with ep_vp) wanted in result of
check_exec() call. it will be copied to `ps_comm'.
Comments or OK ?
--
Sebastien Marie
diff 8de782433999755fc9356c8ed8dc7b327e532351 /home/semarie/repos/openbsd/src
blob - a31fa5a3269a3b568450066a1bd001317d710354
file + sys/kern/exec_script.c
--- sys/kern/exec_script.c
+++ sys/kern/exec_script.c
@@ -64,19 +64,24 @@ exec_script_makecmds(struct proc *p, struct exec_packa
{
int error, hdrlinelen, shellnamelen, shellarglen;
char *hdrstr = epp->ep_hdr;
- char *cp, *shellname, *shellarg, *oldpnbuf;
+ char *cp, *shellname, *shellarg;
char **shellargp = NULL, **tmpsap;
struct vnode *scriptvp;
+ char old_comm[MAXCOMLEN+1];
+ char *old_name;
+ enum uio_seg old_segflg;
uid_t script_uid = -1;
gid_t script_gid = -1;
u_short script_sbits;
/*
- * remember the old vp and pnbuf for later, so we can restore
+ * remember the old epp values for later, so we can restore
* them if check_exec() fails.
*/
+ old_segflg = epp->ep_segflg;
+ old_name = epp->ep_name;
scriptvp = epp->ep_vp;
- oldpnbuf = epp->ep_ndp->ni_cnd.cn_pnbuf;
+ strlcpy(old_comm, epp->ep_comm, sizeof(old_comm));
/*
* if the magic isn't that of a shell script, or we've already
@@ -186,13 +191,7 @@ check_shell:
FRELE(fp, p);
}
- /* set up the parameters for the recursive check_exec() call */
- epp->ep_ndp->ni_dirfd = AT_FDCWD;
- epp->ep_ndp->ni_dirp = shellname;
- epp->ep_ndp->ni_segflg = UIO_SYSSPACE;
- epp->ep_flags |= EXEC_INDIR;
-
- /* and set up the fake args list, for later */
+ /* set up the fake args list, for later */
shellargp = mallocarray(4, sizeof(char *), M_EXEC, M_WAITOK);
tmpsap = shellargp;
*tmpsap = malloc(shellnamelen + 1, M_EXEC, M_WAITOK);
@@ -214,6 +213,11 @@ check_shell:
tmpsap++;
*tmpsap = NULL;
+ /* set up the parameters for the recursive check_exec() call */
+ epp->ep_segflg = UIO_SYSSPACE;
+ epp->ep_name = shellname;
+ epp->ep_flags |= EXEC_INDIR;
+
/*
* mark the header we have as invalid; check_exec will read
* the header from the new executable
@@ -233,9 +237,6 @@ check_shell:
if ((epp->ep_flags & EXEC_HASFD) == 0)
vn_close(scriptvp, FREAD, p->p_ucred, p);
- /* free the old pathname buffer */
- pool_put(&namei_pool, oldpnbuf);
-
epp->ep_flags |= (EXEC_HASARGL | EXEC_SKIPARG);
epp->ep_fa = shellargp;
/*
@@ -250,8 +251,13 @@ check_shell:
return (0);
}
- /* XXX oldpnbuf not set for "goto fail" path */
- epp->ep_ndp->ni_cnd.cn_pnbuf = oldpnbuf;
+ /*
+ * no need to restore these ep_* in 'fail' as there were not
+ * changed at this point
+ */
+ epp->ep_segflg = old_segflg;
+ epp->ep_name = old_name;
+ strlcpy(epp->ep_comm, old_comm, sizeof(epp->ep_comm));
fail:
/* note that we've clobbered the header */
epp->ep_flags |= EXEC_DESTR;
@@ -265,8 +271,6 @@ fail:
} else
vn_close(scriptvp, FREAD, p->p_ucred, p);
- pool_put(&namei_pool, epp->ep_ndp->ni_cnd.cn_pnbuf);
-
/* free the fake arg list, because we're not returning it */
if (shellargp != NULL) {
free(shellargp[0], M_EXEC, shellnamelen + 1);
blob - 674c62a53ed99e4beb9afc311f3bc01a0046c8fc
file + sys/kern/kern_exec.c
--- sys/kern/kern_exec.c
+++ sys/kern/kern_exec.c
@@ -121,19 +121,28 @@ check_exec(struct proc *p, struct exec_package *epp)
{
int error, i;
struct vnode *vp;
- struct nameidata *ndp;
- size_t resid;
+ struct nameidata nd;
+ size_t resid, len;
- ndp = epp->ep_ndp;
- ndp->ni_cnd.cn_nameiop = LOOKUP;
- ndp->ni_cnd.cn_flags = FOLLOW | LOCKLEAF | SAVENAME;
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | SAVENAME,
+ epp->ep_segflg, epp->ep_name, p);
+ nd.ni_pledge = PLEDGE_EXEC;
+ nd.ni_unveil = UNVEIL_EXEC;
+
if (epp->ep_flags & EXEC_INDIR)
- ndp->ni_cnd.cn_flags |= BYPASSUNVEIL;
+ nd.ni_cnd.cn_flags |= BYPASSUNVEIL;
+
/* first get the vnode */
- if ((error = namei(ndp)) != 0)
+ if ((error = namei(&nd)) != 0)
return (error);
- epp->ep_vp = vp = ndp->ni_vp;
+ epp->ep_vp = vp = nd.ni_vp;
+ /* copy the executable name and free namei buffer */
+ memset(epp->ep_comm, 0, sizeof(epp->ep_comm));
+ len = min(nd.ni_cnd.cn_namelen, MAXCOMLEN);
+ memcpy(epp->ep_comm, nd.ni_cnd.cn_nameptr, len);
+ pool_put(&namei_pool, nd.ni_cnd.cn_pnbuf);
+
/* check for regular file */
if (vp->v_type != VREG) {
error = EACCES;
@@ -222,18 +231,15 @@ check_exec(struct proc *p, struct exec_package *epp)
bad2:
/*
- * close the vnode, free the pathname buf, and punt.
+ * close the vnode, and punt.
*/
vn_close(vp, FREAD, p->p_ucred, p);
- pool_put(&namei_pool, ndp->ni_cnd.cn_pnbuf);
return (error);
bad1:
/*
- * free the namei pathname buffer, and put the vnode
- * (which we don't yet have open).
+ * put the vnode (which we don't yet have open).
*/
- pool_put(&namei_pool, ndp->ni_cnd.cn_pnbuf);
vput(vp);
return (error);
}
@@ -251,7 +257,6 @@ sys_execve(struct proc *p, void *v, register_t *retval
} */ *uap = v;
int error;
struct exec_package pack;
- struct nameidata nid;
struct vattr attr;
struct ucred *cred = p->p_ucred;
char *argp;
@@ -281,18 +286,15 @@ sys_execve(struct proc *p, void *v, register_t *retval
*/
atomic_setbits_int(&pr->ps_flags, PS_INEXEC);
- NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p);
- nid.ni_pledge = PLEDGE_EXEC;
- nid.ni_unveil = UNVEIL_EXEC;
-
/*
* initialize the fields of the exec package.
*/
+ pack.ep_segflg = UIO_USERSPACE;
pack.ep_name = (char *)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_vp = NULL;
pack.ep_interp = NULL;
pack.ep_emul_arg = NULL;
VMCMDSET_INIT(&pack.ep_vmcmds);
@@ -507,8 +509,7 @@ sys_execve(struct proc *p, void *v, register_t *retval
/* set command name & other accounting info */
memset(pr->ps_comm, 0, sizeof(pr->ps_comm));
- len = min(nid.ni_cnd.cn_namelen, MAXCOMLEN);
- memcpy(pr->ps_comm, nid.ni_cnd.cn_nameptr, len);
+ strlcpy(pr->ps_comm, pack.ep_comm, sizeof(pr->ps_comm));
pr->ps_acflag &= ~AFORK;
/* record proc's vnode, for use by sysctl */
@@ -665,7 +666,6 @@ sys_execve(struct proc *p, void *v, register_t *retval
km_free(argp, NCARGS, &kv_exec, &kp_pageable);
- pool_put(&namei_pool, nid.ni_cnd.cn_pnbuf);
vn_close(pack.ep_vp, FREAD, cred, p);
/*
@@ -736,7 +736,6 @@ bad:
free(pack.ep_emul_arg, M_TEMP, pack.ep_emul_argsize);
/* close and put the exec'd file */
vn_close(pack.ep_vp, FREAD, cred, p);
- pool_put(&namei_pool, nid.ni_cnd.cn_pnbuf);
km_free(argp, NCARGS, &kv_exec, &kp_pageable);
freehdr:
@@ -757,7 +756,6 @@ exec_abort:
pool_put(&namei_pool, pack.ep_interp);
if (pack.ep_emul_arg != NULL)
free(pack.ep_emul_arg, M_TEMP, pack.ep_emul_argsize);
- pool_put(&namei_pool, nid.ni_cnd.cn_pnbuf);
vn_close(pack.ep_vp, FREAD, cred, p);
km_free(argp, NCARGS, &kv_exec, &kp_pageable);
blob - e64674347a5fd8f06b9a39a4c9c0fb5fe4b4794d
file + sys/sys/exec.h
--- sys/sys/exec.h
+++ sys/sys/exec.h
@@ -112,11 +112,12 @@ struct exec_vmcmd_set {
};
struct exec_package {
+ enum uio_seg ep_segflg; /* ep_name location */
char *ep_name; /* file's name */
void *ep_hdr; /* file's exec header */
u_int ep_hdrlen; /* length of ep_hdr */
u_int ep_hdrvalid; /* bytes of ep_hdr that are valid */
- struct nameidata *ep_ndp; /* namei data pointer for lookups */
+ char ep_comm[MAXCOMLEN+1]; /* command name */
struct exec_vmcmd_set ep_vmcmds; /* vmcmds used to build vmspace */
struct vnode *ep_vp; /* executable's vnode */
struct vattr *ep_vap; /* executable's attributes */