execvpe limits PATH environment variable to PATH_MAX

Zack Weinberg zackw@panix.com
Mon Mar 30 18:10:52 GMT 2020


On Mon, Mar 30, 2020 at 1:23 PM Adhemerval Zanella via Libc-alpha
<libc-alpha@sourceware.org> wrote:
> On 30/03/2020 13:57, Nils Andre wrote:
> > Regardless of the whether the limit is narrow or not, I find that
> > setting a hard limit for `PATH` to be unintuitive as there is no reason
> > to assume that `PATH` will fall under a certain limit. Even more so,
> > when using `PATH_MAX` (as the limit) because it is unrelated.
> >
> > Would it be possible to know the reason for this limit?
>
> The change was to make it semantically similar to execl and execle
> where POSIX requires to be async-signal-safe.  It also fixes an
> possible issue when used internally by posix_spawn, since the
> helper process that eventually spawns the new process is created
> with CLONE_VM.
>
> And I don't think it would be a good practice to use a different
> semantic for execvpe, i.e, allow arbitrary size paths.  It would
> require either dynamic allocation or an unbounded static
> allocation (such some systems do, for instance FreeBSD).

I can see why execvpe needs to impose a limit on the length of *one
element* of the $PATH list, but not why it needs to impose a limit on
the length of *the entire list*.  Regardless of the length of $PATH,
code vaguely like this should work, ne?

execvpe(const char *program, const char **argv, const char **envp)
{
    size_t proglen = strlen(program);
    char candidate[PATH_MAX];
    const char *path = getenv("PATH");
    const char *p = path, *q;
    while (*p) {
        q = p;
        while (*q && *q != ':') q++;
        size_t dirlen = q - p + 1;
        size_t needed = dirlen + proglen + 1;
        if (needed > PATH_MAX) {
            errno = ENAMETOOLONG;
            return -1;
        }

        memcpy(candidate, p, dirlen);
        candidate[dirlen] = '/';
        memcpy(candidate + dirlen + 1, program, proglen + 1);
        execve(candidate, argv, envp);
        if (errno != ENOENT && errno != ENOTDIR) {
            return -1;
        }
    }
    return -1;
}

(Completely untested, several corner cases ignored, may contain
off-by-one errors.)

zw


More information about the Libc-alpha mailing list