diff options
author | dtucker@openbsd.org <dtucker@openbsd.org> | 2021-06-04 05:09:08 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2021-06-04 15:13:14 +1000 |
commit | 771f57a8626709f2ad207058efd68fbf30d31553 (patch) | |
tree | 95a679c09d4a9b0c2176bb2e33242064939ce2cb | |
parent | f64f8c00d158acc1359b8a096835849b23aa2e86 (diff) |
upstream: 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@
OpenBSD-Commit-ID: bf85bf880fd78e00d7478657644fcda97b9a936f
-rw-r--r-- | sshd.c | 45 |
1 files changed, 31 insertions, 14 deletions
@@ -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 <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -1141,6 +1141,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) socklen_t fromlen; pid_t pid; u_char rnd[256]; + sigset_t nsigset, osigset; /* setup fd set for accept */ fdset = NULL; @@ -1155,10 +1156,31 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) 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, @@ -1171,8 +1193,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), @@ -1184,19 +1208,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++) { |