Download raw body.
sshd: mask SIGTERM/SIGQUIT across SIGHUP restart
On Tue, 15 Sep 2026, Damien Miller wrote:
> 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);
These should of course be osigset
commit 17c7dca55648d955c3bf144745da08b24c54bf78
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..e55bec2 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(&osigset, SIGTERM);
+ sigdelset(&osigset, SIGQUIT);
sigprocmask(SIG_SETMASK, &osigset, NULL);
sighup_restart();
}
sshd: mask SIGTERM/SIGQUIT across SIGHUP restart