On 30/06/15 02:27, Chet Ramey wrote:
> On 6/29/15 3:41 PM, Patrick Plagwitz wrote:
> 
>> Bash Version: 4.3
>> Patch Level: 39
>> Release Status: release
>>
>> Description:
>> There's a bug that happens when waiting for a child process to complete
>> (via wait_for in jobs.c) that isn't part of a job, like command
>> substitution subshells. If a SIGINT is caught by the
>> wait_sigint_handler, the handler sets the wait_sigint_received flag
>> which is checked in set_job_status_and_cleanup. But
>> set_job_status_and_cleanup is called from waitchld only if the pid
>> belongs to a process that is part of a job. The result is that a SIGINT
>> trap, if it exists, is not called if a SIGINT is received while waiting
>> for a comsub subshell.
> 
> This isn't exactly correct.  The SIGINT trap will be called if the process
> in the command substitution exits due to SIGINT.  For instance, if you
> change the assignment to something like:
> 
> foo=$(sleep 20 >&- )
> or
> foo=$(exec 2>&- ; sleep 20)
> 
> both of which I think correctly reproduce the python command, the SIGINT
> trap will execute.
> 
> This is another case of the scenario most recently described in
> 
> http://lists.gnu.org/archive/html/bug-bash/2014-03/msg00108.html
> 
> In this case, python appears to catch the SIGINT (it looks like a
> KeyboardInterrupt exception), print the message, and exit with status 1.
> 
> Chet
> 

Ok, I see.
However, there appears to be some race condition when waiting for a
command substitution.
I have the attached combination of scripts.

When run with
$ bash run-until-end-of-loop.sh bash start-subst-loop.sh '2>/dev/null'
, the script will try to launch and SIGINT subst-loop.sh repeatedly
until the SIGINT trap is once *not* called which will happen in at most
200 repetitions on my machine. The script then ends after printing “end
of loop”. The python script execute-with-sigint.py is only there to
enable subst-loop.sh to receive a SIGINT at all.
subst-loop.sh calls date(1) in a loop; date should have SIGINT set to
SIG_DFL other than python initially.

I analyzed the execution by inserting some debug output into the bash
code. It seems that in the case that the SIGINT trap is not called
subst-loop.sh gets the SIGINT while (or shortly before or after) calling
waitpid in waitchld:jobs.c which will then return without errno ==
EINTR. The wait_sigint_handler will be called, though, and so
wait_sigint_received will be true.

If you try the script often enough, it will sometimes block. I saw that
this is because wait_sigint_handler calls itself in an infinite loop.
If I understand it correctly, this happens since wait_for:jobs.c calls
  old_sigint_handler = set_signal_handler (SIGINT, wait_sigint_handler);
and if a SIGINT is caught after sigaction in set_signal_handler was
called but before old_sigint_handler is set,
  restore_sigint_handler ();
  kill (getpid (), SIGINT);
in wait_sigint_handler will leave wait_sigint_handler as handler.

Attachment: run-until-end-of-loop.sh
Description: application/shellscript

Attachment: start-subst-loop.sh
Description: application/shellscript

import signal
import os
import sys

signal.signal(signal.SIGINT, signal.SIG_DFL)

sys.stderr.write(os.environ["SHELL"] + "\n")
os.execv(os.environ["SHELL"], [os.environ["SHELL"]] + sys.argv[1:])

Attachment: subst-loop.sh
Description: application/shellscript

Reply via email to