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);