Index | Thread | Search

From:
Damien Miller <djm@mindrot.org>
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

Download raw body.

Thread
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 <djm@mindrot.org>
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();
 			}