1 /*- 2 * Copyright (c) 2007 Robert N. M. Watson 3 * All rights reserved. 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 AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 /* 28 * Sockets serialize I/O in each direction in order to avoid interlacing of 29 * I/O by multiple processes or threcvs recving or sending the socket. This 30 * is done using some form of kernel lock (varies by kernel version), called 31 * "sblock" in FreeBSD. However, to avoid unkillable processes waiting on 32 * I/O that may be entirely controlled by a remote network endpoint, that 33 * lock acquisition must be interruptible. 34 * 35 * To test this, set up a local domain stream socket pair and a set of three 36 * processes. Two processes block in recv(), the first on sbwait (wait for 37 * I/O), and the second on the sblock waiting for the first to finish. A 38 * third process is responsible for signalling the second process, then 39 * writing to the socket. Depending on the error returned in the second 40 * process, we can tell whether the sblock wait was interrupted, or if 41 * instead the process only woke up when the write was performed. 42 */ 43 44 #include <sys/socket.h> 45 46 #include <err.h> 47 #include <errno.h> 48 #include <signal.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <unistd.h> 52 53 static int interrupted; 54 static void 55 signal_handler(int signum __unused) 56 { 57 58 interrupted++; 59 } 60 61 /* 62 * Process that will perform a blocking recv on a UNIX domain socket. This 63 * should return one byte of data. 64 */ 65 static void 66 blocking_recver(int fd) 67 { 68 ssize_t len; 69 char ch; 70 71 len = recv(fd, &ch, sizeof(ch), 0); 72 if (len < 0) 73 err(-1, "FAIL: blocking_recver: recv"); 74 if (len == 0) 75 errx(-1, "FAIL: blocking_recver: recv: eof"); 76 if (len != 1) 77 errx(-1, "FAIL: blocking_recver: recv: %zd bytes", len); 78 if (interrupted) 79 errx(-1, "FAIL: blocking_recver: interrupted wrong pid"); 80 } 81 82 /* 83 * Process that will perform a locking recv on a UNIX domain socket. 84 * 85 * This is where we figure out if the test worked or not. If it has failed, 86 * then recv() will return EOF, as the close() arrives before the signal, 87 * meaning that the wait for the sblock was not interrupted; if it has 88 * succeeded, we get EINTR as the signal interrupts the lock request. 89 */ 90 static void 91 locking_recver(int fd) 92 { 93 ssize_t len; 94 char ch; 95 96 if (sleep(1) != 0) 97 err(-1, "FAIL: locking_recver: sleep"); 98 len = recv(fd, &ch, sizeof(ch), 0); 99 if (len < 0 && errno != EINTR) 100 err(-1, "FAIL: locking_recver: recv"); 101 if (len < 0 && errno == EINTR) { 102 fprintf(stderr, "PASS\n"); 103 exit(0); 104 } 105 if (len == 0) 106 errx(-1, "FAIL: locking_recver: recv: eof"); 107 if (!interrupted) 108 errx(-1, "FAIL: locking_recver: not interrupted"); 109 } 110 111 static void 112 signaller(pid_t locking_recver_pid, int fd) 113 { 114 ssize_t len; 115 char ch; 116 117 if (sleep(2) != 0) { 118 warn("signaller sleep(2)"); 119 return; 120 } 121 if (kill(locking_recver_pid, SIGHUP) < 0) { 122 warn("signaller kill(%d)", locking_recver_pid); 123 return; 124 } 125 if (sleep(1) != 0) { 126 warn("signaller sleep(1)"); 127 return; 128 } 129 len = send(fd, &ch, sizeof(ch), 0); 130 if (len < 0) { 131 warn("signaller send"); 132 return; 133 } 134 if (len != sizeof(ch)) { 135 warnx("signaller send ret %zd", len); 136 return; 137 } 138 if (close(fd) < 0) { 139 warn("signaller close"); 140 return; 141 } 142 if (sleep(1) != 0) { 143 warn("signaller sleep(1)"); 144 return; 145 } 146 } 147 148 int 149 main(void) 150 { 151 int error, fds[2], recver_fd, sender_fd; 152 pid_t blocking_recver_pid; 153 pid_t locking_recver_pid; 154 struct sigaction sa; 155 156 if (sigaction(SIGHUP, NULL, &sa) < 0) 157 err(-1, "FAIL: sigaction(SIGHUP, NULL, &sa)"); 158 159 sa.sa_handler = signal_handler; 160 if (sa.sa_flags & SA_RESTART) 161 printf("SIGHUP restartable by default (cleared)\n"); 162 sa.sa_flags &= ~SA_RESTART; 163 164 if (sigaction(SIGHUP, &sa, NULL) < 0) 165 err(-1, "FAIL: sigaction(SIGHUP, &sa, NULL)"); 166 167 #if 0 168 if (signal(SIGHUP, signal_handler) == SIG_ERR) 169 err(-1, "FAIL: signal(SIGHUP)"); 170 #endif 171 172 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) 173 err(-1, "FAIL: socketpair(PF_LOCAL, SOGK_STREAM, 0)"); 174 175 sender_fd = fds[0]; 176 recver_fd = fds[1]; 177 178 blocking_recver_pid = fork(); 179 if (blocking_recver_pid < 0) 180 err(-1, "FAIL: fork"); 181 if (blocking_recver_pid == 0) { 182 close(sender_fd); 183 blocking_recver(recver_fd); 184 exit(0); 185 } 186 187 locking_recver_pid = fork(); 188 if (locking_recver_pid < 0) { 189 error = errno; 190 kill(blocking_recver_pid, SIGKILL); 191 errno = error; 192 err(-1, "FAIL: fork"); 193 } 194 if (locking_recver_pid == 0) { 195 close(sender_fd); 196 locking_recver(recver_fd); 197 exit(0); 198 } 199 200 signaller(locking_recver_pid, sender_fd); 201 202 kill(blocking_recver_pid, SIGKILL); 203 kill(locking_recver_pid, SIGKILL); 204 exit(0); 205 } 206