From d81af7216b3305a1aac211dc847dd1c191f3b307 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 18 Feb 2020 14:28:30 +0100 Subject: [PATCH] ash: eval: Reap zombies after built-in commands and functions Upstream commit: Date: Mon, 26 Mar 2018 23:55:50 +0800 eval: Reap zombies after built-in commands and functions Currently dash does not reap dead children after built-in commands or functions. This means that if you construct a loop consisting of solely built-in commands and functions, then zombies can hang around indefinitely. This patch fixes this by reaping when necessary after each built-in command and function. Reported-by: Denys Vlasenko Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 389db3cd0..8047cf98f 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5355,10 +5355,10 @@ waitforjob(struct job *jp) { int st; - TRACE(("waitforjob(%%%d) called\n", jobno(jp))); + TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0)); INT_OFF; - while (jp->state == JOBRUNNING) { + while ((jp && jp->state == JOBRUNNING) || got_sigchld) { /* In non-interactive shells, we _can_ get * a keyboard signal here and be EINTRed, * but we just loop back, waiting for command to complete. @@ -5393,6 +5393,8 @@ waitforjob(struct job *jp) } INT_ON; + if (!jp) + return exitstatus; st = getstatus(jp); #if JOBS if (jp->jobctl) { @@ -10311,6 +10313,8 @@ evalcommand(union node *cmd, int flags) goto out; } + jp = NULL; + /* Execute the command. */ switch (cmdentry.cmdtype) { default: { @@ -10365,7 +10369,6 @@ evalcommand(union node *cmd, int flags) jp = makejob(/*cmd,*/ 1); if (forkshell(jp, cmd, FORK_FG) != 0) { /* parent */ - status = waitforjob(jp); INT_ON; TRACE(("forked child exited with %d\n", status)); break; @@ -10384,33 +10387,24 @@ evalcommand(union node *cmd, int flags) if (cmd_is_exec && argc > 1) listsetvar(varlist.list, VEXPORT); } - - /* Tight loop with builtins only: - * "while kill -0 $child; do true; done" - * will never exit even if $child died, unless we do this - * to reap the zombie and make kill detect that it's gone: */ - dowait(DOWAIT_NONBLOCK, NULL); - if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { if (exception_type == EXERROR && spclbltin <= 0) { FORCE_INT_ON; - goto readstatus; + break; } raise: longjmp(exception_handler->loc, 1); } - goto readstatus; + break; case CMDFUNCTION: - /* See above for the rationale */ - dowait(DOWAIT_NONBLOCK, NULL); if (evalfun(cmdentry.u.func, argc, argv, flags)) goto raise; - readstatus: - status = exitstatus; break; } /* switch */ + status = waitforjob(jp); + out: if (cmd->ncmd.redirect) popredir(/*drop:*/ cmd_is_exec); -- 2.25.1