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


Reply via email to