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

Reply via email to