[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] Use SIGCONT to avoid fg tcsetpgrp() SIGTTIN/SIGTTOU race
From: |
Earl Chew |
Subject: |
[PATCH] Use SIGCONT to avoid fg tcsetpgrp() SIGTTIN/SIGTTOU race |
Date: |
Fri, 25 Aug 2023 17:59:02 -0700 |
A running background job can be stopped by SIGTTOU or SIGTTIN,
just as the fg command issues tcsetprgp() to give the job
the controlling terminal. This causes the fg command to appear
to have caused the job to stop.
Prevent this from happening by always issuing SIGCONT
after the fg command calls tcsetpgrp().
The following script reproduces the issue with the fg command
returning status 149 (SIGTTIN 21):
+ sleep 0.0008305541
+ sleep 0.00062593
+ sleep 0.004898882
+ jobs
[1]- Running ( sleep ${1}1; read X ) &
[2]+ Running ( sleep ${3}2; kill $! ) &
+ RC=0
+ read X
+ fg %1
( sleep ${1}1; read X )
+ RC=149
+ '[' 149 = 143 -o 149 = 1 -o 149 = 2 ']'
+ jobs
[1]+ Stopped ( sleep ${1}1; read X )
[2]- Running ( sleep ${3}2; kill $! ) &
+ exit 1
#!/bin/sh
set -mbux
rand()
{
set -- $(( $( od -An -N1 -t u1 /dev/random ) % 5 + 6 ))
printf "%0$1u" $(( $(od -An -N4 -t u4 /dev/random) % 1000000 ))
}
race()
{
set -- $1 $2 $(rand)
set -- $1 $2 0.$3
rm -f READ
( sleep ${1}1 ; read X ) &
( sleep ${3}2 ; kill $! ) &
sleep ${2}3
jobs
RC=0
fg %1 || RC=$?
#
# bash fg returns 1, and dash fg can return 2, if
# the job has already terminated.
#
[ $RC = 143 -o $RC = 1 -o $RC = 2 ] || {
#ps wwwaxjfh
jobs
exit 1
}
wait %1
wait %2
wait %3
}
main()
{
set -- ${1-0} ${2-0}
while : ; do
set -- ${1%.*} ${2%.*}
set -- "$@" $(rand)
set -- "$@" $(rand)
set -- $1.$3 $2.$4
race "$@"
done
}
main "$@"
Signed-off-by: Earl Chew <earl_chew@yahoo.com>
---
jobs.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/jobs.c b/jobs.c
index 6b986ed7..e11b1a9f 100644
--- a/jobs.c
+++ b/jobs.c
@@ -3543,6 +3543,7 @@ start_job (job, foreground)
{
register PROCESS *p;
int already_running;
+ int jump_start;
sigset_t set, oset;
char *wd, *s;
static TTYSTRUCT save_stty;
@@ -3564,6 +3565,7 @@ start_job (job, foreground)
}
already_running = RUNNING (job);
+ jump_start = !already_running;
if (foreground == 0 && already_running)
{
@@ -3616,7 +3618,10 @@ start_job (job, foreground)
/* Run the job. */
if (already_running == 0)
- set_job_running (job);
+ {
+ set_job_running (job);
+ jobs[job]->flags |= J_NOTIFIED;
+ }
/* Save the tty settings before we start the job in the foreground. */
if (foreground)
@@ -3625,17 +3630,18 @@ start_job (job, foreground)
save_stty = shell_tty_info;
/* Give the terminal to this job. */
if (IS_JOBCONTROL (job))
- give_terminal_to (jobs[job]->pgrp, 0);
+ {
+ give_terminal_to (jobs[job]->pgrp, 0);
+ jump_start = 1;
+ }
}
else
jobs[job]->flags &= ~J_FOREGROUND;
- /* If the job is already running, then don't bother jump-starting it. */
- if (already_running == 0)
- {
- jobs[job]->flags |= J_NOTIFIED;
- killpg (jobs[job]->pgrp, SIGCONT);
- }
+ /* Jump start if not running. Also jump start if running as foreground
+ via job control to avoid racing with SIGTTOU and SIGTTIN signals. */
+ if (jump_start)
+ killpg (jobs[job]->pgrp, SIGCONT);
if (foreground)
{
--
2.39.1
- [PATCH] Use SIGCONT to avoid fg tcsetpgrp() SIGTTIN/SIGTTOU race,
Earl Chew <=