sh: Fix EINTR race condition in "wait" and "set -T" using sigsuspend().
When waiting for child processes using "wait" or if "set -T" is in effect, a signal interrupts the wait. Make sure there is no window where the signal handler may be invoked (setting a flag) just before going to sleep. There is a similar race condition in the shell language, but scripts can avoid it by exiting from the trap handler or enforcing synchronization using a fifo. If SIGCHLD is not trapped, a signal handler must be installed for it. Only install this handler for the duration of the wait to avoid triggering unexpected [EINTR] errors elsewhere. Note that for some reason only SIGINT and SIGQUIT interrupt a "wait" command. This remains the case.
This commit is contained in:
parent
767a02fb40
commit
1794251add
@ -87,6 +87,10 @@ int in_waitcmd = 0; /* are we in waitcmd()? */
|
||||
volatile sig_atomic_t breakwaitcmd = 0; /* should wait be terminated? */
|
||||
static int ttyfd = -1;
|
||||
|
||||
/* mode flags for dowait */
|
||||
#define DOWAIT_BLOCK 0x1 /* wait until a child exits */
|
||||
#define DOWAIT_SIG 0x2 /* if DOWAIT_BLOCK, abort on signals */
|
||||
|
||||
#if JOBS
|
||||
static void restartjob(struct job *);
|
||||
#endif
|
||||
@ -518,7 +522,7 @@ waitcmd(int argc, char **argv)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (dowait(1, (struct job *)NULL) != -1);
|
||||
} while (dowait(DOWAIT_BLOCK | DOWAIT_SIG, (struct job *)NULL) != -1);
|
||||
in_waitcmd--;
|
||||
|
||||
return 0;
|
||||
@ -965,7 +969,7 @@ waitforjob(struct job *jp, int *origstatus)
|
||||
INTOFF;
|
||||
TRACE(("waitforjob(%%%td) called\n", jp - jobtab + 1));
|
||||
while (jp->state == 0)
|
||||
if (dowait(1, jp) == -1)
|
||||
if (dowait(DOWAIT_BLOCK | (Tflag ? DOWAIT_SIG : 0), jp) == -1)
|
||||
dotrap();
|
||||
#if JOBS
|
||||
if (jp->jobctl) {
|
||||
@ -1003,14 +1007,20 @@ waitforjob(struct job *jp, int *origstatus)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dummy_handler(int sig)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for a process to terminate.
|
||||
*/
|
||||
|
||||
static pid_t
|
||||
dowait(int block, struct job *job)
|
||||
dowait(int mode, struct job *job)
|
||||
{
|
||||
struct sigaction sa, osa;
|
||||
sigset_t mask, omask;
|
||||
pid_t pid;
|
||||
int status;
|
||||
struct procstat *sp;
|
||||
@ -1021,8 +1031,22 @@ dowait(int block, struct job *job)
|
||||
int sig;
|
||||
int coredump;
|
||||
int wflags;
|
||||
int restore_sigchld;
|
||||
|
||||
TRACE(("dowait(%d) called\n", block));
|
||||
restore_sigchld = 0;
|
||||
if ((mode & DOWAIT_SIG) != 0) {
|
||||
sigfillset(&mask);
|
||||
sigprocmask(SIG_BLOCK, &mask, &omask);
|
||||
INTOFF;
|
||||
if (!issigchldtrapped()) {
|
||||
restore_sigchld = 1;
|
||||
sa.sa_handler = dummy_handler;
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGCHLD, &sa, &osa);
|
||||
}
|
||||
}
|
||||
do {
|
||||
#if JOBS
|
||||
if (iflag)
|
||||
@ -1030,13 +1054,25 @@ dowait(int block, struct job *job)
|
||||
else
|
||||
#endif
|
||||
wflags = 0;
|
||||
if (block == 0)
|
||||
if ((mode & (DOWAIT_BLOCK | DOWAIT_SIG)) != DOWAIT_BLOCK)
|
||||
wflags |= WNOHANG;
|
||||
pid = wait3(&status, wflags, (struct rusage *)NULL);
|
||||
TRACE(("wait returns %d, status=%d\n", (int)pid, status));
|
||||
if (pid == 0 && (mode & DOWAIT_SIG) != 0) {
|
||||
sigsuspend(&omask);
|
||||
pid = -1;
|
||||
if (int_pending())
|
||||
break;
|
||||
}
|
||||
} while (pid == -1 && errno == EINTR && breakwaitcmd == 0);
|
||||
if (pid == -1 && errno == ECHILD && job != NULL)
|
||||
job->state = JOBDONE;
|
||||
if ((mode & DOWAIT_SIG) != 0) {
|
||||
if (restore_sigchld)
|
||||
sigaction(SIGCHLD, &osa, NULL);
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
INTON;
|
||||
}
|
||||
if (breakwaitcmd != 0) {
|
||||
breakwaitcmd = 0;
|
||||
if (pid <= 0)
|
||||
|
@ -368,6 +368,14 @@ ignoresig(int signo)
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
issigchldtrapped(void)
|
||||
{
|
||||
|
||||
return (trap[SIGCHLD] != NULL && *trap[SIGCHLD] != '\0');
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Signal handler.
|
||||
*/
|
||||
|
@ -41,6 +41,7 @@ void clear_traps(void);
|
||||
int have_traps(void);
|
||||
void setsignal(int);
|
||||
void ignoresig(int);
|
||||
int issigchldtrapped(void);
|
||||
void onsig(int);
|
||||
void dotrap(void);
|
||||
void setinteractive(int);
|
||||
|
Loading…
x
Reference in New Issue
Block a user