bug-bash
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]