diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -241,9 +241,9 @@ #ifndef _SYS_SYSPROTO_H_ struct execve_args { - char *fname; + char *fname; char **argv; - char **envv; + char **envv; }; #endif @@ -420,11 +420,13 @@ #ifdef KTRACE struct ktr_io_params *kiop; #endif - struct vnode *oldtextvp = NULL, *newtextvp; - int credential_changing; + struct vnode *oldtextvp, *newtextvp; + struct vnode *oldtextdvp, *newtextdvp; + char *oldbinname, *newbinname; + bool credential_changing; #ifdef MAC struct label *interpvplabel = NULL; - int will_transition; + bool will_transition; #endif #ifdef HWPMC_HOOKS struct pmckern_procexec pe; @@ -432,9 +434,13 @@ int error, i, orig_osrel; uint32_t orig_fctl0; Elf_Brandinfo *orig_brandinfo; + size_t freepath_size; static const char fexecv_proc_title[] = "(fexecv)"; imgp = &image_params; + oldtextvp = oldtextdvp = NULL; + newtextvp = newtextdvp = NULL; + newbinname = oldbinname = NULL; #ifdef KTRACE kiop = NULL; #endif @@ -470,18 +476,6 @@ 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, UIO_SYSSPACE, args->fname, td); - } - SDT_PROBE1(proc, , , exec, args->fname); interpret: @@ -498,20 +492,53 @@ 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; + 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(&nd, &imgp->execpath, + &imgp->freepath, &freepath_size) != 0) + imgp->execpath = args->fname; + vn_lock(imgp->vp, LK_SHARED | LK_RETRY); + } + nd.ni_dvp = NULL; } else { AUDIT_ARG_FD(args->fd); /* * Descriptors opened only with O_EXEC or O_RDONLY are allowed. */ - error = fgetvp_exec(td, args->fd, &cap_fexecve_rights, &newtextvp); + error = fgetvp_exec(td, args->fd, &cap_fexecve_rights, + &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; @@ -557,14 +584,14 @@ * XXXMAC: For the time being, use NOSUID to also prohibit * transitions on the file system. */ - credential_changing = 0; + credential_changing = false; credential_changing |= (attr.va_mode & S_ISUID) && oldcred->cr_uid != attr.va_uid; credential_changing |= (attr.va_mode & S_ISGID) && oldcred->cr_gid != attr.va_gid; #ifdef MAC will_transition = mac_vnode_execve_will_transition(oldcred, imgp->vp, - interpvplabel, imgp); + interpvplabel, imgp) != 0; credential_changing |= will_transition; #endif @@ -621,18 +648,6 @@ } /* 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 && args->fname[0] == '/') - imgp->execpath = args->fname; - 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 @@ -678,16 +693,24 @@ VOP_UNSET_TEXT_CHECKED(newtextvp); imgp->textset = false; /* free name buffer and old vnode */ - if (args->fname != NULL) - NDFREE(&nd, NDF_ONLY_PNBUF); #ifdef MAC mac_execve_interpreter_enter(newtextvp, &interpvplabel); #endif if (imgp->opened) { VOP_CLOSE(newtextvp, FREAD, td->td_ucred, td); - imgp->opened = 0; + imgp->opened = false; } vput(newtextvp); + newtextvp = NULL; + if (args->fname != NULL) { + 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; execve_nosetid(imgp); @@ -695,8 +718,6 @@ free(imgp->freepath, M_TEMP); imgp->freepath = NULL; /* set new name to that of the interpreter */ - NDINIT(&nd, LOOKUP, ISOPEN | LOCKLEAF | LOCKSHARED | FOLLOW | - SAVENAME, UIO_SYSSPACE, imgp->interpreter_name, td); args->fname = imgp->interpreter_name; goto interpret; } @@ -858,11 +879,17 @@ } /* - * 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 /* @@ -928,8 +955,6 @@ exec_unmap_first_page(imgp); if (imgp->vp != NULL) { - if (args->fname) - NDFREE(&nd, NDF_ONLY_PNBUF); if (imgp->opened) VOP_CLOSE(imgp->vp, FREAD, td->td_ucred, td); if (imgp->textset) @@ -938,6 +963,11 @@ vput(imgp->vp); else VOP_UNLOCK(imgp->vp); + if (args->fname != NULL) + NDFREE(&nd, NDF_ONLY_PNBUF); + if (newtextdvp != NULL) + vrele(newtextdvp); + free(newbinname, M_PARGS); } if (imgp->object != NULL) @@ -976,6 +1006,9 @@ */ if (oldtextvp != NULL) vrele(oldtextvp); + if (oldtextdvp != NULL) + vrele(oldtextdvp); + free(oldbinname, M_PARGS); #ifdef KTRACE ktr_io_params_free(kiop); #endif @@ -1040,7 +1073,7 @@ #endif error = vm_page_grab_valid_unlocked(&m, object, 0, VM_ALLOC_COUNT(VM_INITIAL_PAGEIN) | - VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY | VM_ALLOC_WIRED); + VM_ALLOC_NORMAL | VM_ALLOC_NOBUSY | VM_ALLOC_WIRED); if (error != VM_PAGER_OK) return (EIO); @@ -1089,7 +1122,7 @@ vm_prot_t stack_prot; u_long ssiz; - imgp->vmspace_destroyed = 1; + imgp->vmspace_destroyed = true; imgp->sysent = sv; if (p->p_sysent->sv_onexec_old != NULL) @@ -1789,7 +1822,7 @@ */ error = VOP_OPEN(vp, FREAD, td->td_ucred, td, NULL); if (error == 0) - imgp->opened = 1; + imgp->opened = true; return (error); } @@ -1985,7 +2018,8 @@ if (locked) PROC_UNLOCK(p); if (cp->comp != NULL) - error = compressor_write(cp->comp, __DECONST(char *, data), len); + error = compressor_write(cp->comp, __DECONST(char *, data), + len); else error = core_write(cp, __DECONST(void *, data), len, cp->offset, UIO_SYSSPACE, NULL); diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -424,12 +424,20 @@ 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. diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -528,6 +528,7 @@ } 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 @@ 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. diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -2233,32 +2235,78 @@ pid_t *pidp = (pid_t *)arg1; unsigned int arglen = arg2; struct proc *p; - struct vnode *vp; - char *retbuf, *freebuf; + struct vnode *vp, *dvp; + char *retbuf, *freebuf, *binname; + struct nameidata nd; + size_t freepath_size; int error; + bool do_fullpath; if (arglen != 1) return (EINVAL); + binname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); + binname[0] = '\0'; if (*pidp == -1) { /* -1 means this process */ p = req->td->td_proc; } else { error = pget(*pidp, PGET_CANSEE, &p); - if (error != 0) + if (error != 0) { + free(binname, M_TEMP); return (error); + } } vp = p->p_textvp; if (vp == NULL) { if (*pidp != -1) PROC_UNLOCK(p); + free(binname, M_TEMP); return (0); } vref(vp); + dvp = p->p_textdvp; + if (dvp != NULL) + vref(dvp); + if (p->p_binname != NULL) + strlcpy(binname, p->p_binname, MAXPATHLEN); if (*pidp != -1) PROC_UNLOCK(p); - error = vn_fullpath(vp, &retbuf, &freebuf); + do_fullpath = true; + if (dvp != NULL && binname[0] != '\0') { + freepath_size = MAXPATHLEN; + nd.ni_vp = vp; + nd.ni_dvp = dvp; + nd.ni_cnd.cn_nameptr = binname; + nd.ni_cnd.cn_namelen = strlen(binname); + if (vn_fullpath_hardlink(&nd, &retbuf, &freebuf, + &freepath_size) == 0) { + /* + * Recheck the looked up path. The binary + * might have been renamed or replaced, in + * which case we should not report old name. + */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, retbuf, + req->td); + error = namei(&nd); + if (error == 0) { + NDFREE(&nd, NDF_ONLY_PNBUF); + if (nd.ni_vp == vp) { + do_fullpath = false; + } else { + free(freebuf, M_TEMP); + freebuf = NULL; + } + vrele(nd.ni_vp); + } + } + } + if (do_fullpath) + error = vn_fullpath(vp, &retbuf, &freebuf); vrele(vp); - if (error) + if (dvp != NULL) + vrele(dvp); + free(binname, M_TEMP); + if (error != 0) return (error); error = SYSCTL_OUT(req, retbuf, strlen(retbuf) + 1); free(freebuf, M_TEMP); diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c --- a/sys/kern/kern_thread.c +++ b/sys/kern/kern_thread.c @@ -97,11 +97,11 @@ "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__ diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -579,8 +579,6 @@ "Number of times 3-way vnode locking failed"); static void cache_zap_locked(struct namecache *ncp); -static int vn_fullpath_hardlink(struct nameidata *ndp, char **retbuf, - char **freebuf, size_t *buflen); static int vn_fullpath_any_smr(struct vnode *vp, struct vnode *rdir, char *buf, char **retbuf, size_t *buflen, size_t addend); static int vn_fullpath_any(struct vnode *vp, struct vnode *rdir, char *buf, @@ -3602,7 +3600,7 @@ * - otherwise we populate the buffer with the saved name and start resolving * from the parent */ -static int +int vn_fullpath_hardlink(struct nameidata *ndp, char **retbuf, char **freebuf, size_t *buflen) { diff --git a/sys/sys/imgact.h b/sys/sys/imgact.h --- a/sys/sys/imgact.h +++ b/sys/sys/imgact.h @@ -58,21 +58,16 @@ }; struct image_params { - struct proc *proc; /* our process struct */ + struct proc *proc; /* our process */ struct label *execlabel; /* optional exec label */ - struct vnode *vp; /* pointer to vnode of file to exec */ + struct vnode *vp; /* pointer to vnode of file to exec */ struct vm_object *object; /* The vm object for this vp */ - struct vattr *attr; /* attributes of file */ - const char *image_header; /* head of file to exec */ - unsigned long entry_addr; /* entry address of target executable */ - unsigned long reloc_base; /* load address of image */ - char vmspace_destroyed; /* flag - we've blown away original vm space */ -#define IMGACT_SHELL 0x1 -#define IMGACT_BINMISC 0x2 - unsigned char interpreted; /* mask of interpreters that have run */ - char opened; /* flag - we have opened executable vnode */ - char *interpreter_name; /* name of the interpreter */ - void *auxargs; /* ELF Auxinfo structure pointer */ + struct vattr *attr; /* attributes of file */ + const char *image_header; /* header of file to exec */ + unsigned long entry_addr; /* entry address of target executable */ + unsigned long reloc_base; /* load address of image */ + char *interpreter_name; /* name of the interpreter */ + void *auxargs; /* ELF Auxinfo structure pointer */ struct sf_buf *firstpage; /* first page that we mapped */ void *ps_strings; /* pointer to ps_string (user space) */ struct image_args *args; /* system call arguments */ @@ -90,7 +85,12 @@ u_long stack_sz; u_long eff_stack_sz; struct ucred *newcred; /* new credentials if changing */ +#define IMGACT_SHELL 0x1 +#define IMGACT_BINMISC 0x2 + unsigned char interpreted; /* mask of interpreters that have run */ bool credential_setid; /* true if becoming setid */ + bool vmspace_destroyed; /* we've blown away original vm space */ + bool opened; /* we have opened executable vnode */ bool textset; u_int map_flags; }; diff --git a/sys/sys/proc.h b/sys/sys/proc.h --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -666,6 +666,8 @@ 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. */ diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -697,6 +697,8 @@ int vn_getcwd(char *buf, char **retbuf, size_t *buflen); int vn_fullpath(struct vnode *vp, char **retbuf, char **freebuf); int vn_fullpath_global(struct vnode *vp, char **retbuf, char **freebuf); +int vn_fullpath_hardlink(struct nameidata *ndp, char **retbuf, + char **freebuf, size_t *buflen); struct vnode * vn_dir_dd_ino(struct vnode *vp); int vn_commname(struct vnode *vn, char *buf, u_int buflen);