bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Scripts passed into "-c" with an embedded newline won't execute trap if


From: Brian Vandenberg
Subject: Scripts passed into "-c" with an embedded newline won't execute trap if last command fails
Date: Thu, 21 Apr 2016 10:48:47 -0600

Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: Linux-gnu
Compiler: gcc
Compilation CFLAGS: -DPROGRAM='bash' -DCONF_HOSTTYPE='x86_64' -DCONF_OSTYPE='Linux-gnu' -DCONF_MACHTYPE='x86_64-redhat-linux-gnu' -DCONF_VENDOR='redhat' -DLOCALEDIR='/usr/share/locale' -DPACKAGE='bash' -DSHELL -DHAVE_CONFIG_H   -I.  -I. -I./include -I./lib  -D_GNU_SOURCE -DRECYCLES_PIDS  -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -fwrapv
uname output: Linux (hostname removed) 2.6.32-504.8.1.el6.x86_64 #1 SMP Fri Dec 19 12:09:25 EST 2014 x86_64 x86_64 x86_64 GNU/Linux
Machine Type: x86_64-redhat-linux-gnu

Bash Version: 4.1
Patch Level: 2
Release Status: release

Description:

I ran into this issue while experimenting with gnu make and .ONESHELL.  I've boiled the issue down to the following simple example:

$ newline=$'\n'
$ bash -e -c "trap 'echo handler' INT ERR;/bin/true; sleep 2; ${newline}/bin/false"

It will successfully trap if I do the following:

* hit ^C during the sleep
* use "false" instead of "/bin/false" because that'll use a builtin
* trap on EXIT instead
* remove the newline
* add another command after "/bin/false"
* add another command before "/bin/false" without a newline after it

If I replace "/bin/false" with a sleep then hit ^C then the INT trap also won't execute.

I traced this down to builtins/evalstring.c inside parse_and_execute (this is from the latest "develop" version of the file, not from the version I tested with):

#if defined (ONESHOT)
      /*
       * IF
       *   we were invoked as `bash -c' (startup_state == 2) AND
       *   parse_and_execute has not been called recursively AND
       *   we're not running a trap AND
       *   we have parsed the full command (string == '\0') AND
       *   we're not going to run the exit trap AND
       *   we have a simple command without redirections AND
       *   the command is not being timed AND
       *   the command's return status is not being inverted
       * THEN
       *   tell the execution code that we don't need to fork
       */
      if (should_suppress_fork (command))
{
  command->flags |= CMD_NO_FORK;
  command->value.Simple->flags |= CMD_NO_FORK;
}
      else if (command->type == cm_connection)
optimize_fork (command);
#endif /* ONESHOT */

... and it appears this is the culprit.  At first I was under the mistaken assumption "we're not running a trap" meant "a trap is set that could be triggered" when in reality it means what it says -- a trap is currently running.

Fix:

Two possible fixes come to mind:

1) Have the above code loop over the possible signals calling "signal_is_trapped"
2) Alter "change_signal" as follows:

/* mask of currently trapped signals */
unsigned long long current_traps;
/*...*/
/* If SIG has a string assigned to it, get rid of it.  Then give it
   VALUE. */
static void
change_signal (sig, value)
     int sig;
     char *value;
{
  if ((sigmodes[sig] & SIG_INPROGRESS) == 0)
    free_trap_command (sig);
  trap_list[sig] = value;

  sigmodes[sig] |= SIG_TRAPPED;
  if (value == (char *)IGNORE_SIG)
    sigmodes[sig] |= SIG_IGNORED;
  else
    sigmodes[sig] &= ~SIG_IGNORED;
  if (sigmodes[sig] & SIG_INPROGRESS)
    sigmodes[sig] |= SIG_CHANGED;
+
+ if( sigmodes[sig] & SIG_TRAPPED ) {
+   current_traps |= 1 << sig;
+ } else {
+   current_traps &= ~(1 << sig);
+ }
}

... then alter the block quoted above in parse_and_execute to check the value of "current_traps".

-brian

reply via email to

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