1 /* $OpenBSD: select_close.c,v 1.1 2021/11/21 06:21:01 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 struct timeval tv; 87 int ret; 88 char b; 89 90 FD_ZERO(&rfds); 91 FD_SET(sock[1], &rfds); 92 tv.tv_sec = 0; 93 tv.tv_usec = 100000; 94 ret = select(sock[1] + 1, &rfds, NULL, NULL, &tv); 95 assert(ret == 1); 96 assert(FD_ISSET(sock[1], &rfds)); 97 98 /* Drain data to prevent subsequent wakeups. */ 99 read(sock[1], &b, 1); 100 101 /* Sync with parent thread. */ 102 write(barrier[1], "y", 1); 103 read(barrier[1], &b, 1); 104 105 FD_ZERO(&rfds); 106 FD_SET(sock[1], &rfds); 107 tv.tv_sec = 0; 108 tv.tv_usec = 100000; 109 ret = select(sock[1] + 1, &rfds, NULL, NULL, &tv); 110 assert(ret == -1); 111 assert(errno == EBADF); 112 113 return NULL; 114 } 115 116 int 117 main(void) 118 { 119 pthread_t t; 120 int ret, saved_fd; 121 char b; 122 123 /* Enforce test timeout. */ 124 alarm(10); 125 126 if (socketpair(AF_UNIX, SOCK_STREAM, 0, barrier) == -1) 127 err(1, "can't create socket pair"); 128 129 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == -1) 130 err(1, "can't create socket pair"); 131 132 ret = pthread_create(&t, NULL, thread_main, NULL); 133 if (ret != 0) { 134 fprintf(stderr, "can't start thread: %s\n", strerror(ret)); 135 return 1; 136 } 137 138 /* Let the thread settle in select(). */ 139 wait_wchan("kqread"); 140 141 /* Awaken poll(). */ 142 write(sock[0], "x", 1); 143 144 /* Wait until the thread has left select(). */ 145 read(barrier[0], &b, 1); 146 147 /* 148 * Close and restore the fd that the thread has polled. 149 * This creates a pending badfd knote in the kernel. 150 */ 151 saved_fd = dup(sock[1]); 152 close(sock[1]); 153 dup2(saved_fd, sock[1]); 154 close(saved_fd); 155 156 /* Let the thread continue. */ 157 write(barrier[0], "x", 1); 158 159 /* Let the thread settle in select(). */ 160 wait_wchan("kqread"); 161 162 /* Close the fd to awaken select(). */ 163 close(sock[1]); 164 165 pthread_join(t, NULL); 166 167 close(sock[0]); 168 169 return 0; 170 } 171