1 /* $OpenBSD: select_close.c,v 1.3 2021/12/24 10:22:41 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 <assert.h> 29 #include <err.h> 30 #include <errno.h> 31 #include <pthread.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 static int barrier[2]; 37 static int sock[2]; 38 39 static void * 40 thread_main(void *arg) 41 { 42 fd_set rfds; 43 int ret; 44 char b; 45 46 FD_ZERO(&rfds); 47 FD_SET(sock[1], &rfds); 48 ret = select(sock[1] + 1, &rfds, NULL, NULL, NULL); 49 assert(ret == 1); 50 assert(FD_ISSET(sock[1], &rfds)); 51 52 /* Drain data to prevent subsequent wakeups. */ 53 read(sock[1], &b, 1); 54 55 /* Sync with parent thread. */ 56 write(barrier[1], "y", 1); 57 read(barrier[1], &b, 1); 58 59 FD_ZERO(&rfds); 60 FD_SET(sock[1], &rfds); 61 ret = select(sock[1] + 1, &rfds, NULL, NULL, NULL); 62 assert(ret == -1); 63 assert(errno == EBADF); 64 65 return NULL; 66 } 67 68 int 69 main(void) 70 { 71 pthread_t t; 72 int ret, saved_fd; 73 char b; 74 75 /* Enforce test timeout. */ 76 alarm(10); 77 78 if (socketpair(AF_UNIX, SOCK_STREAM, 0, barrier) == -1) 79 err(1, "can't create socket pair"); 80 81 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == -1) 82 err(1, "can't create socket pair"); 83 84 ret = pthread_create(&t, NULL, thread_main, NULL); 85 if (ret != 0) { 86 fprintf(stderr, "can't start thread: %s\n", strerror(ret)); 87 return 1; 88 } 89 90 /* Let the thread settle in select(). */ 91 usleep(100000); 92 93 /* Awaken poll(). */ 94 write(sock[0], "x", 1); 95 96 /* Wait until the thread has left select(). */ 97 read(barrier[0], &b, 1); 98 99 /* 100 * Close and restore the fd that the thread has polled. 101 * This creates a pending badfd knote in the kernel. 102 */ 103 saved_fd = dup(sock[1]); 104 close(sock[1]); 105 dup2(saved_fd, sock[1]); 106 close(saved_fd); 107 108 /* Let the thread continue. */ 109 write(barrier[0], "x", 1); 110 111 /* Let the thread settle in select(). */ 112 usleep(100000); 113 114 /* Close the fd to awaken select(). */ 115 close(sock[1]); 116 117 pthread_join(t, NULL); 118 119 close(sock[0]); 120 121 return 0; 122 } 123