1 /* $NetBSD: t_fdrestart.c,v 1.4 2023/11/18 19:46:55 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2023 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #define _KMEMUSER /* ERESTART */ 30 31 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: t_fdrestart.c,v 1.4 2023/11/18 19:46:55 riastradh Exp $"); 33 34 #include <sys/ioctl.h> 35 #include <sys/socket.h> 36 #include <sys/un.h> 37 38 #include <atf-c.h> 39 #include <errno.h> 40 #include <pthread.h> 41 #include <unistd.h> 42 43 #include <rump/rump.h> 44 #include <rump/rump_syscalls.h> 45 46 #include "h_macros.h" 47 48 struct fdrestart { 49 void (*op)(struct fdrestart *); 50 int fd; 51 pthread_barrier_t barrier; 52 }; 53 54 static void 55 waitforbarrier(struct fdrestart *F, const char *caller) 56 { 57 int error; 58 59 error = pthread_barrier_wait(&F->barrier); 60 switch (error) { 61 case 0: 62 case PTHREAD_BARRIER_SERIAL_THREAD: 63 break; 64 default: 65 atf_tc_fail("%s: pthread_barrier_wait: %d, %s", caller, error, 66 strerror(error)); 67 } 68 } 69 70 static void 71 doread(struct fdrestart *F) 72 { 73 char c; 74 ssize_t nread; 75 int error; 76 77 /* 78 * Wait for the other thread to be ready. 79 */ 80 waitforbarrier(F, "reader"); 81 82 /* 83 * Start a read. This should block, and then, when the other 84 * thread closes the fd, should be woken to fail with ERESTART. 85 */ 86 nread = rump_sys_read(F->fd, &c, sizeof(c)); 87 ATF_REQUIRE_EQ_MSG(nread, -1, "nread=%zd", nread); 88 error = errno; 89 ATF_REQUIRE_EQ_MSG(error, ERESTART, "errno=%d (%s)", error, 90 strerror(error)); 91 92 /* 93 * Now further attempts at I/O should fail with EBADF because 94 * the fd has been closed. 95 */ 96 nread = rump_sys_read(F->fd, &c, sizeof(c)); 97 ATF_REQUIRE_EQ_MSG(nread, -1, "nread=%zd", nread); 98 error = errno; 99 ATF_REQUIRE_EQ_MSG(error, EBADF, "errno=%d (%s)", error, 100 strerror(error)); 101 } 102 103 static void 104 dowrite(struct fdrestart *F) 105 { 106 static const char buf[1024*1024]; /* XXX >BIG_PIPE_SIZE */ 107 ssize_t nwrit; 108 int error; 109 110 /* 111 * Make sure the pipe's buffer is full first. 112 */ 113 for (;;) { 114 int nspace; 115 116 RL(rump_sys_ioctl(F->fd, FIONSPACE, &nspace)); 117 ATF_REQUIRE_MSG(nspace >= 0, "nspace=%d", nspace); 118 if (nspace == 0) 119 break; 120 RL(rump_sys_write(F->fd, buf, (size_t)nspace)); 121 } 122 123 /* 124 * Wait for the other thread to be ready. 125 */ 126 waitforbarrier(F, "writer"); 127 128 /* 129 * Start a write. This should block, and then, when the other 130 * thread closes the fd, should be woken to fail with ERESTART. 131 */ 132 nwrit = rump_sys_write(F->fd, buf, sizeof(buf)); 133 ATF_REQUIRE_EQ_MSG(nwrit, -1, "nwrit=%zd", nwrit); 134 error = errno; 135 ATF_REQUIRE_EQ_MSG(error, ERESTART, "errno=%d (%s)", error, 136 strerror(error)); 137 138 /* 139 * Now further attempts at I/O should fail with EBADF because 140 * the fd has been closed. 141 */ 142 nwrit = rump_sys_write(F->fd, buf, sizeof(buf)); 143 ATF_REQUIRE_EQ_MSG(nwrit, -1, "nwrit=%zd", nwrit); 144 error = errno; 145 ATF_REQUIRE_EQ_MSG(error, EBADF, "errno=%d (%s)", error, 146 strerror(error)); 147 } 148 149 static void * 150 doit(void *cookie) 151 { 152 struct fdrestart *F = cookie; 153 154 (*F->op)(F); 155 156 return NULL; 157 } 158 159 static void 160 on_sigalrm(int signo) 161 { 162 163 atf_tc_fail("timed out"); 164 } 165 166 static void 167 testfdrestart(struct fdrestart *F) 168 { 169 pthread_t t; 170 171 ATF_REQUIRE_MSG(signal(SIGALRM, &on_sigalrm) != SIG_ERR, 172 "errno=%d (%s)", errno, strerror(errno)); 173 174 RZ(pthread_barrier_init(&F->barrier, NULL, 2)); 175 RZ(pthread_create(&t, NULL, &doit, F)); 176 waitforbarrier(F, "closer"); /* wait for thread to start */ 177 (void)sleep(1); /* wait for op to start */ 178 (void)alarm(1); /* set a deadline */ 179 RL(rump_sys_close(F->fd)); /* wake op in other thread */ 180 RZ(pthread_join(t, NULL)); /* wait for op to wake and fail */ 181 (void)alarm(0); /* clear the deadline */ 182 } 183 184 ATF_TC(pipe_read); 185 ATF_TC_HEAD(pipe_read, tc) 186 { 187 atf_tc_set_md_var(tc, "descr", "Test pipe read fails on close"); 188 } 189 ATF_TC_BODY(pipe_read, tc) 190 { 191 struct fdrestart fdrestart, *F = &fdrestart; 192 int fd[2]; 193 194 rump_init(); 195 196 RL(rump_sys_pipe(fd)); 197 198 memset(F, 0, sizeof(*F)); 199 F->op = &doread; 200 F->fd = fd[0]; 201 testfdrestart(F); 202 } 203 204 ATF_TC(pipe_write); 205 ATF_TC_HEAD(pipe_write, tc) 206 { 207 atf_tc_set_md_var(tc, "descr", "Test pipe write fails on close"); 208 } 209 ATF_TC_BODY(pipe_write, tc) 210 { 211 struct fdrestart fdrestart, *F = &fdrestart; 212 int fd[2]; 213 214 rump_init(); 215 216 RL(rump_sys_pipe(fd)); 217 218 memset(F, 0, sizeof(*F)); 219 F->op = &dowrite; 220 F->fd = fd[1]; 221 atf_tc_expect_fail("PR kern/57659"); 222 testfdrestart(F); 223 } 224 225 ATF_TC(socketpair_read); 226 ATF_TC_HEAD(socketpair_read, tc) 227 { 228 atf_tc_set_md_var(tc, "descr", "Test socketpair read fails on close"); 229 } 230 ATF_TC_BODY(socketpair_read, tc) 231 { 232 struct fdrestart fdrestart, *F = &fdrestart; 233 int fd[2]; 234 235 rump_init(); 236 237 RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM, 0, fd)); 238 239 memset(F, 0, sizeof(*F)); 240 F->op = &doread; 241 F->fd = fd[0]; 242 testfdrestart(F); 243 } 244 245 ATF_TC(socketpair_write); 246 ATF_TC_HEAD(socketpair_write, tc) 247 { 248 atf_tc_set_md_var(tc, "descr", "Test socketpair write fails on close"); 249 } 250 ATF_TC_BODY(socketpair_write, tc) 251 { 252 struct fdrestart fdrestart, *F = &fdrestart; 253 int fd[2]; 254 255 rump_init(); 256 257 RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM, 0, fd)); 258 259 memset(F, 0, sizeof(*F)); 260 F->op = &dowrite; 261 F->fd = fd[0]; 262 testfdrestart(F); 263 } 264 265 ATF_TP_ADD_TCS(tp) 266 { 267 268 ATF_TP_ADD_TC(tp, pipe_read); 269 ATF_TP_ADD_TC(tp, pipe_write); 270 ATF_TP_ADD_TC(tp, socketpair_read); 271 ATF_TP_ADD_TC(tp, socketpair_write); 272 273 return atf_no_error(); 274 } 275