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 /* 29 * Regression tests for the closefrom(2) system call. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/mman.h> 34 #include <sys/stat.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 #include <atf-c.h> 48 49 static char *shared_page; 50 51 /* 52 * A variant of ATF_REQUIRE that is suitable for use in child 53 * processes. Since these tests close stderr, errors are reported to 54 * a shared page of memory checked by the parent process. 55 */ 56 #define CHILD_REQUIRE(exp) do { \ 57 if (!(exp)) \ 58 child_fail_require(__FILE__, __LINE__, \ 59 #exp " not met"); \ 60 } while (0) 61 62 static __dead2 __printflike(3, 4) void 63 child_fail_require(const char *file, int line, const char *fmt, ...) 64 { 65 FILE *fp; 66 va_list ap; 67 68 fp = fmemopen(shared_page, PAGE_SIZE - 1, "w"); 69 if (fp == NULL) 70 exit(1); 71 72 fprintf(fp, "%s:%d: ", file, line); 73 va_start(ap, fmt); 74 vfprintf(fp, fmt, ap); 75 va_end(ap); 76 fclose(fp); 77 78 exit(0); 79 } 80 81 static pid_t 82 child_fork(void) 83 { 84 shared_page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | 85 MAP_SHARED, -1, 0); 86 ATF_REQUIRE_MSG(shared_page != MAP_FAILED, "mmap: %s", strerror(errno)); 87 return (atf_utils_fork()); 88 } 89 90 static void 91 child_wait(pid_t pid) 92 { 93 atf_utils_wait(pid, 0, "", ""); 94 if (shared_page[0] != '\0') 95 atf_tc_fail("%s", shared_page); 96 } 97 98 /* 99 * Use kinfo_getfile() to fetch the list of file descriptors and figure out 100 * the highest open file descriptor. 101 */ 102 static int 103 highest_fd(void) 104 { 105 struct kinfo_file *kif; 106 int cnt, i, highest; 107 108 kif = kinfo_getfile(getpid(), &cnt); 109 ATF_REQUIRE_MSG(kif != NULL, "kinfo_getfile: %s", strerror(errno)); 110 highest = -1; 111 for (i = 0; i < cnt; i++) 112 if (kif[i].kf_fd > highest) 113 highest = kif[i].kf_fd; 114 free(kif); 115 return (highest); 116 } 117 118 static int 119 devnull(void) 120 { 121 int fd; 122 123 fd = open(_PATH_DEVNULL, O_RDONLY); 124 ATF_REQUIRE_MSG(fd != -1, "open(\" "_PATH_DEVNULL" \"): %s", 125 strerror(errno)); 126 return (fd); 127 } 128 129 ATF_TC_WITHOUT_HEAD(closefrom_simple); 130 ATF_TC_BODY(closefrom_simple, tc) 131 { 132 int fd, start; 133 134 /* We'd better start up with fd's 0, 1, and 2 open. */ 135 start = highest_fd(); 136 ATF_REQUIRE(start >= 2); 137 138 fd = devnull(); 139 ATF_REQUIRE(fd > start); 140 141 /* Make sure highest_fd() works. */ 142 ATF_REQUIRE_INTEQ(fd, highest_fd()); 143 144 /* Try to use closefrom() to close just the new fd. */ 145 closefrom(fd); 146 ATF_REQUIRE_INTEQ(start, highest_fd()); 147 } 148 149 ATF_TC_WITHOUT_HEAD(closefrom_with_holes); 150 ATF_TC_BODY(closefrom_with_holes, tc) 151 { 152 int i, start; 153 154 start = highest_fd(); 155 156 /* Eat up 16 descriptors. */ 157 for (i = 0; i < 16; i++) 158 (void)devnull(); 159 160 ATF_REQUIRE_INTEQ(start + 16, highest_fd()); 161 162 /* Close half of them. */ 163 closefrom(start + 9); 164 ATF_REQUIRE_INTEQ(start + 8, highest_fd()); 165 166 /* Explicitly close two descriptors to create holes. */ 167 ATF_REQUIRE_MSG(close(start + 3) == 0, "close(start + 3): %s", 168 strerror(errno)); 169 ATF_REQUIRE_MSG(close(start + 5) == 0, "close(start + 5): %s", 170 strerror(errno)); 171 172 /* Verify that close on the closed descriptors fails with EBADF. */ 173 ATF_REQUIRE_ERRNO(EBADF, close(start + 3) == -1); 174 ATF_REQUIRE_ERRNO(EBADF, close(start + 5) == -1); 175 176 /* Close most remaining descriptors. */ 177 closefrom(start + 2); 178 ATF_REQUIRE_INTEQ(start + 1, highest_fd()); 179 } 180 181 ATF_TC_WITHOUT_HEAD(closefrom_zero); 182 ATF_TC_BODY(closefrom_zero, tc) 183 { 184 pid_t pid; 185 int fd; 186 187 /* Ensure standard descriptors are open. */ 188 ATF_REQUIRE(highest_fd() >= 2); 189 190 pid = child_fork(); 191 if (pid == 0) { 192 /* Child. */ 193 closefrom(0); 194 fd = highest_fd(); 195 CHILD_REQUIRE(fd == -1); 196 exit(0); 197 } 198 199 child_wait(pid); 200 } 201 202 ATF_TC_WITHOUT_HEAD(closefrom_negative_one); 203 ATF_TC_BODY(closefrom_negative_one, tc) 204 { 205 pid_t pid; 206 int fd; 207 208 /* Ensure standard descriptors are open. */ 209 ATF_REQUIRE(highest_fd() >= 2); 210 211 pid = child_fork(); 212 if (pid == 0) { 213 /* Child. */ 214 closefrom(-1); 215 fd = highest_fd(); 216 CHILD_REQUIRE(fd == -1); 217 exit(0); 218 } 219 220 child_wait(pid); 221 } 222 223 ATF_TC_WITHOUT_HEAD(closefrom_in_holes); 224 ATF_TC_BODY(closefrom_in_holes, tc) 225 { 226 int start; 227 228 start = highest_fd(); 229 ATF_REQUIRE(start >= 2); 230 231 /* Dup stdout to a higher fd. */ 232 ATF_REQUIRE_INTEQ(start + 4, dup2(1, start + 4)); 233 ATF_REQUIRE_INTEQ(start + 4, highest_fd()); 234 235 /* Do a closefrom() starting in a hole. */ 236 closefrom(start + 2); 237 ATF_REQUIRE_INTEQ(start, highest_fd()); 238 239 /* Do a closefrom() beyond our highest open fd. */ 240 closefrom(start + 32); 241 ATF_REQUIRE_INTEQ(start, highest_fd()); 242 } 243 244 ATF_TC_WITHOUT_HEAD(closerange_basic); 245 ATF_TC_BODY(closerange_basic, tc) 246 { 247 struct stat sb; 248 int i, start; 249 250 start = highest_fd(); 251 252 /* Open 8 file descriptors */ 253 for (i = 0; i < 8; i++) 254 (void)devnull(); 255 ATF_REQUIRE_INTEQ(start + 8, highest_fd()); 256 257 /* close_range() a hole in the middle */ 258 ATF_REQUIRE_INTEQ(0, close_range(start + 3, start + 5, 0)); 259 for (i = start + 3; i < start + 6; ++i) 260 ATF_REQUIRE_ERRNO(EBADF, fstat(i, &sb) == -1); 261 262 /* close_range from the middle of the hole */ 263 ATF_REQUIRE_INTEQ(0, close_range(start + 4, start + 6, 0)); 264 ATF_REQUIRE_INTEQ(start + 8, highest_fd()); 265 266 /* close_range to the end; effectively closefrom(2) */ 267 ATF_REQUIRE_INTEQ(0, close_range(start + 3, ~0L, 0)); 268 ATF_REQUIRE_INTEQ(start + 2, highest_fd()); 269 270 /* Now close the rest */ 271 ATF_REQUIRE_INTEQ(0, close_range(start + 1, start + 4, 0)); 272 ATF_REQUIRE_INTEQ(start, highest_fd()); 273 } 274 275 ATF_TC_WITHOUT_HEAD(closefrom_zero_twice); 276 ATF_TC_BODY(closefrom_zero_twice, tc) 277 { 278 pid_t pid; 279 int fd; 280 281 /* Ensure standard descriptors are open. */ 282 ATF_REQUIRE(highest_fd() >= 2); 283 284 pid = child_fork(); 285 if (pid == 0) { 286 /* Child. */ 287 closefrom(0); 288 fd = highest_fd(); 289 CHILD_REQUIRE(fd == -1); 290 closefrom(0); 291 fd = highest_fd(); 292 CHILD_REQUIRE(fd == -1); 293 exit(0); 294 } 295 296 child_wait(pid); 297 } 298 299 static void 300 require_fd_flag(int fd, const char *descr, const char *descr2, int flag, 301 bool set) 302 { 303 int flags; 304 305 flags = fcntl(fd, F_GETFD); 306 ATF_REQUIRE_MSG(flags >= 0, "fcntl(.., F_GETFD): %s", strerror(errno)); 307 308 if (set) { 309 ATF_REQUIRE_MSG((flags & flag) == flag, 310 "%s did not set %s on fd %d", descr, descr2, fd); 311 } else { 312 ATF_REQUIRE_MSG((flags & flag) == 0, 313 "%s set %s when it should not have on fd %d", descr, descr2, 314 fd); 315 } 316 } 317 318 ATF_TC_WITHOUT_HEAD(closerange_CLOEXEC); 319 ATF_TC_BODY(closerange_CLOEXEC, tc) 320 { 321 int i, start; 322 323 start = highest_fd(); 324 ATF_REQUIRE(start >= 2); 325 326 for (i = 0; i < 8; i++) 327 (void)devnull(); 328 ATF_REQUIRE_INTEQ(start + 8, highest_fd()); 329 330 ATF_REQUIRE_INTEQ(0, close_range(start + 2, start + 5, 331 CLOSE_RANGE_CLOEXEC)); 332 for (i = 1; i < 9; i++) { 333 require_fd_flag(start + i, "CLOSE_RANGE_CLOEXEC", 334 "close-on-exec", FD_CLOEXEC, i >= 2 && i <= 5); 335 } 336 ATF_REQUIRE_INTEQ(0, close_range(start + 1, start + 8, 0)); 337 } 338 339 ATF_TC_WITHOUT_HEAD(closerange_CLOFORK); 340 ATF_TC_BODY(closerange_CLOFORK, tc) 341 { 342 int i, start; 343 344 start = highest_fd(); 345 ATF_REQUIRE(start >= 2); 346 347 for (i = 0; i < 8; i++) 348 (void)devnull(); 349 ATF_REQUIRE_INTEQ(start + 8, highest_fd()); 350 351 ATF_REQUIRE_INTEQ(0, close_range(start + 2, start + 5, 352 CLOSE_RANGE_CLOFORK)); 353 for (i = 1; i < 9; i++) { 354 require_fd_flag(start + i, "CLOSE_RANGE_CLOFORK", 355 "close-on-fork", FD_CLOFORK, i >= 2 && i <= 5); 356 } 357 ATF_REQUIRE_INTEQ(0, close_range(start + 1, start + 8, 0)); 358 } 359 360 ATF_TP_ADD_TCS(tp) 361 { 362 ATF_TP_ADD_TC(tp, closefrom_simple); 363 ATF_TP_ADD_TC(tp, closefrom_with_holes); 364 ATF_TP_ADD_TC(tp, closefrom_zero); 365 ATF_TP_ADD_TC(tp, closefrom_negative_one); 366 ATF_TP_ADD_TC(tp, closefrom_in_holes); 367 ATF_TP_ADD_TC(tp, closerange_basic); 368 ATF_TP_ADD_TC(tp, closefrom_zero_twice); 369 ATF_TP_ADD_TC(tp, closerange_CLOEXEC); 370 ATF_TP_ADD_TC(tp, closerange_CLOFORK); 371 372 return (atf_no_error()); 373 } 374