exec: store parent directory and hardlink name of the binary in struct proc

While doing it, also move all the code to resolve pathnames and obtain
text vp and dvp, into single place.   Besides simplifying the code, it
avoids spurious vnode relocks and validates the explanation why
a transient text reference on the script vnode is not harmful.

Reviewed by:	markj
Tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
Differential revision:	https://reviews.freebsd.org/D32611
This commit is contained in:
Konstantin Belousov 2021-10-23 21:44:22 +03:00
parent 0c10648fbb
commit 351d5f7fc5
5 changed files with 85 additions and 53 deletions

View File

@ -420,7 +420,9 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
#ifdef KTRACE
struct ktr_io_params *kiop;
#endif
struct vnode *oldtextvp = NULL, *newtextvp;
struct vnode *oldtextvp, *newtextvp;
struct vnode *oldtextdvp, *newtextdvp;
char *oldbinname, *newbinname;
bool credential_changing;
#ifdef MAC
struct label *interpvplabel = NULL;
@ -436,6 +438,9 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
static const char fexecv_proc_title[] = "(fexecv)";
imgp = &image_params;
oldtextvp = oldtextdvp = NULL;
newtextvp = newtextdvp = NULL;
newbinname = oldbinname = NULL;
#ifdef KTRACE
kiop = NULL;
#endif
@ -471,19 +476,6 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
goto exec_fail;
#endif
/*
* Translate the file name. namei() returns a vnode pointer
* in ni_vp among other things.
*
* XXXAUDIT: It would be desirable to also audit the name of the
* interpreter if this is an interpreted binary.
*/
if (args->fname != NULL) {
NDINIT(&nd, LOOKUP, ISOPEN | LOCKLEAF | LOCKSHARED | FOLLOW |
SAVENAME | AUDITVNODE1 | WANTPARENT,
UIO_SYSSPACE, args->fname, td);
}
SDT_PROBE1(proc, , , exec, args->fname);
interpret:
@ -500,12 +492,42 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
goto exec_fail;
}
#endif
/*
* Translate the file name. namei() returns a vnode
* pointer in ni_vp among other things.
*/
NDINIT(&nd, LOOKUP, ISOPEN | LOCKLEAF | LOCKSHARED | FOLLOW |
SAVENAME | AUDITVNODE1 | WANTPARENT, UIO_SYSSPACE,
args->fname, td);
error = namei(&nd);
if (error)
goto exec_fail;
newtextvp = nd.ni_vp;
newtextdvp = nd.ni_dvp;
nd.ni_dvp = NULL;
newbinname = malloc(nd.ni_cnd.cn_namelen + 1, M_PARGS,
M_WAITOK);
memcpy(newbinname, nd.ni_cnd.cn_nameptr, nd.ni_cnd.cn_namelen);
newbinname[nd.ni_cnd.cn_namelen] = '\0';
imgp->vp = newtextvp;
/*
* Do the best to calculate the full path to the image file.
*/
if (args->fname[0] == '/') {
imgp->execpath = args->fname;
} else {
VOP_UNLOCK(imgp->vp);
freepath_size = MAXPATHLEN;
if (vn_fullpath_hardlink(newtextvp, newtextdvp,
newbinname, nd.ni_cnd.cn_namelen, &imgp->execpath,
&imgp->freepath, &freepath_size) != 0)
imgp->execpath = args->fname;
vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
}
} else {
AUDIT_ARG_FD(args->fd);
/*
@ -515,6 +537,9 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
&newtextvp);
if (error)
goto exec_fail;
if (vn_fullpath(imgp->vp, &imgp->execpath,
&imgp->freepath) != 0)
imgp->execpath = args->fname;
vn_lock(newtextvp, LK_SHARED | LK_RETRY);
AUDIT_ARG_VNODE1(newtextvp);
imgp->vp = newtextvp;
@ -624,28 +649,6 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
}
/* The new credentials are installed into the process later. */
/*
* Do the best to calculate the full path to the image file.
*/
if (args->fname != NULL) {
if (args->fname[0] == '/') {
imgp->execpath = args->fname;
} else {
VOP_UNLOCK(imgp->vp);
freepath_size = MAXPATHLEN;
if (vn_fullpath_hardlink(&nd, &imgp->execpath,
&imgp->freepath, &freepath_size) != 0)
imgp->execpath = args->fname;
vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
}
} else {
VOP_UNLOCK(imgp->vp);
if (vn_fullpath(imgp->vp, &imgp->execpath,
&imgp->freepath) != 0)
imgp->execpath = args->fname;
vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
}
/*
* If the current process has a special image activator it
* wants to try first, call it. For example, emulating shell
@ -699,10 +702,15 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
imgp->opened = false;
}
vput(newtextvp);
imgp->vp = newtextvp = NULL;
if (args->fname != NULL) {
if (nd.ni_dvp != NULL)
vrele(nd.ni_dvp);
if (newtextdvp != NULL) {
vrele(newtextdvp);
newtextdvp = NULL;
}
NDFREE(&nd, NDF_ONLY_PNBUF);
free(newbinname, M_PARGS);
newbinname = NULL;
}
vm_object_deallocate(imgp->object);
imgp->object = NULL;
@ -712,9 +720,6 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
imgp->freepath = NULL;
/* set new name to that of the interpreter */
args->fname = imgp->interpreter_name;
NDINIT(&nd, LOOKUP, ISOPEN | LOCKLEAF | LOCKSHARED | FOLLOW |
SAVENAME | WANTPARENT,
UIO_SYSSPACE, imgp->interpreter_name, td);
goto interpret;
}
@ -875,11 +880,17 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
}
/*
* Store the vp for use in procfs. This vnode was referenced by namei
* or fgetvp_exec.
* Store the vp for use in kern.proc.pathname. This vnode was
* referenced by namei() or fgetvp_exec().
*/
oldtextvp = p->p_textvp;
p->p_textvp = newtextvp;
oldtextdvp = p->p_textdvp;
p->p_textdvp = newtextdvp;
newtextdvp = NULL;
oldbinname = p->p_binname;
p->p_binname = newbinname;
newbinname = NULL;
#ifdef KDTRACE_HOOKS
/*
@ -953,11 +964,11 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
vput(imgp->vp);
else
VOP_UNLOCK(imgp->vp);
if (args->fname != NULL) {
if (nd.ni_dvp != NULL)
vrele(nd.ni_dvp);
if (args->fname != NULL)
NDFREE(&nd, NDF_ONLY_PNBUF);
}
if (newtextdvp != NULL)
vrele(newtextdvp);
free(newbinname, M_PARGS);
}
if (imgp->object != NULL)
@ -996,6 +1007,9 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
*/
if (oldtextvp != NULL)
vrele(oldtextvp);
if (oldtextdvp != NULL)
vrele(oldtextdvp);
free(oldbinname, M_PARGS);
#ifdef KTRACE
ktr_io_params_free(kiop);
#endif

View File

@ -424,12 +424,20 @@ exit1(struct thread *td, int rval, int signo)
ktrprocexit(td);
#endif
/*
* Release reference to text vnode
* Release reference to text vnode etc
*/
if (p->p_textvp != NULL) {
vrele(p->p_textvp);
p->p_textvp = NULL;
}
if (p->p_textdvp != NULL) {
vrele(p->p_textdvp);
p->p_textdvp = NULL;
}
if (p->p_binname != NULL) {
free(p->p_binname, M_PARGS);
p->p_binname = NULL;
}
/*
* Release our limits structure.

View File

@ -528,6 +528,7 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
}
p2->p_textvp = p1->p_textvp;
p2->p_textdvp = p1->p_textdvp;
p2->p_fd = fd;
p2->p_fdtol = fdtol;
p2->p_pd = pd;
@ -549,9 +550,16 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
PROC_UNLOCK(p1);
PROC_UNLOCK(p2);
/* Bump references to the text vnode (for procfs). */
if (p2->p_textvp)
/*
* Bump references to the text vnode and directory, and copy
* the hardlink name.
*/
if (p2->p_textvp != NULL)
vrefact(p2->p_textvp);
if (p2->p_textdvp != NULL)
vrefact(p2->p_textdvp);
p2->p_binname = p1->p_binname == NULL ? NULL :
strdup(p1->p_binname, M_PARGS);
/*
* Set up linkage for kernel based threading.

View File

@ -97,11 +97,11 @@ _Static_assert(offsetof(struct proc, p_flag) == 0xb8,
"struct proc KBI p_flag");
_Static_assert(offsetof(struct proc, p_pid) == 0xc4,
"struct proc KBI p_pid");
_Static_assert(offsetof(struct proc, p_filemon) == 0x3b8,
_Static_assert(offsetof(struct proc, p_filemon) == 0x3c8,
"struct proc KBI p_filemon");
_Static_assert(offsetof(struct proc, p_comm) == 0x3d0,
_Static_assert(offsetof(struct proc, p_comm) == 0x3e0,
"struct proc KBI p_comm");
_Static_assert(offsetof(struct proc, p_emuldata) == 0x4b8,
_Static_assert(offsetof(struct proc, p_emuldata) == 0x4c8,
"struct proc KBI p_emuldata");
#endif
#ifdef __i386__

View File

@ -666,6 +666,8 @@ struct proc {
int p_traceflag; /* (o) Kernel trace points. */
struct ktr_io_params *p_ktrioparms; /* (c + o) Params for ktrace. */
struct vnode *p_textvp; /* (b) Vnode of executable. */
struct vnode *p_textdvp; /* (b) Dir containing textvp. */
char *p_binname; /* (b) Binary hardlink name. */
u_int p_lock; /* (c) Proclock (prevent swap) count. */
struct sigiolst p_sigiolst; /* (c) List of sigio sources. */
int p_sigparent; /* (c) Signal to parent on exit. */