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