aboutsummaryrefslogtreecommitdiffstats
path: root/lib/libc/gen/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/gen/exec.c')
-rw-r--r--lib/libc/gen/exec.c67
1 files changed, 46 insertions, 21 deletions
diff --git a/lib/libc/gen/exec.c b/lib/libc/gen/exec.c
index e20ff42d3c90..0bd8be738dca 100644
--- a/lib/libc/gen/exec.c
+++ b/lib/libc/gen/exec.c
@@ -49,6 +49,9 @@ __FBSDID("$FreeBSD$");
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 @@ execvPe(const char *name, const char *path, char * const *argv,
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,7 @@ execvPe(const char *name, const char *path, char * const *argv,
/* If it's an absolute or relative path name, it's easy. */
if (strchr(name, '/')) {
bp = name;
- cur = NULL;
+ op = NULL;
goto retry;
}
bp = buf;
@@ -169,23 +172,30 @@ execvPe(const char *name, const char *path, char * const *argv,
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 == op) {
+ /* Empty component. */
p = ".";
lp = 1;
- } else
- lp = strlen(p);
- ln = strlen(name);
+ } else {
+ /* Non-empty component. */
+ p = op;
+ lp = np - op;
+ }
+
+ /* Advance to the next component or terminate after this. */
+ if (*np == '\0')
+ op = NULL;
+ else
+ op = np + 1;
/*
* If the path is too long complain. This is a possible
@@ -193,10 +203,11 @@ execvPe(const char *name, const char *path, char * const *argv,
* 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);
@@ -215,14 +226,28 @@ retry: (void)_execve(bp, argv, envp);
case ENOEXEC:
for (cnt = 0; argv[cnt]; ++cnt)
;
- memp = alloca((cnt + 2) * sizeof(char *));
+
+ /*
+ * cnt may be 0 above; always allocate at least
+ * 3 entries so that we can at least fit "sh", bp, and
+ * the NULL terminator. We can rely on cnt to take into
+ * account the NULL terminator in all other scenarios,
+ * as we drop argv[0].
+ */
+ memp = alloca(MAX(3, cnt + 2) * sizeof(char *));
if (memp == NULL) {
/* errno = ENOMEM; XXX override ENOEXEC? */
goto done;
}
- memp[0] = "sh";
- memp[1] = bp;
- bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
+ if (cnt > 0) {
+ memp[0] = argv[0];
+ memp[1] = bp;
+ bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
+ } else {
+ memp[0] = "sh";
+ memp[1] = bp;
+ memp[2] = NULL;
+ }
(void)_execve(_PATH_BSHELL,
__DECONST(char **, memp), envp);
goto done;