From 15409324f1974d41c183904ad575da7188058c1c Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Wed, 17 Nov 2021 16:47:24 -0500 Subject: [PATCH] Bash-5.1 patch 12: fix race condition with child processes and resetting trapped signals --- command.h | 1 + execute_cmd.c | 8 +++++++- jobs.c | 2 ++ nojobs.c | 2 ++ patchlevel.h | 2 +- sig.c | 10 +++++++++- subst.c | 2 ++ trap.c | 26 ++++++++++++++++++++++++++ 8 files changed, 50 insertions(+), 3 deletions(-) diff --git a/command.h b/command.h index 914198f9..b8477528 100644 --- a/command.h +++ b/command.h @@ -124,6 +124,7 @@ enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select, #define SUBSHELL_PROCSUB 0x20 /* subshell caused by <(command) or >(command) */ #define SUBSHELL_COPROC 0x40 /* subshell from a coproc pipeline */ #define SUBSHELL_RESETTRAP 0x80 /* subshell needs to reset trap strings on first call to trap */ +#define SUBSHELL_IGNTRAP 0x100 /* subshell should reset trapped signals from trap_handler */ /* A structure which represents a word. */ typedef struct word_desc { diff --git a/execute_cmd.c b/execute_cmd.c index 90129e06..425679a2 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -1547,6 +1547,9 @@ execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close) clear_pending_traps (); reset_signal_handlers (); subshell_environment |= SUBSHELL_RESETTRAP; + /* Note that signal handlers have been reset, so we should no longer + reset the handler and resend trapped signals to ourselves. */ + subshell_environment &= ~SUBSHELL_IGNTRAP; /* We are in a subshell, so forget that we are running a trap handler or that the signal handler has changed (we haven't changed it!) */ @@ -4320,7 +4323,8 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) already_forked = 1; cmdflags |= CMD_NO_FORK; - subshell_environment = SUBSHELL_FORK; /* XXX */ + /* We redo some of what make_child() does with SUBSHELL_IGNTRAP */ + subshell_environment = SUBSHELL_FORK|SUBSHELL_IGNTRAP; /* XXX */ if (pipe_in != NO_PIPE || pipe_out != NO_PIPE) subshell_environment |= SUBSHELL_PIPE; if (async) @@ -4574,6 +4578,7 @@ run_builtin: trap strings if we run trap to change a signal disposition. */ reset_signal_handlers (); subshell_environment |= SUBSHELL_RESETTRAP; + subshell_environment &= ~SUBSHELL_IGNTRAP; if (async) { @@ -5514,6 +5519,7 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, reset_terminating_signals (); /* XXX */ /* Cancel traps, in trap.c. */ restore_original_signals (); + subshell_environment &= ~SUBSHELL_IGNTRAP; #if defined (JOB_CONTROL) FREE (p); diff --git a/jobs.c b/jobs.c index a581f305..7c3b6e83 100644 --- a/jobs.c +++ b/jobs.c @@ -2217,6 +2217,8 @@ make_child (command, flags) signals to the default state for a new process. */ pid_t mypid; + subshell_environment |= SUBSHELL_IGNTRAP; + /* If this ends up being changed to modify or use `command' in the child process, go back and change callers who free `command' in the child process when this returns. */ diff --git a/nojobs.c b/nojobs.c index c5fc83d9..f2563ca0 100644 --- a/nojobs.c +++ b/nojobs.c @@ -575,6 +575,8 @@ make_child (command, flags) last_asynchronous_pid = getpid (); #endif + subshell_environment |= SUBSHELL_IGNTRAP; + default_tty_job_signals (); } else diff --git a/patchlevel.h b/patchlevel.h index 8b14f289..eb2aca52 100644 --- a/patchlevel.h +++ b/patchlevel.h @@ -25,6 +25,6 @@ regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh looks for to find the patch level (for the sccs version string). */ -#define PATCHLEVEL 11 +#define PATCHLEVEL 12 #endif /* _PATCHLEVEL_H_ */ diff --git a/sig.c b/sig.c index 6964d862..e6537d26 100644 --- a/sig.c +++ b/sig.c @@ -55,7 +55,8 @@ # include "bashhist.h" #endif -extern void initialize_siglist (); +extern void initialize_siglist PARAMS((void)); +extern void set_original_signal PARAMS((int, SigHandler *)); #if !defined (JOB_CONTROL) extern void initialize_job_signals PARAMS((void)); @@ -255,6 +256,13 @@ initialize_terminating_signals () sigaction (XSIG (i), &act, &oact); XHANDLER(i) = oact.sa_handler; XSAFLAGS(i) = oact.sa_flags; + +#if 0 + set_original_signal (XSIG(i), XHANDLER(i)); /* optimization */ +#else + set_original_signal (XSIG(i), act.sa_handler); /* optimization */ +#endif + /* Don't do anything with signals that are ignored at shell entry if the shell is not interactive. */ /* XXX - should we do this for interactive shells, too? */ diff --git a/subst.c b/subst.c index 462752de..327de083 100644 --- a/subst.c +++ b/subst.c @@ -5951,6 +5951,7 @@ process_substitute (string, open_for_read_in_child) free_pushed_string_input (); /* Cancel traps, in trap.c. */ restore_original_signals (); /* XXX - what about special builtins? bash-4.2 */ + subshell_environment &= ~SUBSHELL_IGNTRAP; QUIT; /* catch any interrupts we got post-fork */ setup_async_signals (); #if 0 @@ -6382,6 +6383,7 @@ command_substitute (string, quoted, flags) } QUIT; /* catch any interrupts we got post-fork */ subshell_environment |= SUBSHELL_RESETTRAP; + subshell_environment &= ~SUBSHELL_IGNTRAP; } #if defined (JOB_CONTROL) diff --git a/trap.c b/trap.c index c7f8ded5..1b27fb3a 100644 --- a/trap.c +++ b/trap.c @@ -481,6 +481,32 @@ trap_handler (sig) SIGRETURN (0); } + /* This means we're in a subshell, but have not yet reset the handler for + trapped signals. We're not supposed to execute the trap in this situation; + we should restore the original signal and resend the signal to ourselves + to preserve the Posix "signal traps that are not being ignored shall be + set to the default action" semantics. */ + if ((subshell_environment & SUBSHELL_IGNTRAP) && trap_list[sig] != (char *)IGNORE_SIG) + { + sigset_t mask; + + /* Paranoia */ + if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER) + original_signals[sig] = SIG_DFL; + + restore_signal (sig); + + /* Make sure we let the signal we just caught through */ + sigemptyset (&mask); + sigprocmask (SIG_SETMASK, (sigset_t *)NULL, &mask); + sigdelset (&mask, sig); + sigprocmask (SIG_SETMASK, &mask, (sigset_t *)NULL); + + kill (getpid (), sig); + + SIGRETURN (0); + } + if ((sig >= NSIG) || (trap_list[sig] == (char *)DEFAULT_SIG) || (trap_list[sig] == (char *)IGNORE_SIG)) -- 2.31.1