From: Koral Ilgun <[email protected]> exit () runs wait_for_proc_all (), the atexit handler that reaps the last commands. When one of them was killed by a signal or exited with status 255, wait_for_proc () called exit () again. POSIX leaves a second call undefined, and musl 1.2.6 traps it, so xargs died with SIGSEGV instead of an exit with status 125 or 124. This happens with and without -P.
wait_for_proc_all () is the only caller that passes ALL, and it already ends with _exit () when the status changed, so wait_for_proc () now leaves the exit to it there. * xargs/xargs.c (wait_for_proc): Do not call exit () when ALL is set. Signed-off-by: Koral Ilgun <[email protected]> --- Tested on glibc with an LD_PRELOAD shim that traps exit () from an atexit handler, as musl 1.2.6 does. Unpatched master died with SIGSEGV when a command exited with 255 or was killed by SIGTERM, with -P2 and without -P. With this change xargs exits with 124 and 125 in those cases, and statuses 123 and 0 are unchanged. On an arm64 router with musl 1.2.6, the unpatched xargs crashed the same way; the device ran an earlier form of this fix, which kept a flag instead of testing ALL. The test suite was not run, because this host lacks the autotools to bootstrap from git. xargs/xargs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xargs/xargs.c b/xargs/xargs.c index d7d0ae88..3a18559a 100644 --- a/xargs/xargs.c +++ b/xargs/xargs.c @@ -1768,7 +1768,10 @@ wait_for_proc (bool all, unsigned int minreap) child_error = deferred_exit_status > child_error ? deferred_exit_status : child_error; - exit (child_error); + /* With ALL, the atexit handler wait_for_proc_all () is the caller, + and a second exit () is undefined. It ends with _exit (). */ + if (!all) + exit (child_error); } } -- 2.43.0
