Current posix_spawnp implementation wrongly tries to execute invalid
binaries (for instance script without shebang) as a shell script in
non compat mode. It was a regression introduced by
9ff72da471 when __spawni started to use
__execvpe instead of __execve (glibc __execvpe try to execute ENOEXEC
as shell script regardless).
This patch fixes it by using an internal symbol (__execvpex) with the
faulty semantic (since compat mode is handled by spawni.c itself).
It was reported by Daniel Drake on libc-help [1].
Checked on x86_64-linux-gnu and i686-linux-gnu.
[BZ #23264]
* include/unistd.h (__execvpex): New prototype.
* posix/Makefile (tests): Add tst-spawn4.
(tests-internal): Add tst-spawn4-compat.
* posix/execvpe.c (__execvpe_common, __execvpex): New functions.
* posix/tst-spawn4-compat.c: New file.
* posix/tst-spawn4.c: Likewise.
* sysdeps/unix/sysv/linux/spawni.c (__spawni): Do not interpret invalid
binaries as shell scripts.
* sysdeps/posix/spawni.c (__spawni): Likewise.
[1] https://sourceware.org/ml/libc-help/2018-06/msg00012.html
Commit 6c9e1be87a wrongly fixes BZ#20847 by lefting the else branch
on maybe_script_execute to still being able to invalid write on stack
allocated buffer. It happens if execvp{e} is executed with an empty
arguments list ({ NULL }) and although manual states first argument
should be the script name itself, by convention, old and current
implementation allows it.
This patch fixes the issue by just account for arguments and not the
final 'NULL' (since the 'argv + 1' will indeed ignored the script name).
The empty argument list is handled in a special case with a minimum
allocated size. The patch also adds extra tests for such case in
tst-vfork3.
Tested on x86_64.
[BZ #20847]
* posix/execvpe.c (maybe_script_execute): Remove write past allocated
array bounds for else branch.
(__execvpe): Style fixes.
* posix/tst-vfork3.c (run_script): New function.
(create_script): Likewise.
(do_test): Use run_script internal function.
(do_prepare): Use create_script internal function.
This patch fixes an invalid write out or stack allocated buffer in
2 places at execvpe implementation:
1. On 'maybe_script_execute' function where it allocates the new
argument list and it does not account that a minimum of argc
plus 3 elements (default shell path, script name, arguments,
and ending null pointer) should be considered. The straightforward
fix is just to take account of the correct list size on argument
copy.
2. On '__execvpe' where the executable file name lenght may not
account for ending '\0' and thus subsequent path creation may
write past array bounds because it requires to add the terminating
null. The fix is to change how to calculate the executable name
size to add the final '\0' and adjust the rest of the code
accordingly.
As described in GCC bug report 78433 [1], these issues were masked off by
GCC because it allocated several bytes more than necessary so that many
off-by-one bugs went unnoticed.
Checked on x86_64 with a latest GCC (7.0.0 20161121) with -O3 on CFLAGS.
[BZ #20847]
* posix/execvpe.c (maybe_script_execute): Remove write past allocated
array bounds.
(__execvpe): Likewise.
[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78433
This patch removes all the dynamic allocation on execvpe code and
instead use direct stack allocation. This is QoI approach to make
it possible use in scenarios where memory is shared with parent
(vfork or clone with CLONE_VM).
For default process spawn (script file without a shebang), stack
allocation is bounded by NAME_MAX plus PATH_MAX plus 1. Large
file arguments returns an error (ENAMETOOLONG). This differs than
current GLIBC pratice in general, but it used to limit stack
allocation for large inputs. Also, path in PATH environment variable
larger than PATH_MAX are ignored.
The shell direct execution exeception, where execve returns ENOEXEC,
might requires a large stack allocation due large input argument list.
Tested on i686, x86_64, powerpc64le, and aarch64.
* posix/execvpe.c (__execvpe): Remove dynamic allocation.
* posix/Makefile (tests): Add tst-execvpe{1,2,3,4,5,6}.
* posix/tst-execvp1.c (do_test): Use a macro to call execvp.
* posix/tst-execvp2.c (do_test): Likewise.
* posix/tst-execvp3.c (do_test): Likewise.
* posix/tst-execvp4.c (do_test): Likewise.
* posix/tst-execvpe1.c: New file.
* posix/tst-execvpe2.c: Likewise.
* posix/tst-execvpe3.c: Likewise.
* posix/tst-execvpe4.c: Likewise.
* posix/tst-execvpe5.c: Likewise.
* posix/tst-execvpe6.c: Likewise.