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 @@ -421,10 +421,10 @@ struct ktr_io_params *kiop; #endif struct vnode *oldtextvp = NULL, *newtextvp; - int credential_changing; + bool credential_changing, hardlink_lookup; #ifdef MAC struct label *interpvplabel = NULL; - int will_transition; + bool will_transition; #endif #ifdef HWPMC_HOOKS struct pmckern_procexec pe; @@ -432,6 +432,7 @@ 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; @@ -477,9 +478,11 @@ * XXXAUDIT: It would be desirable to also audit the name of the * interpreter if this is an interpreted binary. */ + hardlink_lookup = args->fname != NULL && args->fname[0] != '/'; if (args->fname != NULL) { NDINIT(&nd, LOOKUP, ISOPEN | LOCKLEAF | LOCKSHARED | FOLLOW | - SAVENAME | AUDITVNODE1, UIO_SYSSPACE, args->fname, td); + SAVENAME | AUDITVNODE1 | (hardlink_lookup ? WANTPARENT : 0), + UIO_SYSSPACE, args->fname, td); } SDT_PROBE1(proc, , , exec, args->fname); @@ -509,7 +512,8 @@ /* * 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; vn_lock(newtextvp, LK_SHARED | LK_RETRY); @@ -557,14 +561,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 @@ -624,11 +628,24 @@ /* * Do the best to calculate the full path to the image file. */ - if (args->fname != NULL && args->fname[0] == '/') - imgp->execpath = args->fname; - else { + if (args->fname != NULL) { + if (args->fname[0] == '/') { + MPASS(!hardlink_lookup); + imgp->execpath = args->fname; + } else { + VOP_UNLOCK(imgp->vp); + MPASS(hardlink_lookup); + 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) + MPASS(!hardlink_lookup); + if (vn_fullpath(imgp->vp, &imgp->execpath, + &imgp->freepath) != 0) imgp->execpath = args->fname; vn_lock(imgp->vp, LK_SHARED | LK_RETRY); } @@ -678,16 +695,19 @@ 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); + if (args->fname != NULL) { + if (hardlink_lookup && nd.ni_dvp != NULL) + vrele(nd.ni_dvp); + NDFREE(&nd, NDF_ONLY_PNBUF); + } vm_object_deallocate(imgp->object); imgp->object = NULL; execve_nosetid(imgp); @@ -695,9 +715,11 @@ 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; + hardlink_lookup = args->fname != NULL && args->fname[0] != '/'; + NDINIT(&nd, LOOKUP, ISOPEN | LOCKLEAF | LOCKSHARED | FOLLOW | + SAVENAME | (hardlink_lookup ? WANTPARENT : 0), + UIO_SYSSPACE, imgp->interpreter_name, td); goto interpret; } @@ -928,8 +950,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 +958,11 @@ vput(imgp->vp); else VOP_UNLOCK(imgp->vp); + if (args->fname != NULL) { + if (hardlink_lookup && nd.ni_dvp != NULL) + vrele(nd.ni_dvp); + NDFREE(&nd, NDF_ONLY_PNBUF); + } } if (imgp->object != NULL) @@ -1040,7 +1065,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 +1114,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 +1814,7 @@ */ error = VOP_OPEN(vp, FREAD, td->td_ucred, td, NULL); if (error == 0) - imgp->opened = 1; + imgp->opened = true; return (error); } @@ -1985,7 +2010,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/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/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);