Index: include/unistd.h =================================================================== --- include/unistd.h +++ include/unistd.h @@ -431,6 +431,7 @@ int faccessat(int, const char *, int, int); int fchownat(int, const char *, uid_t, gid_t, int); int fexecve(int, char *const [], char *const []); +int fldexec(int, int, char *const [], char *const []); int linkat(int, const char *, int, const char *, int); ssize_t readlinkat(int, const char * __restrict, char * __restrict, size_t); int symlinkat(const char *, int, const char *); Index: lib/libc/sys/Makefile.inc =================================================================== --- lib/libc/sys/Makefile.inc +++ lib/libc/sys/Makefile.inc @@ -364,7 +364,8 @@ cpuset.2 cpuset_setid.2 MLINKS+=cpuset_getaffinity.2 cpuset_setaffinity.2 MLINKS+=dup.2 dup2.2 -MLINKS+=execve.2 fexecve.2 +MLINKS+=execve.2 fexecve.2 \ + execve.2 fldexec.2 MLINKS+=extattr_get_file.2 extattr.2 \ extattr_get_file.2 extattr_delete_fd.2 \ extattr_get_file.2 extattr_delete_file.2 \ Index: lib/libc/sys/execve.2 =================================================================== --- lib/libc/sys/execve.2 +++ lib/libc/sys/execve.2 @@ -33,7 +33,8 @@ .Os .Sh NAME .Nm execve , -.Nm fexecve +.Nm fexecve, +.Nm fldexec .Nd execute a file .Sh LIBRARY .Lb libc Index: sys/compat/freebsd32/syscalls.master =================================================================== --- sys/compat/freebsd32/syscalls.master +++ sys/compat/freebsd32/syscalls.master @@ -1081,3 +1081,5 @@ id_t id, \ const struct vm_domain_policy *policy); } 550 AUE_FSYNC NOPROTO { int fdatasync(int fd); } +551 AUE_NULL STD { int fldexec(int rtld, int bin, char **argv, \ + char **envv); } Index: sys/kern/capabilities.conf =================================================================== --- sys/kern/capabilities.conf +++ sys/kern/capabilities.conf @@ -176,6 +176,7 @@ ## such as disallowing privilege escalation. ## fexecve +fldexec ## ## Allow flock(2), subject to capability rights. Index: sys/kern/imgact_elf.c =================================================================== --- sys/kern/imgact_elf.c +++ sys/kern/imgact_elf.c @@ -87,8 +87,10 @@ static int __elfN(check_header)(const Elf_Ehdr *hdr); static Elf_Brandinfo *__elfN(get_brandinfo)(struct image_params *imgp, const char *interp, int interp_name_len, int32_t *osrel); -static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr, +static int __elfN(load_file)(struct proc *p, struct vnode *vp, u_long *addr, u_long *entry, size_t pagesize); +static int __elfN(load_path)(struct proc *p, const char *path, u_long *addr, + u_long *entry, size_t pagesize); static int __elfN(load_section)(struct image_params *imgp, vm_ooffset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot, size_t pagesize); @@ -624,17 +626,15 @@ * the entry point for the loaded file. */ static int -__elfN(load_file)(struct proc *p, const char *file, u_long *addr, +__elfN(load_file)(struct proc *p, struct vnode *vp, u_long *addr, u_long *entry, size_t pagesize) { struct { - struct nameidata nd; struct vattr attr; struct image_params image_params; } *tempdata; const Elf_Ehdr *hdr = NULL; const Elf_Phdr *phdr = NULL; - struct nameidata *nd; struct vattr *attr; struct image_params *imgp; vm_prot_t prot; @@ -642,17 +642,7 @@ u_long base_addr = 0; int error, i, numsegs; -#ifdef CAPABILITY_MODE - /* - * XXXJA: This check can go away once we are sufficiently confident - * that the checks in namei() are correct. - */ - if (IN_CAPABILITY_MODE(curthread)) - return (ECAPMODE); -#endif - tempdata = malloc(sizeof(*tempdata), M_TEMP, M_WAITOK); - nd = &tempdata->nd; attr = &tempdata->attr; imgp = &tempdata->image_params; @@ -665,14 +655,7 @@ imgp->image_header = NULL; imgp->object = NULL; imgp->execlabel = NULL; - - NDINIT(nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, file, curthread); - if ((error = namei(nd)) != 0) { - nd->ni_vp = NULL; - goto fail; - } - NDFREE(nd, NDF_ONLY_PNBUF); - imgp->vp = nd->ni_vp; + imgp->vp = vp; /* * Check permissions, modes, uid, etc on the file, and "open" it. @@ -689,9 +672,9 @@ * Also make certain that the interpreter stays the same, so set * its VV_TEXT flag, too. */ - VOP_SET_TEXT(nd->ni_vp); + VOP_SET_TEXT(vp); - imgp->object = nd->ni_vp->v_object; + imgp->object = vp->v_object; hdr = (const Elf_Ehdr *)imgp->image_header; if ((error = __elfN(check_header)(hdr)) != 0) @@ -744,17 +727,47 @@ if (imgp->firstpage) exec_unmap_first_page(imgp); - if (nd->ni_vp) - vput(nd->ni_vp); - + vput(vp); free(tempdata, M_TEMP); return (error); } +/* + * Load a file into memory as specified by a path. + */ +static int +__elfN(load_path)(struct proc *p, const char *path, u_long *addr, + u_long *entry, size_t pagesize) +{ + struct nameidata nd; + struct vnode *vp = NULL; + int error; + +#ifdef CAPABILITY_MODE + /* + * XXXJA: This check can go away once we are sufficiently confident + * that the checks in namei() are correct. + */ + if (IN_CAPABILITY_MODE(curthread)) + return (ECAPMODE); +#endif + + NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, path, curthread); + error = namei(&nd); + vp = nd.ni_vp; + NDFREE(&nd, NDF_ONLY_PNBUF); + if (error != 0) { + return (error); + } + + return __elfN(load_file)(p, vp, addr, entry, pagesize); +} + static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) { + cap_rights_t rights; struct thread *td; const Elf_Ehdr *hdr; const Elf_Phdr *phdr; @@ -768,7 +781,7 @@ u_long text_size, data_size, total_size, text_addr, data_addr; u_long seg_size, seg_addr, addr, baddr, et_dyn_addr, entry, proghdr; int32_t osrel; - int error, i, n, interp_name_len, have_interp; + int error, i, n, interp_fd, interp_name_len, have_interp; hdr = (const Elf_Ehdr *)imgp->image_header; @@ -818,6 +831,10 @@ break; case PT_INTERP: /* Path to interpreter */ + if (interp != NULL) { + /* fldexec() is overriding the interpreter */ + break; + } if (phdr[i].p_filesz > MAXPATHLEN) { uprintf("Invalid PT_INTERP\n"); error = ENOEXEC; @@ -1013,12 +1030,32 @@ if (interp != NULL) { have_interp = FALSE; VOP_UNLOCK(imgp->vp, 0); + if ((interp_fd = imgp->args->interpreter) != -1) { + /* fldexec() is overriding the interpreter */ + error = fgetvp_exec(td, interp_fd, + cap_rights_init(&rights, CAP_FEXECVE), &imgp->vp); + if (error) { + uprintf("failed getting interpreter from FD %d", + interp_fd); + goto ret; + } + + error = vn_lock(imgp->vp, LK_SHARED | LK_RETRY); + if (error) { + goto ret; + } + + error = __elfN(load_file)(imgp->proc, imgp->vp, &addr, + &imgp->entry_addr, sv->sv_pagesize); + if (error == 0) + have_interp = TRUE; + } if (brand_info->emul_path != NULL && brand_info->emul_path[0] != '\0') { path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); snprintf(path, MAXPATHLEN, "%s%s", brand_info->emul_path, interp); - error = __elfN(load_file)(imgp->proc, path, &addr, + error = __elfN(load_path)(imgp->proc, path, &addr, &imgp->entry_addr, sv->sv_pagesize); free(path, M_TEMP); if (error == 0) @@ -1027,13 +1064,13 @@ if (!have_interp && newinterp != NULL && (brand_info->interp_path == NULL || strcmp(interp, brand_info->interp_path) == 0)) { - error = __elfN(load_file)(imgp->proc, newinterp, &addr, + error = __elfN(load_path)(imgp->proc, newinterp, &addr, &imgp->entry_addr, sv->sv_pagesize); if (error == 0) have_interp = TRUE; } if (!have_interp) { - error = __elfN(load_file)(imgp->proc, interp, &addr, + error = __elfN(load_path)(imgp->proc, interp, &addr, &imgp->entry_addr, sv->sv_pagesize); } vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); Index: sys/kern/kern_exec.c =================================================================== --- sys/kern/kern_exec.c +++ sys/kern/kern_exec.c @@ -252,6 +252,35 @@ } #ifndef _SYS_SYSPROTO_H_ +struct fldexec_args { + int interpreter; + int fd; + char **argv; + char **envv; +} +#endif +int +sys_fldexec(struct thread *td, struct fldexec_args *uap) +{ + struct image_args args; + struct vmspace *oldvmspace; + int error; + + error = pre_execve(td, &oldvmspace); + if (error != 0) + return (error); + error = exec_copyin_args(&args, NULL, UIO_SYSSPACE, + uap->argv, uap->envv); + if (error == 0) { + args.interpreter = uap->interpreter; + args.fd = uap->fd; + error = kern_execve(td, &args, NULL); + } + post_execve(td, error, oldvmspace); + return (error); +} + +#ifndef _SYS_SYSPROTO_H_ struct __mac_execve_args { char *fname; char **argv; @@ -1172,6 +1201,11 @@ return (EFAULT); /* + * Don't override the imgact-specified interpreter by default. + */ + args->interpreter = -1; + + /* * Allocate demand-paged memory for the file name, argument, and * environment strings. */ Index: sys/kern/syscalls.master =================================================================== --- sys/kern/syscalls.master +++ sys/kern/syscalls.master @@ -995,6 +995,8 @@ id_t id, const struct \ vm_domain_policy_entry *policy); } 550 AUE_FSYNC STD { int fdatasync(int fd); } +551 AUE_NULL STD { int fldexec(int interpreter, int fd, \ + char **argv, char **envv); } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master Index: sys/sys/imgact.h =================================================================== --- sys/sys/imgact.h +++ sys/sys/imgact.h @@ -51,6 +51,7 @@ int stringspace; /* space left in arg & env buffer */ int argc; /* count of argument strings */ int envc; /* count of environment strings */ + int interpreter; /* descriptor of interpreter to override with */ int fd; /* file descriptor of the executable */ struct filedesc *fdp; /* new file descriptor table */ };