Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -march=x86-64 -mtune=generic -O2 -pipe -fno-plt
-fexceptions -Wp,-D_FORTIFY_SOURCE=3 -Wformat
-Werror=format-security -fstack-clash-protection
-fcf-protection -fno-omit-frame-pointer
-mno-omit-leaf-frame-pointer -g
-ffile-prefix-map=/build/bash/src=/usr/src/debug/bash -flto=auto
-DDEFAULT_PATH_VALUE='/usr/local/sbin:/usr/local/bin:/usr/bin'
-DSTANDARD_UTILS_PATH='/usr/bin' -DSYS_BASHRC='/etc/bash.bashrc'
-DSYS_BASH_LOGOUT='/etc/bash.bash_logout' -DNON_INTERACTIVE_LOGIN_SHELLS
-std=gnu17
uname output: Linux ben-laptop 6.18.9-arch1-2 #1 SMP PREEMPT_DYNAMIC
Mon, 09 Feb 2026 17:16:33 +0000 x86_64 GNU/Linux
Machine Type: x86_64-pc-linux-gnu
Bash Version: 5.3
Patch Level: 9
Release Status: release
Description:
In the following script, we create a subshell that traps SIGINT and
SIGTERM
and then uses setsid to run sleep in a separate process group.
We then wait
for sleep to finish (note setsid won't fork and immediately exit
because job
control isn't inherited by the subshell so it doesn't start out as
a process
group leader, instead it just calls setsid and execs sleep). We now
have
sleep running as a child of the subshell as a process group leader.
However
in certain, somewhat contrived, circumstances, bash's builtin kill
will send
the signal to the WRONG process group. See the following strace
output when
running the script below:
> Sending SIGTERM to process group: 5710 (kill -TERM -5710)
[pid 5709] kill(-5709, SIGTERM) = 0
As you can see we ran `kill -TERM -5710`, but bash actually sent it to
process group 5709 (the subshell itself). If you don't use the
builtin kill
then it works as expected (note the syntax for killing process
groups with
kill binaries seems to vary a lot. It seems like with procps-ng
kill you need
to do `/bin/kill -TERM -- -PGID` instead)
I also tested the following script with Dash/BusyBox/Zsh and it worked
correctly. However it misbehaves in every version of Bash that I tested
(POSIX mode or otherwise).
Repeat-By:
Here is the script to reproduce the issue:
#!/usr/bin/env sh
set -u
# Only happens with job control enabled
set -m
(
echo "> In subshell"
trap 'echo "> SIGTERM received in subshell SHOULD NOT HAPPEN!"' TERM
trap 'echo "> SIGINT received in subshell"' INT
setsid sleep 10 &
pid=$!
echo "> Started new process group $pid"
# Wait for for sleep, SIGINT from main process will interrupt this
wait "$pid"
echo "> Sending SIGTERM to process group: $pid (kill -TERM -$pid)"
kill -TERM -$pid
wait "$pid"
) &
subshell_pid=$!
echo "Subshell pid $subshell_pid"
sleep 5
echo "Sending SIGINT to subshell $subshell_pid"
kill -INT "$subshell_pid"
wait "$subshell_pid"