aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2020-09-29 20:35:55 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2021-01-01 14:30:58 +0100
commite7629ffbf5c697a0fa805e2439c278e019d84835 (patch)
treece0165773d1c957643b9c4968a180a3cafc998d0
parente4c7cf3f8cc67bd2b9aeb4d622c38bad515b09a0 (diff)
downloadbusybox-w32-e7629ffbf5c697a0fa805e2439c278e019d84835.tar.gz
busybox-w32-e7629ffbf5c697a0fa805e2439c278e019d84835.tar.bz2
busybox-w32-e7629ffbf5c697a0fa805e2439c278e019d84835.zip
ash: jobs: Fix waitcmd busy loop
Upstream commit: Date: Tue, 2 Jun 2020 23:46:48 +1000 jobs: Fix waitcmd busy loop We need to clear gotsigchld in waitproc because it is used as a loop conditional for the waitcmd case. Without it waitcmd may busy loop after a SIGCHLD. This patch also changes gotsigchld into a volatile sig_atomic_t to prevent compilers from optimising its accesses away. Fixes: 6c691b3e5099 ("jobs: Only clear gotsigchld when waiting...") Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> This change also incorporates other changes to bring us closer to upstream. function old new delta dowait 553 636 +83 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash.c91
1 files changed, 34 insertions, 57 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 13470b2fa..07aa2da2e 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -4273,64 +4273,55 @@ sprint_status48(char *os, int status, int sigonly)
4273 return s - os; 4273 return s - os;
4274} 4274}
4275 4275
4276#define DOWAIT_NONBLOCK 0
4277#define DOWAIT_BLOCK 1
4278#define DOWAIT_BLOCK_OR_SIG 2
4279#if BASH_WAIT_N
4280# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
4281#endif
4282
4276static int 4283static int
4277wait_block_or_sig(int *status) 4284waitproc(int block, int *status)
4278{ 4285{
4279 int pid; 4286 sigset_t oldmask;
4287 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4288 int err;
4280 4289
4281 do { 4290#if JOBS
4282 sigset_t mask; 4291 if (doing_jobctl)
4292 flags |= WUNTRACED;
4293#endif
4283 4294
4284 /* Poll all children for changes in their state */ 4295 do {
4285 got_sigchld = 0; 4296 got_sigchld = 0;
4286 /* if job control is active, accept stopped processes too */ 4297 do
4287 pid = waitpid(-1, status, doing_jobctl ? (WNOHANG|WUNTRACED) : WNOHANG); 4298 err = waitpid(-1, status, flags);
4288 if (pid != 0) 4299 while (err < 0 && errno == EINTR);
4289 break; /* Error (e.g. EINTR, ECHILD) or pid */
4290 4300
4291 /* Children exist, but none are ready. Sleep until interesting signal */ 4301 if (err || (err = -!block))
4292#if 1 4302 break;
4293 sigfillset(&mask); 4303
4294 sigprocmask2(SIG_SETMASK, &mask); /* mask is updated */ 4304 sigfillset(&oldmask);
4295 while (!got_sigchld && !pending_sig) { 4305 sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */
4296 sigsuspend(&mask);
4297 /* ^^^ add "sigdelset(&mask, SIGCHLD);" before sigsuspend
4298 * to make sure SIGCHLD is not masked off?
4299 * It was reported that this:
4300 * fn() { : | return; }
4301 * shopt -s lastpipe
4302 * fn
4303 * exec ash SCRIPT
4304 * under bash 4.4.23 runs SCRIPT with SIGCHLD masked,
4305 * making "wait" commands in SCRIPT block forever.
4306 */
4307 }
4308 sigprocmask(SIG_SETMASK, &mask, NULL);
4309#else /* unsafe: a signal can set pending_sig after check, but before pause() */
4310 while (!got_sigchld && !pending_sig) 4306 while (!got_sigchld && !pending_sig)
4311 pause(); 4307 sigsuspend(&oldmask);
4312#endif 4308 sigprocmask(SIG_SETMASK, &oldmask, NULL);
4309 //simpler, but unsafe: a signal can set pending_sig after check, but before pause():
4310 //while (!got_sigchld && !pending_sig)
4311 // pause();
4313 4312
4314 /* If it was SIGCHLD, poll children again */
4315 } while (got_sigchld); 4313 } while (got_sigchld);
4316 4314
4317 return pid; 4315 return err;
4318} 4316}
4319 4317
4320#define DOWAIT_NONBLOCK 0
4321#define DOWAIT_BLOCK 1
4322#define DOWAIT_BLOCK_OR_SIG 2
4323#if BASH_WAIT_N
4324# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
4325#endif
4326
4327static int 4318static int
4328waitone(int block, struct job *job) 4319waitone(int block, struct job *job)
4329{ 4320{
4330 int pid; 4321 int pid;
4331 int status; 4322 int status;
4332 struct job *jp; 4323 struct job *jp;
4333 struct job *thisjob; 4324 struct job *thisjob = NULL;
4334#if BASH_WAIT_N 4325#if BASH_WAIT_N
4335 bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS); 4326 bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS);
4336 block = (block & ~DOWAIT_JOBSTATUS); 4327 block = (block & ~DOWAIT_JOBSTATUS);
@@ -4357,21 +4348,8 @@ waitone(int block, struct job *job)
4357 * SIG_DFL handler does not wake sigsuspend(). 4348 * SIG_DFL handler does not wake sigsuspend().
4358 */ 4349 */
4359 INT_OFF; 4350 INT_OFF;
4360 if (block == DOWAIT_BLOCK_OR_SIG) { 4351 pid = waitproc(block, &status);
4361 pid = wait_block_or_sig(&status); 4352 TRACE(("wait returns pid %d, status=%d\n", pid, status));
4362 } else {
4363 int wait_flags = 0;
4364 if (block == DOWAIT_NONBLOCK)
4365 wait_flags = WNOHANG;
4366 /* if job control is active, accept stopped processes too */
4367 if (doing_jobctl)
4368 wait_flags |= WUNTRACED;
4369 /* NB: _not_ safe_waitpid, we need to detect EINTR */
4370 pid = waitpid(-1, &status, wait_flags);
4371 }
4372 TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
4373 pid, status, errno, strerror(errno)));
4374 thisjob = NULL;
4375 if (pid <= 0) 4353 if (pid <= 0)
4376 goto out; 4354 goto out;
4377 4355
@@ -4466,7 +4444,6 @@ dowait(int block, struct job *jp)
4466 rpid = 1; 4444 rpid = 1;
4467 4445
4468 do { 4446 do {
4469 got_sigchld = 0;
4470 pid = waitone(block, jp); 4447 pid = waitone(block, jp);
4471 rpid &= !!pid; 4448 rpid &= !!pid;
4472 4449
@@ -4721,7 +4698,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
4721 job = getjob(*argv, 0); 4698 job = getjob(*argv, 0);
4722 } 4699 }
4723 /* loop until process terminated or stopped */ 4700 /* loop until process terminated or stopped */
4724 dowait(DOWAIT_BLOCK_OR_SIG, NULL); 4701 dowait(DOWAIT_BLOCK_OR_SIG, job);
4725 if (pending_sig) 4702 if (pending_sig)
4726 goto sigout; 4703 goto sigout;
4727 job->waited = 1; 4704 job->waited = 1;