1 /* 2 * Copyright (c) 2001 Markus Friedl. All rights reserved. 3 * Copyright (c) 2021 Darren Tucker (dtucker at dtucker net). 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "includes.h" 27 #ifndef HAVE_PSELECT 28 29 #include <sys/types.h> 30 #include <sys/time.h> 31 #ifdef HAVE_SYS_SELECT_H 32 # include <sys/select.h> 33 #endif 34 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <signal.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include "log.h" 43 #include "misc.h" /* for set_nonblock */ 44 45 #ifndef HAVE_SIGHANDLER_T 46 typedef void (*sighandler_t)(int); 47 #endif 48 49 static sighandler_t saved_sighandler[_NSIG]; 50 51 /* 52 * Set up the descriptors. Because they are close-on-exec, in the case 53 * where sshd's re-exec fails notify_pipe will still point to a descriptor 54 * that was closed by the exec attempt but if that descriptor has been 55 * reopened then we'll attempt to use that. Ensure that notify_pipe is 56 * outside of the range used by sshd re-exec but within NFDBITS (so we don't 57 * need to expand the fd_sets). 58 */ 59 #define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) 60 static int 61 pselect_notify_setup_fd(int *fd) 62 { 63 int r; 64 65 if ((r = fcntl(*fd, F_DUPFD, REEXEC_MIN_FREE_FD)) < 0 || 66 fcntl(r, F_SETFD, FD_CLOEXEC) < 0 || r >= FD_SETSIZE) 67 return -1; 68 (void)close(*fd); 69 return (*fd = r); 70 } 71 72 /* 73 * we write to this pipe if a SIGCHLD is caught in order to avoid 74 * the race between select() and child_terminated 75 */ 76 static pid_t notify_pid; 77 static int notify_pipe[2]; 78 static void 79 pselect_notify_setup(void) 80 { 81 static int initialized; 82 83 if (initialized && notify_pid == getpid()) 84 return; 85 if (notify_pid == 0) 86 debug3_f("initializing"); 87 else { 88 debug3_f("pid changed, reinitializing"); 89 if (notify_pipe[0] != -1) 90 close(notify_pipe[0]); 91 if (notify_pipe[1] != -1) 92 close(notify_pipe[1]); 93 } 94 if (pipe(notify_pipe) == -1) { 95 error("pipe(notify_pipe) failed %s", strerror(errno)); 96 } else if (pselect_notify_setup_fd(¬ify_pipe[0]) == -1 || 97 pselect_notify_setup_fd(¬ify_pipe[1]) == -1) { 98 error("fcntl(notify_pipe, ...) failed %s", strerror(errno)); 99 close(notify_pipe[0]); 100 close(notify_pipe[1]); 101 } else { 102 set_nonblock(notify_pipe[0]); 103 set_nonblock(notify_pipe[1]); 104 notify_pid = getpid(); 105 debug3_f("pid %d saved %d pipe0 %d pipe1 %d", getpid(), 106 notify_pid, notify_pipe[0], notify_pipe[1]); 107 initialized = 1; 108 return; 109 } 110 notify_pipe[0] = -1; /* read end */ 111 notify_pipe[1] = -1; /* write end */ 112 } 113 static void 114 pselect_notify_parent(void) 115 { 116 if (notify_pipe[1] != -1) 117 (void)write(notify_pipe[1], "", 1); 118 } 119 static void 120 pselect_notify_prepare(fd_set *readset) 121 { 122 if (notify_pipe[0] != -1) 123 FD_SET(notify_pipe[0], readset); 124 } 125 static void 126 pselect_notify_done(fd_set *readset) 127 { 128 char c; 129 130 if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) { 131 while (read(notify_pipe[0], &c, 1) != -1) 132 debug2_f("reading"); 133 FD_CLR(notify_pipe[0], readset); 134 } 135 } 136 137 /*ARGSUSED*/ 138 static void 139 pselect_sig_handler(int sig) 140 { 141 int save_errno = errno; 142 143 pselect_notify_parent(); 144 if (saved_sighandler[sig] != NULL) 145 (*saved_sighandler[sig])(sig); /* call original handler */ 146 errno = save_errno; 147 } 148 149 /* 150 * A minimal implementation of pselect(2), built on top of select(2). 151 */ 152 153 int 154 pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, 155 const struct timespec *timeout, const sigset_t *mask) 156 { 157 int ret, sig, saved_errno, unmasked = 0; 158 sigset_t osig; 159 struct sigaction sa, osa; 160 struct timeval tv, *tvp = NULL; 161 162 if (timeout != NULL) { 163 tv.tv_sec = timeout->tv_sec; 164 tv.tv_usec = timeout->tv_nsec / 1000; 165 tvp = &tv; 166 } 167 if (mask == NULL) /* no signal mask, just call select */ 168 return select(nfds, readfds, writefds, exceptfds, tvp); 169 170 /* For each signal we're unmasking, install our handler if needed. */ 171 for (sig = 0; sig < _NSIG; sig++) { 172 if (sig == SIGKILL || sig == SIGSTOP || sigismember(mask, sig)) 173 continue; 174 if (sigaction(sig, NULL, &sa) == 0 && 175 sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) { 176 unmasked = 1; 177 if (sa.sa_handler == pselect_sig_handler) 178 continue; 179 sa.sa_handler = pselect_sig_handler; 180 if (sigaction(sig, &sa, &osa) == 0) { 181 debug3_f("installing signal handler for %s, " 182 "previous %p", strsignal(sig), 183 osa.sa_handler); 184 saved_sighandler[sig] = osa.sa_handler; 185 } 186 } 187 } 188 if (unmasked) { 189 pselect_notify_setup(); 190 pselect_notify_prepare(readfds); 191 nfds = MAX(nfds, notify_pipe[0]); 192 } 193 194 /* Unmask signals, call select then restore signal mask. */ 195 sigprocmask(SIG_SETMASK, mask, &osig); 196 ret = select(nfds, readfds, writefds, exceptfds, tvp); 197 saved_errno = errno; 198 sigprocmask(SIG_SETMASK, &osig, NULL); 199 200 if (unmasked) 201 pselect_notify_done(readfds); 202 errno = saved_errno; 203 return ret; 204 } 205 #endif 206