From a7f01d8509d613d095361decbe98dd94a11d9b6b Mon Sep 17 00:00:00 2001 From: dtucker Date: Fri, 4 Jun 2021 05:09:08 +0000 Subject: [PATCH] Switch the listening select loop from select() to pselect() and mask signals while checking signal flags, umasking for pselect and restoring afterwards. Also restore signals before sighup_restart so they don't remain blocked after restart. This prevents a race where a SIGTERM or SIGHUP can arrive between checking the flag and calling select (eg if sshd is processing a new connection) resulting in sshd not shutting down until the next time it receives a new connection. bz#2158, with & ok djm@ --- usr.bin/ssh/sshd.c | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c index 448d844da09..95c9aeb717f 100644 --- a/usr.bin/ssh/sshd.c +++ b/usr.bin/ssh/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.573 2021/05/07 03:09:38 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.574 2021/06/04 05:09:08 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1087,6 +1087,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) struct sockaddr_storage from; socklen_t fromlen; pid_t pid; + sigset_t nsigset, osigset; /* setup fd set for accept */ fdset = NULL; @@ -1100,11 +1101,32 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) for (i = 0; i < options.max_startups; i++) startup_pipes[i] = -1; + /* + * Prepare signal mask that we use to block signals that might set + * received_sigterm or received_sighup, so that we are guaranteed + * to immediately wake up the pselect if a signal is received after + * the flag is checked. + */ + sigemptyset(&nsigset); + sigaddset(&nsigset, SIGHUP); + sigaddset(&nsigset, SIGCHLD); + sigaddset(&nsigset, SIGTERM); + sigaddset(&nsigset, SIGQUIT); + /* * Stay listening for connections until the system crashes or * the daemon is killed with a signal. */ for (;;) { + sigprocmask(SIG_BLOCK, &nsigset, &osigset); + if (received_sigterm) { + logit("Received signal %d; terminating.", + (int) received_sigterm); + close_listen_socks(); + if (options.pid_file != NULL) + unlink(options.pid_file); + exit(received_sigterm == SIGTERM ? 0 : 255); + } if (ostartups != startups) { setproctitle("%s [listener] %d of %d-%d startups", listener_proctitle, startups, @@ -1117,8 +1139,10 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) close_listen_socks(); lameduck = 1; } - if (listening <= 0) + if (listening <= 0) { + sigprocmask(SIG_SETMASK, &osigset, NULL); sighup_restart(); + } } free(fdset); fdset = xcalloc(howmany(maxfd + 1, NFDBITS), @@ -1130,19 +1154,12 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) if (startup_pipes[i] != -1) FD_SET(startup_pipes[i], fdset); - /* Wait in select until there is a connection. */ - ret = select(maxfd+1, fdset, NULL, NULL, NULL); + /* Wait until a connection arrives or a child exits. */ + ret = pselect(maxfd+1, fdset, NULL, NULL, NULL, &osigset); if (ret == -1 && errno != EINTR) - error("select: %.100s", strerror(errno)); - if (received_sigterm) { - logit("Received signal %d; terminating.", - (int) received_sigterm); - close_listen_socks(); - if (options.pid_file != NULL) - unlink(options.pid_file); - exit(received_sigterm == SIGTERM ? 0 : 255); - } - if (ret == -1) + error("pselect: %.100s", strerror(errno)); + sigprocmask(SIG_SETMASK, &osigset, NULL); + if (received_sigterm) continue; for (i = 0; i < options.max_startups; i++) { -- 2.20.1