Here's a simple fexecve(2) implementation. Comments?
christos Index: include/unistd.h =================================================================== RCS file: /cvsroot/src/include/unistd.h,v retrieving revision 1.151 diff -u -p -u -r1.151 unistd.h --- include/unistd.h 18 Nov 2018 19:22:23 -0000 1.151 +++ include/unistd.h 8 Sep 2019 17:20:08 -0000 @@ -107,6 +107,7 @@ int execle(const char *, const char *, int execlp(const char *, const char *, ...); int execv(const char *, char * const *); int execve(const char *, char * const *, char * const *); +int fexecve(int, char * const *, char * const *); int execvp(const char *, char * const *); pid_t fork(void); long fpathconf(int, int); Index: sys/kern/exec_elf.c =================================================================== RCS file: /cvsroot/src/sys/kern/exec_elf.c,v retrieving revision 1.98 diff -u -p -u -r1.98 exec_elf.c --- sys/kern/exec_elf.c 7 Jun 2019 23:35:52 -0000 1.98 +++ sys/kern/exec_elf.c 8 Sep 2019 17:20:08 -0000 @@ -157,6 +157,7 @@ elf_populate_auxv(struct lwp *l, struct size_t len, vlen; AuxInfo ai[ELF_AUX_ENTRIES], *a, *execname; struct elf_args *ap; + char *path = l->l_proc->p_path; int error; a = ai; @@ -224,9 +225,11 @@ elf_populate_auxv(struct lwp *l, struct a->a_v = l->l_proc->p_stackbase; a++; - execname = a; - a->a_type = AT_SUN_EXECNAME; - a++; + if (*path == '/') { + execname = a; + a->a_type = AT_SUN_EXECNAME; + a++; + } exec_free_emul_arg(pack); } else { @@ -242,7 +245,6 @@ elf_populate_auxv(struct lwp *l, struct KASSERT(vlen <= sizeof(ai)); if (execname) { - char *path = l->l_proc->p_path; execname->a_v = (uintptr_t)(*stackp + vlen); len = strlen(path) + 1; if ((error = copyout(path, (*stackp + vlen), len)) != 0) Index: sys/kern/kern_exec.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_exec.c,v retrieving revision 1.479 diff -u -p -u -r1.479 kern_exec.c --- sys/kern/kern_exec.c 7 Sep 2019 15:34:44 -0000 1.479 +++ sys/kern/kern_exec.c 8 Sep 2019 17:20:08 -0000 @@ -309,7 +309,8 @@ exec_path_free(struct execve_data *data) { pathbuf_stringcopy_put(data->ed_pathbuf, data->ed_pathstring); pathbuf_destroy(data->ed_pathbuf); - PNBUF_PUT(data->ed_resolvedpathbuf); + if (data->ed_resolvedpathbuf) + PNBUF_PUT(data->ed_resolvedpathbuf); } /* @@ -343,22 +344,35 @@ check_exec(struct lwp *l, struct exec_pa { int error, i; struct vnode *vp; - struct nameidata nd; size_t resid; - // grab the absolute pathbuf here before namei() trashes it. - pathbuf_copystring(pb, epp->ep_resolvedname, PATH_MAX); - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb); + if (epp->ep_resolvedname) { + struct nameidata nd; - /* first get the vnode */ - if ((error = namei(&nd)) != 0) - return error; - epp->ep_vp = vp = nd.ni_vp; + // grab the absolute pathbuf here before namei() trashes it. + pathbuf_copystring(pb, epp->ep_resolvedname, PATH_MAX); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb); + + /* first get the vnode */ + if ((error = namei(&nd)) != 0) + return error; + epp->ep_vp = vp = nd.ni_vp; #ifdef DIAGNOSTIC - /* paranoia (take this out once namei stuff stabilizes) */ - memset(nd.ni_pnbuf, '~', PATH_MAX); + /* paranoia (take this out once namei stuff stabilizes) */ + memset(nd.ni_pnbuf, '~', PATH_MAX); #endif + } else { + struct file *fp; + + if ((error = fd_getvnode(epp->ep_xfd, &fp)) != 0) + return error; + epp->ep_vp = vp = fp->f_vnode; + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + vref(vp); + fd_putfile(epp->ep_xfd); + } + /* check access and type */ if (vp->v_type != VREG) { @@ -387,19 +401,21 @@ check_exec(struct lwp *l, struct exec_pa /* unlock vp, since we need it unlocked from here on out. */ VOP_UNLOCK(vp); + if (epp->ep_resolvedname) { #if NVERIEXEC > 0 - error = veriexec_verify(l, vp, epp->ep_resolvedname, - epp->ep_flags & EXEC_INDIR ? VERIEXEC_INDIRECT : VERIEXEC_DIRECT, - NULL); - if (error) - goto bad2; + error = veriexec_verify(l, vp, epp->ep_resolvedname, + epp->ep_flags & EXEC_INDIR ? VERIEXEC_INDIRECT + : VERIEXEC_DIRECT, NULL); + if (error) + goto bad2; #endif /* NVERIEXEC > 0 */ #ifdef PAX_SEGVGUARD - error = pax_segvguard(l, vp, epp->ep_resolvedname, false); - if (error) - goto bad2; + error = pax_segvguard(l, vp, epp->ep_resolvedname, false); + if (error) + goto bad2; #endif /* PAX_SEGVGUARD */ + } /* now we have the file, get the exec header */ error = vn_rdwr(UIO_READ, vp, epp->ep_hdr, epp->ep_hdrlen, 0, @@ -545,7 +561,7 @@ sys_execve(struct lwp *l, const struct s syscallarg(char * const *) envp; } */ - return execve1(l, SCARG(uap, path), SCARG(uap, argp), + return execve1(l, SCARG(uap, path), -1, SCARG(uap, argp), SCARG(uap, envp), execve_fetch_element); } @@ -559,7 +575,8 @@ sys_fexecve(struct lwp *l, const struct syscallarg(char * const *) envp; } */ - return ENOSYS; + return execve1(l, NULL, SCARG(uap, fd), SCARG(uap, argp), + SCARG(uap, envp), execve_fetch_element); } /* @@ -679,8 +696,17 @@ exec_vm_minaddr(vaddr_t va_min) return va_min; } +static void +exec_makefdbuf(struct lwp *l, unsigned int fd, struct pathbuf **pbp) +{ + char *buf = PNBUF_GET(); + snprintf(buf, MAXPATHLEN, "/proc/%d/fd/%u", l->l_proc->p_ppid, fd); + + *pbp = pathbuf_assimilate(buf); +} + static int -execve_loadvm(struct lwp *l, const char *path, char * const *args, +execve_loadvm(struct lwp *l, const char *path, int fd, char * const *args, char * const *envs, execve_fetch_element_t fetch_element, struct execve_data * restrict data) { @@ -689,7 +715,6 @@ execve_loadvm(struct lwp *l, const char struct proc *p; char *dp; u_int modgen; - size_t offs; KASSERT(data != NULL); @@ -732,24 +757,36 @@ execve_loadvm(struct lwp *l, const char */ rw_enter(&p->p_reflock, RW_WRITER); - /* - * Init the namei data to point the file user's program name. - * This is done here rather than in check_exec(), so that it's - * possible to override this settings if any of makecmd/probe - * functions call check_exec() recursively - for example, - * see exec_script_makecmds(). - */ - if ((error = exec_makepathbuf(l, path, UIO_USERSPACE, - &data->ed_pathbuf, &offs)) != 0) - goto clrflg; - data->ed_pathstring = pathbuf_stringcopy_get(data->ed_pathbuf); - data->ed_resolvedpathbuf = PNBUF_GET(); + if (path == NULL) { + exec_makefdbuf(l, fd, &data->ed_pathbuf); + data->ed_pathstring = pathbuf_stringcopy_get(data->ed_pathbuf); + epp->ep_kname = "(fexecve)"; + data->ed_resolvedpathbuf = NULL; + epp->ep_resolvedname = NULL; + epp->ep_xfd = fd; + } else { + size_t offs; + /* + * Init the namei data to point the file user's program name. + * This is done here rather than in check_exec(), so that it's + * possible to override this settings if any of makecmd/probe + * functions call check_exec() recursively - for example, + * see exec_script_makecmds(). + */ + if ((error = exec_makepathbuf(l, path, UIO_USERSPACE, + &data->ed_pathbuf, &offs)) != 0) + goto clrflg; + data->ed_pathstring = pathbuf_stringcopy_get(data->ed_pathbuf); + epp->ep_kname = data->ed_pathstring + offs; + data->ed_resolvedpathbuf = PNBUF_GET(); + epp->ep_resolvedname = data->ed_resolvedpathbuf; + epp->ep_xfd = -1; + } + /* * initialize the fields of the exec package. */ - epp->ep_kname = data->ed_pathstring + offs; - epp->ep_resolvedname = data->ed_resolvedpathbuf; epp->ep_hdr = kmem_alloc(exec_maxhdrsz, KM_SLEEP); epp->ep_hdrlen = exec_maxhdrsz; epp->ep_hdrvalid = 0; @@ -942,7 +979,14 @@ execve_free_data(struct execve_data *dat static void pathexec(struct proc *p, const char *resolvedname) { - KASSERT(resolvedname[0] == '/'); + if (resolvedname == NULL) { + strlcpy(p->p_comm, "(fexecve)", sizeof(p->p_comm)); + kmem_strfree(p->p_path); + p->p_path = kmem_strdupsize("", NULL, KM_SLEEP); + return; + } + KASSERTMSG(resolvedname[0] == '/', "bad resolvedname `%s'", + resolvedname); /* set command name & other accounting info */ strlcpy(p->p_comm, strrchr(resolvedname, '/') + 1, sizeof(p->p_comm)); @@ -1346,13 +1390,13 @@ execve_runproc(struct lwp *l, struct exe } int -execve1(struct lwp *l, const char *path, char * const *args, +execve1(struct lwp *l, const char *path, int fd, char * const *args, char * const *envs, execve_fetch_element_t fetch_element) { struct execve_data data; int error; - error = execve_loadvm(l, path, args, envs, fetch_element, &data); + error = execve_loadvm(l, path, fd, args, envs, fetch_element, &data); if (error) return error; error = execve_runproc(l, &data, false, false); @@ -2376,7 +2420,7 @@ do_posix_spawn(struct lwp *l1, pid_t *pi * Do the first part of the exec now, collect state * in spawn_data. */ - error = execve_loadvm(l1, path, argv, + error = execve_loadvm(l1, path, -1, argv, envp, fetch, &spawn_data->sed_exec); if (error == EJUSTRETURN) error = 0; Index: sys/kern/vfs_vnops.c =================================================================== RCS file: /cvsroot/src/sys/kern/vfs_vnops.c,v retrieving revision 1.200 diff -u -p -u -r1.200 vfs_vnops.c --- sys/kern/vfs_vnops.c 7 Mar 2019 11:09:48 -0000 1.200 +++ sys/kern/vfs_vnops.c 8 Sep 2019 17:20:08 -0000 @@ -309,6 +309,9 @@ vn_openchk(struct vnode *vp, kauth_cred_ if ((fflags & FREAD) != 0) { permbits = VREAD; } + if ((fflags & FEXEC) != 0) { + permbits |= VEXEC; + } if ((fflags & (FWRITE | O_TRUNC)) != 0) { permbits |= VWRITE; if (vp->v_type == VDIR) { Index: sys/sys/exec.h =================================================================== RCS file: /cvsroot/src/sys/sys/exec.h,v retrieving revision 1.154 diff -u -p -u -r1.154 exec.h --- sys/sys/exec.h 27 Jan 2019 02:08:50 -0000 1.154 +++ sys/sys/exec.h 8 Sep 2019 17:20:08 -0000 @@ -188,7 +188,8 @@ struct exec_fakearg { struct exec_package { const char *ep_kname; /* kernel-side copy of file's name */ - char *ep_resolvedname; /* fully resolved path from namei */ + char *ep_resolvedname; /* fully resolved path from namei */ + int ep_xfd; /* fexecve file descriptor */ void *ep_hdr; /* file's exec header */ u_int ep_hdrlen; /* length of ep_hdr */ u_int ep_hdrvalid; /* bytes of ep_hdr that are valid */ @@ -301,7 +302,7 @@ void new_vmcmd(struct exec_vmcmd_set *, new_vmcmd(evsp,lwp,len,addr,vp,offset,prot,flags) typedef int (*execve_fetch_element_t)(char * const *, size_t, char **); -int execve1(struct lwp *, const char *, char * const *, char * const *, +int execve1(struct lwp *, const char *, int, char * const *, char * const *, execve_fetch_element_t); struct posix_spawn_file_actions; Index: sys/sys/fcntl.h =================================================================== RCS file: /cvsroot/src/sys/sys/fcntl.h,v retrieving revision 1.50 diff -u -p -u -r1.50 fcntl.h --- sys/sys/fcntl.h 20 Feb 2018 18:20:05 -0000 1.50 +++ sys/sys/fcntl.h 8 Sep 2019 17:20:08 -0000 @@ -121,6 +121,7 @@ #if defined(_NETBSD_SOURCE) #define O_NOSIGPIPE 0x01000000 /* don't deliver sigpipe */ #define O_REGULAR 0x02000000 /* fail if not a regular file */ +#define O_EXEC 0x04000000 /* open for executing only */ #endif #ifdef _KERNEL @@ -132,8 +133,9 @@ #define O_MASK (O_ACCMODE|O_NONBLOCK|O_APPEND|O_SHLOCK|O_EXLOCK|\ O_ASYNC|O_SYNC|O_CREAT|O_TRUNC|O_EXCL|O_DSYNC|\ O_RSYNC|O_NOCTTY|O_ALT_IO|O_NOFOLLOW|O_DIRECT|\ - O_DIRECTORY|O_CLOEXEC|O_NOSIGPIPE|O_REGULAR) + O_DIRECTORY|O_CLOEXEC|O_NOSIGPIPE|O_REGULAR|O_EXEC) +#define FEXEC O_EXEC #define FMARK 0x00001000 /* mark during gc() */ #define FDEFER 0x00002000 /* defer for next gc pass */ #define FHASLOCK 0x00004000 /* descriptor holds advisory lock */ @@ -144,7 +146,7 @@ #define FCNTLFLAGS (FAPPEND|FASYNC|FFSYNC|FNONBLOCK|FDSYNC|FRSYNC|FALTIO|\ FDIRECT|FNOSIGPIPE) /* bits to save after open(2) */ -#define FMASK (FREAD|FWRITE|FCNTLFLAGS) +#define FMASK (FREAD|FWRITE|FCNTLFLAGS|FEXEC) #endif /* _KERNEL */ /* Index: sys/compat/netbsd32/netbsd32_execve.c =================================================================== RCS file: /cvsroot/src/sys/compat/netbsd32/netbsd32_execve.c,v retrieving revision 1.39 diff -u -p -u -r1.39 netbsd32_execve.c --- sys/compat/netbsd32/netbsd32_execve.c 3 Sep 2018 16:29:29 -0000 1.39 +++ sys/compat/netbsd32/netbsd32_execve.c 8 Sep 2019 17:20:08 -0000 @@ -71,9 +71,8 @@ netbsd32_execve(struct lwp *l, const str syscallarg(netbsd32_charpp) argp; syscallarg(netbsd32_charpp) envp; } */ - const char *path = SCARG_P32(uap, path); - return execve1(l, path, SCARG_P32(uap, argp), + return execve1(l, SCARG_P32(uap, path), -1, SCARG_P32(uap, argp), SCARG_P32(uap, envp), netbsd32_execve_fetch_element); } @@ -86,13 +85,9 @@ netbsd32_fexecve(struct lwp *l, const st syscallarg(netbsd32_charpp) argp; syscallarg(netbsd32_charpp) envp; } */ - struct sys_fexecve_args ua; - NETBSD32TO64_UAP(fd); - NETBSD32TOP_UAP(argp, char * const); - NETBSD32TOP_UAP(envp, char * const); - - return sys_fexecve(l, &ua, retval); + return execve1(l, NULL, SCARG(uap, fd), SCARG_P32(uap, argp), + SCARG_P32(uap, envp), netbsd32_execve_fetch_element); } static int