1 /*- 2 * Copyright (c) 2009 Hudson River Trading LLC 3 * Written by: John H. Baldwin <jhb@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 /* 30 * Regression tests for the closefrom(2) system call. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/mman.h> 35 #include <sys/user.h> 36 #include <sys/wait.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <libutil.h> 40 #include <paths.h> 41 #include <stdarg.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 struct shared_info { 48 int failed; 49 char tag[64]; 50 char message[0]; 51 }; 52 53 static int test = 1; 54 55 static void 56 ok(const char *descr) 57 { 58 59 printf("ok %d - %s\n", test, descr); 60 test++; 61 } 62 63 static void 64 fail(const char *descr, const char *fmt, ...) 65 { 66 va_list ap; 67 68 printf("not ok %d - %s", test, descr); 69 test++; 70 if (fmt) { 71 va_start(ap, fmt); 72 printf(" # "); 73 vprintf(fmt, ap); 74 va_end(ap); 75 } 76 printf("\n"); 77 exit(1); 78 } 79 80 #define fail_err(descr) fail((descr), "%s", strerror(errno)) 81 82 static void 83 cok(struct shared_info *info, const char *descr) 84 { 85 86 info->failed = 0; 87 strlcpy(info->tag, descr, sizeof(info->tag)); 88 exit(0); 89 } 90 91 static void 92 cfail(struct shared_info *info, const char *descr, const char *fmt, ...) 93 { 94 va_list ap; 95 96 info->failed = 1; 97 strlcpy(info->tag, descr, sizeof(info->tag)); 98 if (fmt) { 99 va_start(ap, fmt); 100 vsprintf(info->message, fmt, ap); 101 va_end(ap); 102 } 103 exit(0); 104 } 105 106 #define cfail_err(info, descr) cfail((info), (descr), "%s", strerror(errno)) 107 108 /* 109 * Use kinfo_getfile() to fetch the list of file descriptors and figure out 110 * the highest open file descriptor. 111 */ 112 static int 113 highest_fd(void) 114 { 115 struct kinfo_file *kif; 116 int cnt, i, highest; 117 118 kif = kinfo_getfile(getpid(), &cnt); 119 if (kif == NULL) 120 fail_err("kinfo_getfile"); 121 highest = INT_MIN; 122 for (i = 0; i < cnt; i++) 123 if (kif[i].kf_fd > highest) 124 highest = kif[i].kf_fd; 125 free(kif); 126 return (highest); 127 } 128 129 static int 130 devnull(void) 131 { 132 int fd; 133 134 fd = open(_PATH_DEVNULL, O_RDONLY); 135 if (fd < 0) 136 fail_err("open(\" "_PATH_DEVNULL" \")"); 137 return (fd); 138 } 139 140 int 141 main(void) 142 { 143 struct shared_info *info; 144 pid_t pid; 145 int fd, flags, i, start; 146 147 printf("1..21\n"); 148 149 /* We'd better start up with fd's 0, 1, and 2 open. */ 150 start = devnull(); 151 if (start == -1) 152 fail("open", "bad descriptor %d", start); 153 ok("open"); 154 155 /* Make sure highest_fd() works. */ 156 fd = highest_fd(); 157 if (start != fd) 158 fail("highest_fd", "bad descriptor %d != %d", start, fd); 159 ok("highest_fd"); 160 161 /* Try to use closefrom() for just closing fd 3. */ 162 closefrom(start + 1); 163 fd = highest_fd(); 164 if (fd != start) 165 fail("closefrom", "highest fd %d", fd); 166 ok("closefrom"); 167 168 /* Eat up 16 descriptors. */ 169 for (i = 0; i < 16; i++) 170 (void)devnull(); 171 fd = highest_fd(); 172 if (fd != start + 16) 173 fail("open 16", "highest fd %d", fd); 174 ok("open 16"); 175 176 /* Close half of them. */ 177 closefrom(11); 178 fd = highest_fd(); 179 if (fd != 10) 180 fail("closefrom", "highest fd %d", fd); 181 ok("closefrom"); 182 183 /* Explicitly close descriptors 6 and 8 to create holes. */ 184 if (close(6) < 0 || close(8) < 0) 185 fail_err("close2 "); 186 ok("close 2"); 187 188 /* Verify that close on 6 and 8 fails with EBADF. */ 189 if (close(6) == 0) 190 fail("close(6)", "did not fail"); 191 if (errno != EBADF) 192 fail_err("close(6)"); 193 ok("close(6)"); 194 if (close(8) == 0) 195 fail("close(8)", "did not fail"); 196 if (errno != EBADF) 197 fail_err("close(8)"); 198 ok("close(8)"); 199 200 /* Close from 4 on. */ 201 closefrom(4); 202 fd = highest_fd(); 203 if (fd != 3) 204 fail("closefrom", "highest fd %d", fd); 205 ok("closefrom"); 206 207 /* Allocate a small SHM region for IPC with our child. */ 208 info = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON | 209 MAP_SHARED, -1, 0); 210 if (info == MAP_FAILED) 211 fail_err("mmap"); 212 ok("mmap"); 213 214 /* Fork a child process to test closefrom(0). */ 215 pid = fork(); 216 if (pid < 0) 217 fail_err("fork"); 218 if (pid == 0) { 219 /* Child. */ 220 closefrom(0); 221 fd = highest_fd(); 222 if (fd >= 0) 223 cfail(info, "closefrom(0)", "highest fd %d", fd); 224 cok(info, "closefrom(0)"); 225 } 226 if (wait(NULL) < 0) 227 fail_err("wait"); 228 if (info->failed) 229 fail(info->tag, "%s", info->message); 230 ok(info->tag); 231 232 /* Fork a child process to test closefrom(-1). */ 233 pid = fork(); 234 if (pid < 0) 235 fail_err("fork"); 236 if (pid == 0) { 237 /* Child. */ 238 closefrom(-1); 239 fd = highest_fd(); 240 if (fd >= 0) 241 cfail(info, "closefrom(-1)", "highest fd %d", fd); 242 cok(info, "closefrom(-1)"); 243 } 244 if (wait(NULL) < 0) 245 fail_err("wait"); 246 if (info->failed) 247 fail(info->tag, "%s", info->message); 248 ok(info->tag); 249 250 /* Dup stdout to 6. */ 251 if (dup2(1, 6) < 0) 252 fail_err("dup2"); 253 fd = highest_fd(); 254 if (fd != 6) 255 fail("dup2", "highest fd %d", fd); 256 ok("dup2"); 257 258 /* Do a closefrom() starting in a hole. */ 259 closefrom(4); 260 fd = highest_fd(); 261 if (fd != 3) 262 fail("closefrom", "highest fd %d", fd); 263 ok("closefrom"); 264 265 /* Do a closefrom() beyond our highest open fd. */ 266 closefrom(32); 267 fd = highest_fd(); 268 if (fd != 3) 269 fail("closefrom", "highest fd %d", fd); 270 ok("closefrom"); 271 272 /* Chew up another 8 fd */ 273 for (i = 0; i < 8; i++) 274 (void)devnull(); 275 fd = highest_fd(); 276 start = fd - 7; 277 278 /* close_range() a hole in the middle */ 279 close_range(start + 3, start + 5, 0); 280 for (i = start + 3; i < start + 6; ++i) { 281 if (close(i) == 0 || errno != EBADF) { 282 --i; 283 break; 284 } 285 } 286 if (i != start + 6) 287 fail("close_range", "failed to close at %d in %d - %d", i + 1, 288 start + 3, start + 6); 289 ok("close_range"); 290 291 /* close_range from the middle of the hole */ 292 close_range(start + 4, start + 6, 0); 293 if ((i = highest_fd()) != fd) 294 fail("close_range", "highest fd %d", i); 295 ok("close_range"); 296 297 /* close_range to the end; effectively closefrom(2) */ 298 close_range(start + 3, ~0L, 0); 299 if ((i = highest_fd()) != start + 2) 300 fail("close_range", "highest fd %d", i); 301 ok("close_range"); 302 303 /* Now close the rest */ 304 close_range(start, start + 4, 0); 305 fd = highest_fd(); 306 if (fd != 3) 307 fail("close_range", "highest fd %d", fd); 308 ok("close_range"); 309 310 /* Fork a child process to test closefrom(0) twice. */ 311 pid = fork(); 312 if (pid < 0) 313 fail_err("fork"); 314 if (pid == 0) { 315 /* Child. */ 316 closefrom(0); 317 closefrom(0); 318 cok(info, "closefrom(0)"); 319 } 320 if (wait(NULL) < 0) 321 fail_err("wait"); 322 if (info->failed) 323 fail(info->tag, "%s", info->message); 324 ok(info->tag); 325 326 /* test CLOSE_RANGE_CLOEXEC */ 327 for (i = 0; i < 8; i++) 328 (void)devnull(); 329 fd = highest_fd(); 330 start = fd - 8; 331 if (close_range(start + 1, start + 4, CLOSE_RANGE_CLOEXEC) < 0) 332 fail_err("close_range(..., CLOSE_RANGE_CLOEXEC)"); 333 flags = fcntl(start, F_GETFD); 334 if (flags < 0) 335 fail_err("fcntl(.., F_GETFD)"); 336 if ((flags & FD_CLOEXEC) != 0) 337 fail("close_range", "CLOSE_RANGE_CLOEXEC set close-on-exec " 338 "when it should not have on fd %d", start); 339 for (i = start + 1; i <= start + 4; i++) { 340 flags = fcntl(i, F_GETFD); 341 if (flags < 0) 342 fail_err("fcntl(.., F_GETFD)"); 343 if ((flags & FD_CLOEXEC) == 0) 344 fail("close_range", "CLOSE_RANGE_CLOEXEC did not set " 345 "close-on-exec on fd %d", i); 346 } 347 for (; i < start + 8; i++) { 348 flags = fcntl(i, F_GETFD); 349 if (flags < 0) 350 fail_err("fcntl(.., F_GETFD)"); 351 if ((flags & FD_CLOEXEC) != 0) 352 fail("close_range", "CLOSE_RANGE_CLOEXEC set close-on-exec " 353 "when it should not have on fd %d", i); 354 } 355 if (close_range(start, start + 8, 0) < 0) 356 fail_err("close_range"); 357 ok("close_range(..., CLOSE_RANGE_CLOEXEC)"); 358 359 return (0); 360 } 361