Index: lib/libc/gen/exec.c =================================================================== --- lib/libc/gen/exec.c +++ lib/libc/gen/exec.c @@ -49,6 +49,9 @@ extern char **environ; +static const char execvPe_err_preamble[] = "execvP: "; +static const char execvPe_err_trailer[] = ": path too long\n"; + int execl(const char *name, const char *arg, ...) { @@ -149,8 +152,8 @@ const char **memp; size_t cnt, lp, ln; int eacces, save_errno; - char *cur, buf[MAXPATHLEN]; - const char *p, *bp; + char buf[MAXPATHLEN]; + const char *bp, *np, *op, *p; struct stat sb; eacces = 0; @@ -158,7 +161,6 @@ /* If it's an absolute or relative path name, it's easy. */ if (strchr(name, '/')) { bp = name; - cur = NULL; goto retry; } bp = buf; @@ -169,23 +171,34 @@ return (-1); } - cur = alloca(strlen(path) + 1); - if (cur == NULL) { - errno = ENOMEM; - return (-1); - } - strcpy(cur, path); - while ((p = strsep(&cur, ":")) != NULL) { + op = path; + ln = strlen(name); + while (op != NULL) { + np = strchrnul(op, ':'); + /* * It's a SHELL path -- double, leading and trailing colons * mean the current directory. */ - if (*p == '\0') { + if (*np == '\0') { + if (op == np) { + /* Trailing colon. */ + p = "."; + lp = 1; + } else { + /* Final non-empty component. */ + p = op; + lp = strlen(p); + } + } else if (np == op) { + /* Double colon in the middle, or a leading colon. */ p = "."; lp = 1; - } else - lp = strlen(p); - ln = strlen(name); + } else { + /* Standard non-empty component. */ + p = op; + lp = np - op; + } /* * If the path is too long complain. This is a possible @@ -193,10 +206,11 @@ * the user may execute the wrong program. */ if (lp + ln + 2 > sizeof(buf)) { - (void)_write(STDERR_FILENO, "execvP: ", 8); + (void)_write(STDERR_FILENO, execvPe_err_preamble, + sizeof(execvPe_err_preamble) - 1); (void)_write(STDERR_FILENO, p, lp); - (void)_write(STDERR_FILENO, ": path too long\n", - 16); + (void)_write(STDERR_FILENO, execvPe_err_trailer, + sizeof(execvPe_err_trailer) - 1); continue; } bcopy(p, buf, lp); @@ -204,6 +218,12 @@ bcopy(name, buf + lp + 1, ln); buf[lp + ln + 1] = '\0'; + /* Advance */ + if (*np == '\0') + op = NULL; + else + op = np + 1; + retry: (void)_execve(bp, argv, envp); switch (errno) { case E2BIG: Index: lib/libc/gen/posix_spawn.c =================================================================== --- lib/libc/gen/posix_spawn.c +++ lib/libc/gen/posix_spawn.c @@ -246,8 +246,23 @@ pid_t p; #ifdef _RFORK_THREAD_STACK_SIZE char *stack; - - stack = malloc(_RFORK_THREAD_STACK_SIZE); + size_t cnt, stacksz; + + stacksz = _RFORK_THREAD_STACK_SIZE; + if (use_env_path) { + /* + * We need to make sure we have enough room on the stack for the + * potential alloca() in execvPe if it gets kicked back an + * ENOEXEC from execve(2), plus the original buffer we gave + * ourselves; this protects us in the event that the caller + * intentionally or inadvertently supplies enough arguments to + * make us blow past the stack we've allocated from it. + */ + stacksz += (2 * sizeof(char *)); + for (cnt = 0; argv[cnt] != NULL; ++cnt) + stacksz += sizeof(char *); + } + stack = malloc(stacksz); if (stack == NULL) return (ENOMEM); #endif @@ -273,8 +288,7 @@ * parent. Because of this, we must use rfork_thread instead while * almost every other arch stores the return address in a register. */ - p = rfork_thread(RFSPAWN, stack + _RFORK_THREAD_STACK_SIZE, - _posix_spawn_thr, &psa); + p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa); free(stack); #else p = rfork(RFSPAWN);