[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
the test shell script never returns to shell
From: |
Wenlin Kang |
Subject: |
the test shell script never returns to shell |
Date: |
Wed, 25 Oct 2023 11:14:10 +0800 |
User-agent: |
Mozilla Thunderbird |
Hi
I want to report a bug, it rarely observes the problem while running
shell script aborting test repeatedly.
At the problem, the test shell script never returns to shell.
1. Version:
bash-5.1.16(but I also checked the latest codes, I think it still
exist the problem)
2. Steps to reproduce:
run test.sh file and ctrl-c repeatedly
3. Analysis:
During the progress, bash register a SIGINT handler
wait_sigint_handler() in addition to original SIGINT handler of bash.
At the registration, the original SIGINT handler saved to
old_sigint_handler pointer.(See wait_for())
When SIGINT happens by ctrl-c, wait_sigint_handler() will be invoked
first. the wait_sigint_handler() restores the original SIGINT handler
from old_sigint_handler pointer as primary SIGINT handler by
restore_signal_handler(), then send kill(SIGINT) to itself.
As a consequence, the original handler will be invoked. (See
wait_sigint_handler())
According to the ours debugging and analysis, there is a small window in
wait_for() which causes the problem.
If SIGINT happens during the window, the original SIGINT handler is
never be saved to old_sigint_handler.
So, restore_sigint_handler() will do nothing. Sending kill(SIGINT) to
itself just invokes itself(wait_sigint_handler) again.
The restore_sigint_handler() and kill(SIGINT) cycle will never be
stopped. Therefore the shell script never return to shell.
<bash-5.1.16/jobs.c>
int
wait_for (pid, flags)
pid_t pid;
int flags;
{
...
/* This is possibly a race condition – should it go in stop_pipeline? */
wait_sigint_received = child_caught_sigint = 0;
if (job_control == 0 || (subshell_environment&SUBSHELL_COMSUB))
{
SigHandler *temp_sigint_handler;
temp_sigint_handler = set_signal_handler (SIGINT,
wait_sigint_handler);
/* Small window begin. added by the reporter */
if (temp_sigint_handler == wait_sigint_handler)
{
#if defined (DEBUG)
internal_warning ("wait_for: recursively setting
old_sigint_handler to wait_sigint_handler: running_trap = %d",
running_trap);
#endif
}
else
/* Small window end. added by the reporter */
old_sigint_handler = temp_sigint_handler;
waiting_for_child = 0;
if (old_sigint_handler == SIG_IGN)
set_signal_handler (SIGINT, old_sigint_handler);
}
...
}
static sighandler
wait_sigint_handler (sig)
int sig;
{
...
/* XXX - should this be interrupt_state? If it is, the shell will act
as if it got the SIGINT interrupt. */
if (waiting_for_child)
wait_sigint_received = 1;
else
{
set_exit_status (128+SIGINT);
restore_sigint_handler();
kill (getpid (), SIGINT);
}
...
}
static void
restore_sigint_handler ()
{
if (old_sigint_handler != INVALID_SIGNAL_HANDLER)
{
set_signal_handler (SIGINT, old_sigint_handler);
old_sigint_handler = INVALID_SIGNAL_HANDLER;
waiting_for_child = 0;
}
}
4. To reproduce rapidly it, you can apply the debug patch.
diff --git a/bash-5.1.16/jobs.c b/bash-5.1.16/jobs.c
index 7c3b6e8..1b4330b 100644
--- a/bash-5.1.16/jobs.c
+++ b/bash-5.1.16/jobs.c
@@ -2941,8 +2941,10 @@ wait_for (pid, flags)
internal_warning ("wait_for: recursively setting
old_sigint_handler to wait_sigint_handler: running_trap = %d",
running_trap);
#endif
}
- else
+ else{
+ sleep(1); /* Add this */
old_sigint_handler = temp_sigint_handler;
+ }
waiting_for_child = 0;
if (old_sigint_handler == SIG_IGN)
set_signal_handler (SIGINT, old_sigint_handler);
5. test.sh file:
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/local/diag;
export PATH
max=4
for ((i=0; i < $max; i++)); do
echo "############### start $i ###########"
eval "ls -liR /usr" 2>&1
echo "################ end of $1 ############"
echo
done
--
Thanks,
Wenlin Kang
- the test shell script never returns to shell,
Wenlin Kang <=