f0fbdf1f4f
Some environments in which execvPe may be called have a limited amount of stack available. Currently, it avoidably allocates a segment on the stack large enough to hold PATH so that it may be mutated and use strsep() for easy parsing. This logic is now rewritten to just operate on the immutable string passed in and do the necessary math to extract individual paths, since it will be copying out those segments to another buffer anyways and piecing them together with the name for a full path. Additional size is also needed for the stack in posix_spawnp(), because it may need to push all of argv to the stack and rebuild the command with sh in front of it. We'll make sure it's properly aligned for the new thread, but future work should likely make rfork_thread a little easier to use by ensuring proper alignment. Some trivial cleanup has been done with a couple of error writes, moving strings into char arrays for use with the less fragile sizeof(). Reported by: Andrew Gierth <andrew_tao173.riddles.org.uk> Reviewed by: jilles, kib, Andrew Gierth MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D25038
307 lines
7.0 KiB
C
307 lines
7.0 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Copyright (c) 1991, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__SCCSID("@(#)exec.c 8.1 (Berkeley) 6/4/93");
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "namespace.h"
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <paths.h>
|
|
|
|
#include <stdarg.h>
|
|
#include "un-namespace.h"
|
|
#include "libc_private.h"
|
|
|
|
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, ...)
|
|
{
|
|
va_list ap;
|
|
const char **argv;
|
|
int n;
|
|
|
|
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;
|
|
return (-1);
|
|
}
|
|
va_start(ap, arg);
|
|
n = 1;
|
|
argv[0] = arg;
|
|
while ((argv[n] = va_arg(ap, char *)) != NULL)
|
|
n++;
|
|
va_end(ap);
|
|
return (_execve(name, __DECONST(char **, argv), environ));
|
|
}
|
|
|
|
int
|
|
execle(const char *name, const char *arg, ...)
|
|
{
|
|
va_list ap;
|
|
const char **argv;
|
|
char **envp;
|
|
int n;
|
|
|
|
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;
|
|
return (-1);
|
|
}
|
|
va_start(ap, arg);
|
|
n = 1;
|
|
argv[0] = arg;
|
|
while ((argv[n] = va_arg(ap, char *)) != NULL)
|
|
n++;
|
|
envp = va_arg(ap, char **);
|
|
va_end(ap);
|
|
return (_execve(name, __DECONST(char **, argv), envp));
|
|
}
|
|
|
|
int
|
|
execlp(const char *name, const char *arg, ...)
|
|
{
|
|
va_list ap;
|
|
const char **argv;
|
|
int n;
|
|
|
|
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;
|
|
return (-1);
|
|
}
|
|
va_start(ap, arg);
|
|
n = 1;
|
|
argv[0] = arg;
|
|
while ((argv[n] = va_arg(ap, char *)) != NULL)
|
|
n++;
|
|
va_end(ap);
|
|
return (execvp(name, __DECONST(char **, argv)));
|
|
}
|
|
|
|
int
|
|
execv(const char *name, char * const *argv)
|
|
{
|
|
(void)_execve(name, argv, environ);
|
|
return (-1);
|
|
}
|
|
|
|
int
|
|
execvp(const char *name, char * const *argv)
|
|
{
|
|
return (_execvpe(name, argv, environ));
|
|
}
|
|
|
|
static int
|
|
execvPe(const char *name, const char *path, char * const *argv,
|
|
char * const *envp)
|
|
{
|
|
const char **memp;
|
|
size_t cnt, lp, ln;
|
|
int eacces, save_errno;
|
|
char buf[MAXPATHLEN];
|
|
const char *bp, *np, *op, *p;
|
|
struct stat sb;
|
|
|
|
eacces = 0;
|
|
|
|
/* If it's an absolute or relative path name, it's easy. */
|
|
if (strchr(name, '/')) {
|
|
bp = name;
|
|
op = NULL;
|
|
goto retry;
|
|
}
|
|
bp = buf;
|
|
|
|
/* If it's an empty path name, fail in the usual POSIX way. */
|
|
if (*name == '\0') {
|
|
errno = ENOENT;
|
|
return (-1);
|
|
}
|
|
|
|
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 (np == op) {
|
|
/* Empty component. */
|
|
p = ".";
|
|
lp = 1;
|
|
} 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
|
|
* security issue; given a way to make the path too long
|
|
* the user may execute the wrong program.
|
|
*/
|
|
if (lp + ln + 2 > sizeof(buf)) {
|
|
(void)_write(STDERR_FILENO, execvPe_err_preamble,
|
|
sizeof(execvPe_err_preamble) - 1);
|
|
(void)_write(STDERR_FILENO, p, lp);
|
|
(void)_write(STDERR_FILENO, execvPe_err_trailer,
|
|
sizeof(execvPe_err_trailer) - 1);
|
|
continue;
|
|
}
|
|
bcopy(p, buf, lp);
|
|
buf[lp] = '/';
|
|
bcopy(name, buf + lp + 1, ln);
|
|
buf[lp + ln + 1] = '\0';
|
|
|
|
retry: (void)_execve(bp, argv, envp);
|
|
switch (errno) {
|
|
case E2BIG:
|
|
goto done;
|
|
case ELOOP:
|
|
case ENAMETOOLONG:
|
|
case ENOENT:
|
|
break;
|
|
case ENOEXEC:
|
|
for (cnt = 0; argv[cnt]; ++cnt)
|
|
;
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
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;
|
|
case ENOMEM:
|
|
goto done;
|
|
case ENOTDIR:
|
|
break;
|
|
case ETXTBSY:
|
|
/*
|
|
* We used to retry here, but sh(1) doesn't.
|
|
*/
|
|
goto done;
|
|
default:
|
|
/*
|
|
* EACCES may be for an inaccessible directory or
|
|
* a non-executable file. Call stat() to decide
|
|
* which. This also handles ambiguities for EFAULT
|
|
* and EIO, and undocumented errors like ESTALE.
|
|
* We hope that the race for a stat() is unimportant.
|
|
*/
|
|
save_errno = errno;
|
|
if (stat(bp, &sb) != 0)
|
|
break;
|
|
if (save_errno == EACCES) {
|
|
eacces = 1;
|
|
continue;
|
|
}
|
|
errno = save_errno;
|
|
goto done;
|
|
}
|
|
}
|
|
if (eacces)
|
|
errno = EACCES;
|
|
else
|
|
errno = ENOENT;
|
|
done:
|
|
return (-1);
|
|
}
|
|
|
|
int
|
|
execvP(const char *name, const char *path, char * const argv[])
|
|
{
|
|
return execvPe(name, path, argv, environ);
|
|
}
|
|
|
|
int
|
|
_execvpe(const char *name, char * const argv[], char * const envp[])
|
|
{
|
|
const char *path;
|
|
|
|
/* Get the path we're searching. */
|
|
if ((path = getenv("PATH")) == NULL)
|
|
path = _PATH_DEFPATH;
|
|
|
|
return (execvPe(name, path, argv, envp));
|
|
}
|