Theo de Raadt <[email protected]> wrote:

> Maybe we should investigate using mmap.  Of the 4 cases, 3 are not
> too difficult, but the 4th case will be very messy, including unwind
> for the 3rd case.

Here is a version that uses mmap instead of alloca, including rollback
of resource allocations in case of failure.  This is similar to the mmap
use in vfprintf which I added many years ago to support positional
arguments without alloca or malloc, so that snprintf family can be
remain maximally reentrant.  So I've been here before...

I have done a little testing, but it may still have bugs.

Index: lib/libc/gen/exec.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/exec.c,v
retrieving revision 1.23
diff -u -p -u -r1.23 exec.c
--- lib/libc/gen/exec.c 13 Mar 2016 18:34:20 -0000      1.23
+++ lib/libc/gen/exec.c 16 Sep 2021 20:48:25 -0000
@@ -30,6 +30,7 @@
 
 #include <sys/types.h>
 #include <sys/uio.h>
+#include <sys/mman.h>
 
 #include <errno.h>
 #include <limits.h>
@@ -45,25 +46,31 @@ execl(const char *name, const char *arg,
 {
        va_list ap;
        char **argv;
-       int n;
+       size_t maplen;
+       int save_errno, n, error;
 
        va_start(ap, arg);
        n = 1;
        while (va_arg(ap, char *) != NULL)
                n++;
        va_end(ap);
-       argv = alloca((n + 1) * sizeof(*argv));
-       if (argv == NULL) {
-               errno = ENOMEM;
+       maplen = (n + 1) * sizeof(*argv);
+       argv = mmap(NULL, maplen, PROT_WRITE|PROT_READ,
+           MAP_ANON|MAP_PRIVATE, -1, 0);
+       if (argv == MAP_FAILED)
                return (-1);
-       }
        va_start(ap, arg);
        n = 1;
        argv[0] = (char *)arg;
        while ((argv[n] = va_arg(ap, char *)) != NULL)
                n++;
        va_end(ap);
-       return (execve(name, argv, environ));
+
+       error = execve(name, argv, environ);
+       save_errno = errno;
+       munmap(argv, maplen);
+       errno = save_errno;
+       return (error);
 }
 DEF_WEAK(execl);
 
@@ -72,18 +79,21 @@ execle(const char *name, const char *arg
 {
        va_list ap;
        char **argv, **envp;
-       int n;
+       size_t maplen;
+       int save_errno, n, error;
 
        va_start(ap, arg);
        n = 1;
        while (va_arg(ap, char *) != NULL)
                n++;
        va_end(ap);
-       argv = alloca((n + 1) * sizeof(*argv));
-       if (argv == NULL) {
-               errno = ENOMEM;
+
+       maplen = (n + 1) * sizeof(*argv);
+       argv = mmap(NULL, maplen, PROT_WRITE|PROT_READ,
+           MAP_ANON|MAP_PRIVATE, -1, 0);
+       if (argv == MAP_FAILED)
                return (-1);
-       }
+
        va_start(ap, arg);
        n = 1;
        argv[0] = (char *)arg;
@@ -91,7 +101,12 @@ execle(const char *name, const char *arg
                n++;
        envp = va_arg(ap, char **);
        va_end(ap);
-       return (execve(name, argv, envp));
+
+       error = execve(name, argv, envp);
+       save_errno = errno;
+       munmap(argv, maplen);
+       errno = save_errno;
+       return error;
 }
 
 int
@@ -99,25 +114,32 @@ execlp(const char *name, const char *arg
 {
        va_list ap;
        char **argv;
-       int n;
+       size_t maplen;
+       int save_errno, n, error;
 
        va_start(ap, arg);
        n = 1;
        while (va_arg(ap, char *) != NULL)
                n++;
        va_end(ap);
-       argv = alloca((n + 1) * sizeof(*argv));
-       if (argv == NULL) {
-               errno = ENOMEM;
+
+       maplen = (n + 1) * sizeof(*argv);
+       argv = mmap(NULL, maplen, PROT_WRITE|PROT_READ,
+           MAP_ANON|MAP_PRIVATE, -1, 0);
+       if (argv == MAP_FAILED)
                return (-1);
-       }
+
        va_start(ap, arg);
        n = 1;
        argv[0] = (char *)arg;
        while ((argv[n] = va_arg(ap, char *)) != NULL)
                n++;
        va_end(ap);
-       return (execvp(name, argv));
+       error = execvp(name, argv);
+       save_errno = errno;
+       munmap(argv, maplen);
+       errno = save_errno;
+       return error;
 }
 
 int
@@ -132,10 +154,12 @@ execvpe(const char *name, char *const *a
 {
        char **memp;
        int cnt;
-       size_t lp, ln, len;
+       size_t lp, ln, curlen;
        char *p;
        int eacces = 0;
        char *bp, *cur, *path, buf[PATH_MAX];
+       size_t maplen;
+       int save_errno, n;
 
        /*
         * Do not allow null name
@@ -156,13 +180,14 @@ execvpe(const char *name, char *const *a
        /* Get the path we're searching. */
        if (!(path = getenv("PATH")))
                path = _PATH_DEFPATH;
-       len = strlen(path) + 1;
-       cur = alloca(len);
-       if (cur == NULL) {
-               errno = ENOMEM;
+
+       curlen = strlen(path) + 1;
+       cur = mmap(NULL, curlen, PROT_WRITE|PROT_READ,
+           MAP_ANON|MAP_PRIVATE, -1, 0);
+       if (cur == MAP_FAILED)
                return (-1);
-       }
-       strlcpy(cur, path, len);
+
+       strlcpy(cur, path, curlen);
        path = cur;
        while ((p = strsep(&cur, ":"))) {
                /*
@@ -210,13 +235,20 @@ retry:            (void)execve(bp, argv, envp);
                case ENOEXEC:
                        for (cnt = 0; argv[cnt]; ++cnt)
                                ;
-                       memp = alloca((cnt + 2) * sizeof(char *));
-                       if (memp == NULL)
+
+                       maplen = (cnt + 2) * sizeof(char *);
+                       memp = mmap(NULL, maplen, PROT_WRITE|PROT_READ,
+                           MAP_ANON|MAP_PRIVATE, -1, 0);
+                       if (memp == MAP_FAILED)
                                goto done;
+
                        memp[0] = "sh";
                        memp[1] = bp;
                        bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
                        (void)execve(_PATH_BSHELL, memp, envp);
+                       save_errno = errno;
+                       munmap(memp, maplen);
+                       errno = save_errno;
                        goto done;
                case ENOMEM:
                        goto done;
@@ -239,6 +271,11 @@ retry:             (void)execve(bp, argv, envp);
        else if (!errno)
                errno = ENOENT;
 done:
+       if (cur) {
+               save_errno = errno;
+               munmap(cur, curlen);
+               errno = save_errno;
+       }
        return (-1);
 }
 DEF_WEAK(execvpe);

Reply via email to