1 /* $OpenBSD: select_close.c,v 1.2 2021/11/27 15:06:10 visa Exp $ */ 2 3 /* 4 * Copyright (c) 2021 Visa Hankala 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Test behaviour when a monitored file descriptor is closed by another thread. 21 * 22 * Note that this case is not defined by POSIX. 23 */ 24 25 #include <sys/types.h> 26 #include <sys/select.h> 27 #include <sys/socket.h> 28 #include <sys/sysctl.h> 29 #include <assert.h> 30 #include <err.h> 31 #include <errno.h> 32 #include <pthread.h> 33 #include <stdio.h> 34 #include <string.h> 35 #include <time.h> 36 #include <unistd.h> 37 38 static int barrier[2]; 39 static int sock[2]; 40 41 static int 42 wait_wchan(const char *wchanname) 43 { 44 struct kinfo_proc kps[3]; /* process + 2 threads */ 45 struct timespec end, now; 46 size_t size; 47 unsigned int i; 48 int mib[6], ret; 49 50 clock_gettime(CLOCK_MONOTONIC, &now); 51 end = now; 52 end.tv_sec += 1; 53 54 mib[0] = CTL_KERN; 55 mib[1] = KERN_PROC; 56 mib[2] = KERN_PROC_PID | KERN_PROC_SHOW_THREADS; 57 mib[3] = getpid(); 58 mib[4] = sizeof(kps[0]); 59 mib[5] = sizeof(kps) / sizeof(kps[0]); 60 61 for (;;) { 62 memset(kps, 0, sizeof(kps)); 63 size = sizeof(kps); 64 ret = sysctl(mib, 6, kps, &size, NULL, 0); 65 if (ret == -1) 66 err(1, "sysctl"); 67 for (i = 0; i < size / sizeof(kps[0]); i++) { 68 if (strncmp(kps[i].p_wmesg, wchanname, 69 sizeof(kps[i].p_wmesg)) == 0) 70 return 0; 71 } 72 73 usleep(1000); 74 clock_gettime(CLOCK_MONOTONIC, &now); 75 if (timespeccmp(&now, &end, >=)) 76 break; 77 } 78 79 errx(1, "wchan %s timeout", wchanname); 80 } 81 82 static void * 83 thread_main(void *arg) 84 { 85 fd_set rfds; 86 int ret; 87 char b; 88 89 FD_ZERO(&rfds); 90 FD_SET(sock[1], &rfds); 91 ret = select(sock[1] + 1, &rfds, NULL, NULL, NULL); 92 assert(ret == 1); 93 assert(FD_ISSET(sock[1], &rfds)); 94 95 /* Drain data to prevent subsequent wakeups. */ 96 read(sock[1], &b, 1); 97 98 /* Sync with parent thread. */ 99 write(barrier[1], "y", 1); 100 read(barrier[1], &b, 1); 101 102 FD_ZERO(&rfds); 103 FD_SET(sock[1], &rfds); 104 ret = select(sock[1] + 1, &rfds, NULL, NULL, NULL); 105 assert(ret == -1); 106 assert(errno == EBADF); 107 108 return NULL; 109 } 110 111 int 112 main(void) 113 { 114 pthread_t t; 115 int ret, saved_fd; 116 char b; 117 118 /* Enforce test timeout. */ 119 alarm(10); 120 121 if (socketpair(AF_UNIX, SOCK_STREAM, 0, barrier) == -1) 122 err(1, "can't create socket pair"); 123 124 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == -1) 125 err(1, "can't create socket pair"); 126 127 ret = pthread_create(&t, NULL, thread_main, NULL); 128 if (ret != 0) { 129 fprintf(stderr, "can't start thread: %s\n", strerror(ret)); 130 return 1; 131 } 132 133 /* Let the thread settle in select(). */ 134 wait_wchan("kqread"); 135 136 /* Awaken poll(). */ 137 write(sock[0], "x", 1); 138 139 /* Wait until the thread has left select(). */ 140 read(barrier[0], &b, 1); 141 142 /* 143 * Close and restore the fd that the thread has polled. 144 * This creates a pending badfd knote in the kernel. 145 */ 146 saved_fd = dup(sock[1]); 147 close(sock[1]); 148 dup2(saved_fd, sock[1]); 149 close(saved_fd); 150 151 /* Let the thread continue. */ 152 write(barrier[0], "x", 1); 153 154 /* Let the thread settle in select(). */ 155 wait_wchan("kqread"); 156 157 /* Close the fd to awaken select(). */ 158 close(sock[1]); 159 160 pthread_join(t, NULL); 161 162 close(sock[0]); 163 164 return 0; 165 } 166