diff options
Diffstat (limited to 'libc/unistd/exec.c')
-rw-r--r-- | libc/unistd/exec.c | 94 |
1 files changed, 57 insertions, 37 deletions
diff --git a/libc/unistd/exec.c b/libc/unistd/exec.c index 11807a8a7..1006ac3c4 100644 --- a/libc/unistd/exec.c +++ b/libc/unistd/exec.c @@ -42,47 +42,66 @@ libc_hidden_proto(munmap) libc_hidden_proto(getenv) /**********************************************************************/ -#if defined(__ARCH_USE_MMU__) || defined(__UCLIBC_UCLINUX_BROKEN_MUNMAP__) +#define EXEC_FUNC_COMMON 0 +#define EXEC_FUNC_EXECVP 1 +#if defined(__ARCH_USE_MMU__) -/* We have an MMU, so use alloca() to grab space for buffers and - * arg lists. Also fall back to alloca() if munmap() is broken. */ +/* We have an MMU, so use alloca() to grab space for buffers and arg lists. */ -# define EXEC_ALLOC_SIZE(VAR) /* nothing to do */ -# define EXEC_ALLOC(SIZE,VAR) alloca((SIZE)) -# define EXEC_FREE(PTR,VAR) ((void)0) +# define EXEC_ALLOC_SIZE(VAR) /* nothing to do */ +# define EXEC_ALLOC(SIZE,VAR,FUNC) alloca((SIZE)) +# define EXEC_FREE(PTR,VAR) ((void)0) #else -/* We do not have an MMU, so using alloca() is not an option. - * Less obviously, using malloc() is not an option either since - * malloc()ed memory can leak in a vfork() and exec*() situation. - * Therefore, we must use mmap() and unmap() directly, caching - * the result as we go. This way we minimize the leak to 1 - * allocation. +/* We do not have an MMU, so using alloca() is not an option (as this will + * easily overflow the stack in most setups). Less obviously, using malloc() + * is not an option either since malloc()ed memory can leak in from a vfork()ed + * child into the parent as no one is around after the child calls exec*() to + * free() the memory. Therefore, we must use mmap() and unmap() directly, + * caching the result as we go. This way we minimize the leak by reusing the + * memory with every call to an exec*(). + * + * To prevent recursive use of the same cached memory, we have to give execvp() + * its own cache. Here are the nested exec calls (a/-: alloc/no-alloc): + * execve(-) -> calls straight to kernel + * execl(a) -> execve(-) + * execlp(a) -> execvp(a) !! recursive usage !! + * execle(a) -> execve(-) + * execv(-) -> execve(-) + * execvp(a) -> execve(-) */ -# define EXEC_ALLOC_SIZE(VAR) size_t VAR; /* Semicolon included! */ -# define EXEC_ALLOC(SIZE,VAR) __exec_alloc((VAR = (SIZE))) -# define EXEC_FREE(PTR,VAR) ((void)0) +# define EXEC_ALLOC_SIZE(VAR) /* nothing to do */ +# define EXEC_ALLOC(SIZE,VAR,FUNC) __exec_alloc((SIZE), FUNC) +# define EXEC_FREE(PTR,VAR) ((void)0) -extern void *__exec_alloc(size_t size) attribute_hidden; +extern void *__exec_alloc(size_t size, int func) attribute_hidden; # ifdef L___exec_alloc -void attribute_hidden *__exec_alloc(size_t size) +void attribute_hidden *__exec_alloc(size_t size, int func) { - static void *p; - static size_t old_size; - - if (old_size >= size) - return p; - else if (p) - munmap(p, old_size); - - old_size = size; - p = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); - - return (p != MAP_FAILED) ? p : NULL; + static void *common_cache, *execvp_cache; + static size_t common_size, execvp_size; + + void **cache = (func ? &execvp_cache : &common_cache); + size_t *cache_size = (func ? &execvp_size : &common_size); + + if (*cache_size >= size) + return *cache; + else if (*cache) + munmap(*cache, *cache_size); + + *cache_size = size; + return *cache = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + + /* We don't actually handle OOM in the exec funcs ... + if (*cache != MAP_FAILED) + return *cache; + else + return (*cache = NULL); + */ } # endif @@ -98,7 +117,7 @@ int execl(const char *path, const char *arg, ...) char **argv; char **p; va_list args; - + n = 0; va_start(args, arg); do { @@ -106,7 +125,7 @@ int execl(const char *path, const char *arg, ...) } while (va_arg(args, char *)); va_end(args); - p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size); + p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size, EXEC_FUNC_COMMON); p[0] = (char *)arg; @@ -146,7 +165,7 @@ int execle(const char *path, const char *arg, ...) char **p; char *const *envp; va_list args; - + n = 0; va_start(args, arg); do { @@ -155,7 +174,7 @@ int execle(const char *path, const char *arg, ...) envp = va_arg(args, char *const *); /* Varies from execl and execlp. */ va_end(args); - p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size); + p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size, EXEC_FUNC_COMMON); p[0] = (char *)arg; @@ -184,7 +203,7 @@ int execlp(const char *file, const char *arg, ...) char **argv; char **p; va_list args; - + n = 0; va_start(args, arg); do { @@ -192,7 +211,7 @@ int execlp(const char *file, const char *arg, ...) } while (va_arg(args, char *)); va_end(args); - p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size); + p = argv = (char **) EXEC_ALLOC((n+1) * sizeof(char *), size, EXEC_FUNC_COMMON); p[0] = (char *)arg; @@ -248,7 +267,7 @@ int execvp(const char *path, char *const argv[]) /* Need the dimension - 1. We omit counting the trailing * NULL but we actually omit the first entry. */ for (n=0 ; argv[n] ; n++) {} - nargv = (char **) EXEC_ALLOC((n+2) * sizeof(char *), size2); + nargv = (char **) EXEC_ALLOC((n+2) * sizeof(char *), size2, EXEC_FUNC_EXECVP); nargv[0] = argv[0]; nargv[1] = (char *)path; memcpy(nargv+2, argv+1, n*sizeof(char *)); @@ -272,7 +291,8 @@ int execvp(const char *path, char *const argv[]) } len = (FILENAME_MAX - 1) - plen; - if ((buf = EXEC_ALLOC(FILENAME_MAX, size)) != NULL) { + buf = EXEC_ALLOC(FILENAME_MAX, size, EXEC_FUNC_EXECVP); + { int seen_small = 0; s0 = buf + len; memcpy(s0, path, plen+1); |