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,7 @@ 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 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/Symbol.map =================================================================== --- lib/libc/sys/Symbol.map +++ lib/libc/sys/Symbol.map @@ -330,6 +330,7 @@ fchmodat; fchownat; fexecve; + fldexec; fstatat; futimesat; jail_get; 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_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot, size_t pagesize); @@ -616,17 +618,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; @@ -634,17 +634,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; @@ -657,14 +647,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. @@ -681,9 +664,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) @@ -736,17 +719,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; @@ -760,7 +773,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; @@ -810,6 +823,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; @@ -1007,12 +1024,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) @@ -1021,13 +1058,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/init_sysent.c =================================================================== --- sys/kern/init_sysent.c +++ sys/kern/init_sysent.c @@ -3,7 +3,6 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 310638 2016-12-27 20:21:11Z jhb */ #include "opt_compat.h" Index: sys/kern/kern_exec.c =================================================================== --- sys/kern/kern_exec.c +++ sys/kern/kern_exec.c @@ -249,6 +249,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; @@ -1169,6 +1198,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.c =================================================================== --- sys/kern/syscalls.c +++ sys/kern/syscalls.c @@ -3,7 +3,6 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 310638 2016-12-27 20:21:11Z jhb */ const char *syscallnames[] = { 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/kern/systrace_args.c =================================================================== --- sys/kern/systrace_args.c +++ sys/kern/systrace_args.c @@ -3324,6 +3324,16 @@ *n_args = 1; break; } + /* fldexec */ + case 551: { + struct fldexec_args *p = params; + iarg[0] = p->interpreter; /* int */ + iarg[1] = p->fd; /* int */ + uarg[2] = (intptr_t) p->argv; /* char ** */ + uarg[3] = (intptr_t) p->envv; /* char ** */ + *n_args = 4; + break; + } default: *n_args = 0; break; @@ -8854,6 +8864,25 @@ break; }; break; + /* fldexec */ + case 551: + switch(ndx) { + case 0: + p = "int"; + break; + case 1: + p = "int"; + break; + case 2: + p = "userland char **"; + break; + case 3: + p = "userland char **"; + break; + default: + break; + }; + break; default: break; }; @@ -10770,6 +10799,11 @@ if (ndx == 0 || ndx == 1) p = "int"; break; + /* fldexec */ + case 551: + if (ndx == 0 || ndx == 1) + p = "int"; + break; default: break; }; Index: sys/sys/imgact.h =================================================================== --- sys/sys/imgact.h +++ sys/sys/imgact.h @@ -50,6 +50,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 */ }; Index: sys/sys/syscall.h =================================================================== --- sys/sys/syscall.h +++ sys/sys/syscall.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 310638 2016-12-27 20:21:11Z jhb + * created fromFreeBSD$ */ #define SYS_syscall 0 @@ -468,4 +468,5 @@ #define SYS_numa_getaffinity 548 #define SYS_numa_setaffinity 549 #define SYS_fdatasync 550 -#define SYS_MAXSYSCALL 551 +#define SYS_fldexec 551 +#define SYS_MAXSYSCALL 552 Index: sys/sys/syscall.mk =================================================================== --- sys/sys/syscall.mk +++ sys/sys/syscall.mk @@ -1,7 +1,7 @@ # FreeBSD system call object files. # DO NOT EDIT-- this file is automatically generated. # $FreeBSD$ -# created from FreeBSD: head/sys/kern/syscalls.master 310638 2016-12-27 20:21:11Z jhb +# created fromFreeBSD$ MIASM = \ syscall.o \ exit.o \ @@ -395,4 +395,5 @@ utimensat.o \ numa_getaffinity.o \ numa_setaffinity.o \ - fdatasync.o + fdatasync.o \ + fldexec.o Index: sys/sys/sysproto.h =================================================================== --- sys/sys/sysproto.h +++ sys/sys/sysproto.h @@ -3,7 +3,7 @@ * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD$ - * created from FreeBSD: head/sys/kern/syscalls.master 310638 2016-12-27 20:21:11Z jhb + * created fromFreeBSD$ */ #ifndef _SYS_SYSPROTO_H_ @@ -1786,6 +1786,12 @@ struct fdatasync_args { char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; }; +struct fldexec_args { + char interpreter_l_[PADL_(int)]; int interpreter; char interpreter_r_[PADR_(int)]; + char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; + char argv_l_[PADL_(char **)]; char ** argv; char argv_r_[PADR_(char **)]; + char envv_l_[PADL_(char **)]; char ** envv; char envv_r_[PADR_(char **)]; +}; int nosys(struct thread *, struct nosys_args *); void sys_sys_exit(struct thread *, struct sys_exit_args *); int sys_fork(struct thread *, struct fork_args *); @@ -2172,6 +2178,7 @@ int sys_numa_getaffinity(struct thread *, struct numa_getaffinity_args *); int sys_numa_setaffinity(struct thread *, struct numa_setaffinity_args *); int sys_fdatasync(struct thread *, struct fdatasync_args *); +int sys_fldexec(struct thread *, struct fldexec_args *); #ifdef COMPAT_43 @@ -2949,6 +2956,7 @@ #define SYS_AUE_numa_getaffinity AUE_NULL #define SYS_AUE_numa_setaffinity AUE_NULL #define SYS_AUE_fdatasync AUE_FSYNC +#define SYS_AUE_fldexec AUE_NULL #undef PAD_ #undef PADL_