From: Damien Miller Subject: sshd: mask SIGTERM/SIGQUIT across SIGHUP restart To: tech@openbsd.org Cc: openssh@openssh.com Date: Tue, 15 Sep 2026 17:56:35 +1000 Hi, This is https://bugzilla.mindrot.org/show_bug.cgi?id=3981 sshd restarts on SIGHUP by re-executing itself. If a SIGTERM/SIGQUIT comes after this has begun then the termination signal can be lost. The bug contains a description of the sequence: > 1. SIGHUP is received. > 2. listener loop checks received_sigterm first; it is not set. > 3. listener loop enters received_sighup handling. > 4. listener restores osigset, unblocking SIGTERM. > 5. SIGTERM arrives. > 6. sigterm_handler() sets received_sigterm. > 7. sighup_restart() continues to execv(). > 8. received_sigterm is lost across exec. > 9. new sshd continues running. This patch keeps SIGTERM/SIGQUIT masked across such a restart. The first thing in sshd.c:main() is a sigprocmask(2) call with an empty signal set, so one of these signals will be delivered then. ok? Another alternative might be to reset the handlers for these signals to SIGDFL, but this seems simpler. commit 01b5ab90cd89a91dc41a493a017439f0501a8e64 Author: Damien Miller Date: Tue Sep 15 17:49:11 2026 +1000 keep SIGTERM/SIGQUIT masked across SIGHUP restart Keep these signals masked so they don't get lost across a sshd restart initiated by SIGHUP. bz3981 Note, the first thing sshd does in main() is clear the sigprocmask. diff --git a/sshd.c b/sshd.c index a568091..65d21fb 100644 --- a/sshd.c +++ b/sshd.c @@ -964,6 +964,12 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s, lameduck = 1; } if (listening <= 0) { + /* + * Leave termination signals blocked, so + * they don't get lost across a restart. + */ + sigdelset(&nsigset, SIGTERM); + sigdelset(&nsigset, SIGQUIT); sigprocmask(SIG_SETMASK, &osigset, NULL); sighup_restart(); }