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 __FBSDID("$FreeBSD$"); 30 31 /* 32 * Regression tests for the closefrom(2) system call. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/mman.h> 37 #include <sys/user.h> 38 #include <sys/wait.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <libutil.h> 42 #include <paths.h> 43 #include <stdarg.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 49 struct shared_info { 50 int failed; 51 char tag[64]; 52 char message[0]; 53 }; 54 55 static int test = 1; 56 57 static void 58 ok(const char *descr) 59 { 60 61 printf("ok %d - %s\n", test, descr); 62 test++; 63 } 64 65 static void 66 fail(const char *descr, const char *fmt, ...) 67 { 68 va_list ap; 69 70 printf("not ok %d - %s", test, descr); 71 test++; 72 if (fmt) { 73 va_start(ap, fmt); 74 printf(" # "); 75 vprintf(fmt, ap); 76 va_end(ap); 77 } 78 printf("\n"); 79 exit(1); 80 } 81 82 #define fail_err(descr) fail((descr), "%s", strerror(errno)) 83 84 static void 85 cok(struct shared_info *info, const char *descr) 86 { 87 88 info->failed = 0; 89 strlcpy(info->tag, descr, sizeof(info->tag)); 90 exit(0); 91 } 92 93 static void 94 cfail(struct shared_info *info, const char *descr, const char *fmt, ...) 95 { 96 va_list ap; 97 98 info->failed = 1; 99 strlcpy(info->tag, descr, sizeof(info->tag)); 100 if (fmt) { 101 va_start(ap, fmt); 102 vsprintf(info->message, fmt, ap); 103 va_end(ap); 104 } 105 exit(0); 106 } 107 108 #define cfail_err(info, descr) cfail((info), (descr), "%s", strerror(errno)) 109 110 /* 111 * Use kinfo_getfile() to fetch the list of file descriptors and figure out 112 * the highest open file descriptor. 113 */ 114 static int 115 highest_fd(void) 116 { 117 struct kinfo_file *kif; 118 int cnt, i, highest; 119 120 kif = kinfo_getfile(getpid(), &cnt); 121 if (kif == NULL) 122 fail_err("kinfo_getfile"); 123 highest = INT_MIN; 124 for (i = 0; i < cnt; i++) 125 if (kif[i].kf_fd > highest) 126 highest = kif[i].kf_fd; 127 free(kif); 128 return (highest); 129 } 130 131 static int 132 devnull(void) 133 { 134 int fd; 135 136 fd = open(_PATH_DEVNULL, O_RDONLY); 137 if (fd < 0) 138 fail_err("open(\" "_PATH_DEVNULL" \")"); 139 return (fd); 140 } 141 142 int 143 main(void) 144 { 145 struct shared_info *info; 146 pid_t pid; 147 int fd, i, start; 148 149 printf("1..20\n"); 150 151 /* We better start up with fd's 0, 1, and 2 open. */ 152 start = devnull(); 153 if (start == -1) 154 fail("open", "bad descriptor %d", start); 155 ok("open"); 156 157 /* Make sure highest_fd() works. */ 158 fd = highest_fd(); 159 if (start != fd) 160 fail("highest_fd", "bad descriptor %d != %d", start, fd); 161 ok("highest_fd"); 162 163 /* Try to use closefrom() for just closing fd 3. */ 164 closefrom(start + 1); 165 fd = highest_fd(); 166 if (fd != start) 167 fail("closefrom", "highest fd %d", fd); 168 ok("closefrom"); 169 170 /* Eat up 16 descriptors. */ 171 for (i = 0; i < 16; i++) 172 (void)devnull(); 173 fd = highest_fd(); 174 if (fd != start + 16) 175 fail("open 16", "highest fd %d", fd); 176 ok("open 16"); 177 178 /* Close half of them. */ 179 closefrom(11); 180 fd = highest_fd(); 181 if (fd != 10) 182 fail("closefrom", "highest fd %d", fd); 183 ok("closefrom"); 184 185 /* Explicitly close descriptors 6 and 8 to create holes. */ 186 if (close(6) < 0 || close(8) < 0) 187 fail_err("close2 "); 188 ok("close 2"); 189 190 /* Verify that close on 6 and 8 fails with EBADF. */ 191 if (close(6) == 0) 192 fail("close(6)", "did not fail"); 193 if (errno != EBADF) 194 fail_err("close(6)"); 195 ok("close(6)"); 196 if (close(8) == 0) 197 fail("close(8)", "did not fail"); 198 if (errno != EBADF) 199 fail_err("close(8)"); 200 ok("close(8)"); 201 202 /* Close from 4 on. */ 203 closefrom(4); 204 fd = highest_fd(); 205 if (fd != 3) 206 fail("closefrom", "highest fd %d", fd); 207 ok("closefrom"); 208 209 /* Allocate a small SHM region for IPC with our child. */ 210 info = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON | 211 MAP_SHARED, -1, 0); 212 if (info == MAP_FAILED) 213 fail_err("mmap"); 214 ok("mmap"); 215 216 /* Fork a child process to test closefrom(0). */ 217 pid = fork(); 218 if (pid < 0) 219 fail_err("fork"); 220 if (pid == 0) { 221 /* Child. */ 222 closefrom(0); 223 fd = highest_fd(); 224 if (fd >= 0) 225 cfail(info, "closefrom(0)", "highest fd %d", fd); 226 cok(info, "closefrom(0)"); 227 } 228 if (wait(NULL) < 0) 229 fail_err("wait"); 230 if (info->failed) 231 fail(info->tag, "%s", info->message); 232 ok(info->tag); 233 234 /* Fork a child process to test closefrom(-1). */ 235 pid = fork(); 236 if (pid < 0) 237 fail_err("fork"); 238 if (pid == 0) { 239 /* Child. */ 240 closefrom(-1); 241 fd = highest_fd(); 242 if (fd >= 0) 243 cfail(info, "closefrom(-1)", "highest fd %d", fd); 244 cok(info, "closefrom(-1)"); 245 } 246 if (wait(NULL) < 0) 247 fail_err("wait"); 248 if (info->failed) 249 fail(info->tag, "%s", info->message); 250 ok(info->tag); 251 252 /* Dup stdout to 6. */ 253 if (dup2(1, 6) < 0) 254 fail_err("dup2"); 255 fd = highest_fd(); 256 if (fd != 6) 257 fail("dup2", "highest fd %d", fd); 258 ok("dup2"); 259 260 /* Do a closefrom() starting in a hole. */ 261 closefrom(4); 262 fd = highest_fd(); 263 if (fd != 3) 264 fail("closefrom", "highest fd %d", fd); 265 ok("closefrom"); 266 267 /* Do a closefrom() beyond our highest open fd. */ 268 closefrom(32); 269 fd = highest_fd(); 270 if (fd != 3) 271 fail("closefrom", "highest fd %d", fd); 272 ok("closefrom"); 273 274 /* Chew up another 8 fd */ 275 for (i = 0; i < 8; i++) 276 (void)devnull(); 277 fd = highest_fd(); 278 start = fd - 7; 279 280 /* close_range() a hole in the middle */ 281 close_range(start + 3, start + 5, 0); 282 for (i = start + 3; i < start + 6; ++i) { 283 if (close(i) == 0 || errno != EBADF) { 284 --i; 285 break; 286 } 287 } 288 if (i != start + 6) 289 fail("close_range", "failed to close at %d in %d - %d", i + 1, 290 start + 3, start + 6); 291 ok("close_range"); 292 293 /* close_range from the middle of the hole */ 294 close_range(start + 4, start + 6, 0); 295 if ((i = highest_fd()) != fd) 296 fail("close_range", "highest fd %d", i); 297 ok("close_range"); 298 299 /* close_range to the end; effectively closefrom(2) */ 300 close_range(start + 3, ~0L, 0); 301 if ((i = highest_fd()) != start + 2) 302 fail("close_range", "highest fd %d", i); 303 ok("close_range"); 304 305 /* Now close the rest */ 306 close_range(start, start + 4, 0); 307 fd = highest_fd(); 308 if (fd != 3) 309 fail("close_range", "highest fd %d", fd); 310 ok("close_range"); 311 312 /* Fork a child process to test closefrom(0) twice. */ 313 pid = fork(); 314 if (pid < 0) 315 fail_err("fork"); 316 if (pid == 0) { 317 /* Child. */ 318 closefrom(0); 319 closefrom(0); 320 cok(info, "closefrom(0)"); 321 } 322 if (wait(NULL) < 0) 323 fail_err("wait"); 324 if (info->failed) 325 fail(info->tag, "%s", info->message); 326 ok(info->tag); 327 328 return (0); 329 } 330