1 /* $OpenBSD: blocked_fifo.c,v 1.2 2012/07/08 11:35:37 guenther Exp $ */ 2 /* 3 * Copyright (c) 2012 Owain G. Ainsworth <oga@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* 19 * Test fifo opening blocking the file descriptor table and thus all other 20 * opens in the process. 21 * Symptoms are that the main thread will sleep on fifor (in the fifo open) and 22 * the deadlocker on fdlock (in open of any other file). 23 */ 24 25 #include <sys/types.h> 26 #include <sys/stat.h> 27 28 #include <pthread.h> 29 #include <unistd.h> 30 #include <fcntl.h> 31 #include "test.h" 32 33 #define FIFO "pthread.fifo" 34 #define FILE "/etc/services" /* just any file to deadlock on */ 35 36 int free_fd, expected_fd; 37 38 static void * 39 deadlock_detector(void *arg) 40 { 41 sleep(10); 42 unlink(FIFO); 43 PANIC("deadlock detected"); 44 } 45 46 static void * 47 fifo_deadlocker(void *arg) 48 { 49 int fd; 50 51 /* let other fifo open start */ 52 sleep(3); 53 54 /* open a random temporary file, if we don't deadlock this'll succeed */ 55 CHECKe(fd = open(FILE, O_RDONLY)); 56 CHECKe(close(fd)); 57 58 /* open fifo to unblock other thread */ 59 CHECKe(fd = open(FIFO, O_WRONLY)); 60 CHECKe(write(fd, "test", 4)); 61 CHECKe(close(fd)); 62 63 return ((caddr_t)NULL + errno); 64 } 65 66 static void * 67 fifo_closer(void *arg) 68 { 69 int fd; 70 71 /* let other fifo open start */ 72 sleep(3); 73 74 /* open a random temporary file and dup2 it over the FIFO */ 75 CHECKe(fd = open(FILE, O_RDONLY)); 76 CHECKe(dup2(fd, expected_fd)); 77 CHECKe(close(fd)); 78 CHECKe(close(expected_fd)); 79 80 /* open fifo to unblock other thread */ 81 CHECKe(fd = open(FIFO, O_WRONLY)); 82 CHECKe(write(fd, "test", 4)); 83 CHECKe(close(fd)); 84 85 return ((caddr_t)NULL + errno); 86 } 87 88 int 89 main(int argc, char *argv[]) 90 { 91 pthread_t test_thread, deadlock_finder; 92 struct stat st; 93 char buf[5]; 94 int fd; 95 ssize_t rlen; 96 97 unlink(FIFO); 98 CHECKe(mkfifo(FIFO, S_IRUSR | S_IWUSR)); 99 100 CHECKr(pthread_create(&deadlock_finder, NULL, 101 deadlock_detector, NULL)); 102 103 /* 104 * Verify that other threads can still do fd-table operations 105 * while a thread is blocked opening a FIFO 106 */ 107 CHECKr(pthread_create(&test_thread, NULL, fifo_deadlocker, NULL)); 108 109 /* Open fifo (this will sleep until we have readers) */ 110 CHECKe(fd = open(FIFO, O_RDONLY)); 111 112 CHECKe(fstat(fd, &st)); 113 ASSERT(S_ISFIFO(st.st_mode)); 114 CHECKe(rlen = read(fd, buf, sizeof buf)); 115 ASSERT(rlen == 4); 116 117 CHECKr(pthread_join(test_thread, NULL)); 118 119 CHECKe(close(fd)); 120 121 122 /* 123 * Verify that if a thread is blocked opening a FIFO and another 124 * thread targets the half-open fd with dup2 that it doesn't blow up. 125 */ 126 CHECKr(pthread_create(&test_thread, NULL, fifo_closer, NULL)); 127 128 free_fd = open("/dev/null", O_RDONLY); 129 expected_fd = dup(free_fd); 130 close(expected_fd); 131 132 /* Open fifo (this will sleep until we have readers) */ 133 CHECKe(fd = open(FIFO, O_RDONLY)); 134 135 ASSERT(fd == expected_fd); 136 ASSERT(close(fd) == -1); 137 ASSERT(errno == EBADF); 138 139 CHECKr(pthread_join(test_thread, NULL)); 140 141 CHECKe(close(free_fd)); 142 143 144 /* clean up */ 145 unlink(FIFO); 146 147 SUCCEED; 148 } 149