execvpe limits PATH environment variable to PATH_MAX
Adhemerval Zanella
adhemerval.zanella@linaro.org
Mon Mar 30 18:27:14 GMT 2020
On 30/03/2020 15:10, Zack Weinberg wrote:
> 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.)
But it does do something similar to this, the allocated buffer is
the length of file plus the length PATH environment limited by
NAME_MAX and PATH_MAX respectively.
And the buffer is used to created the full path by iterating
on path in PATH each time. The only internal check it does it
that if the path within PATH is larger than PATH_MAX it skips
to next one.
More information about the Libc-alpha
mailing list