execvp: fix up the ENOEXEC fallback


execvp: fix up the ENOEXEC fallback

If execve fails with ENOEXEC, execvp is expected to rebuild the command
with /bin/sh instead and try again.

The previous version did this, but overlooked two details:

argv[0] can conceivably be NULL, in which case memp would never get
terminated. We must allocate no less than three * sizeof(char *) so we can
properly terminate at all times. For the non-NULL argv standard case, we
count all the non-NULL elements and actually skip the first argument, so we
end up capturing the NULL terminator in our bcopy().

The second detail is that the spec is actually worded such that we should
have been preserving argv[0] as passed to execvp:

"[...] executed command shall be as if the process invoked the sh utility
using execl() as follows:

execl(<shell path>, arg0, file, arg1, ..., (char *)0);

where <shell path> is an unspecified pathname for the sh utility, file is
the process image file, and for execvp(), where arg0, arg1, and so on
correspond to the values passed to execvp() in argv[0], argv[1], and so on."

So we make this change at this time as well, while we're already touching
it. We decidedly can't preserve a NULL argv[0] as this would be incredibly,
incredibly fragile, so we retain our legacy behavior of using "sh" for
argv[] in this specific instance.

Some light tests are added to try and detect some components of handling the
ENOEXEC fallback; posix_spawnp_enoexec_fallback_null_argv0 is likely not
100% reliable, but it at least won't raise false-alarms and it did result in
useful failures with pre-change libc on my machine.

This is a secondary change in D25038.

Reported by: Andrew Gierth <andrew_tao173.riddles.org.uk>
Reviewed by: jilles, kib, Andrew Gierth
MFC after: 1 week


kevansJun 10 2020, 1:30 AM
rS361994: Add some default cases for unreachable code to silence compiler warnings.