Kill sends signal to wrong process group with job control enabled
Ben Ashton <[email protected]>
| Newsgroups | gmane.comp.shells.bash.bugs |
|---|---|
| Message-ID | <[email protected]> |
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"