diff options
Diffstat (limited to 'tests/sys/file/closefrom_test.c')
| -rw-r--r-- | tests/sys/file/closefrom_test.c | 490 |
1 files changed, 235 insertions, 255 deletions
diff --git a/tests/sys/file/closefrom_test.c b/tests/sys/file/closefrom_test.c index 7dccf858c772..a51e1630e24d 100644 --- a/tests/sys/file/closefrom_test.c +++ b/tests/sys/file/closefrom_test.c @@ -25,13 +25,13 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> /* * Regression tests for the closefrom(2) system call. */ #include <sys/param.h> #include <sys/mman.h> +#include <sys/stat.h> #include <sys/user.h> #include <sys/wait.h> #include <errno.h> @@ -44,67 +44,57 @@ #include <string.h> #include <unistd.h> -struct shared_info { - int failed; - char tag[64]; - char message[0]; -}; +#include <atf-c.h> -static int test = 1; +static char *shared_page; -static void -ok(const char *descr) +/* + * A variant of ATF_REQUIRE that is suitable for use in child + * processes. Since these tests close stderr, errors are reported to + * a shared page of memory checked by the parent process. + */ +#define CHILD_REQUIRE(exp) do { \ + if (!(exp)) \ + child_fail_require(__FILE__, __LINE__, \ + #exp " not met"); \ +} while (0) + +static __dead2 __printflike(3, 4) void +child_fail_require(const char *file, int line, const char *fmt, ...) { + FILE *fp; + va_list ap; - printf("ok %d - %s\n", test, descr); - test++; -} + fp = fmemopen(shared_page, PAGE_SIZE - 1, "w"); + if (fp == NULL) + exit(1); -static void -fail(const char *descr, const char *fmt, ...) -{ - va_list ap; + fprintf(fp, "%s:%d: ", file, line); + va_start(ap, fmt); + vfprintf(fp, fmt, ap); + va_end(ap); + fclose(fp); - printf("not ok %d - %s", test, descr); - test++; - if (fmt) { - va_start(ap, fmt); - printf(" # "); - vprintf(fmt, ap); - va_end(ap); - } - printf("\n"); - exit(1); + exit(0); } -#define fail_err(descr) fail((descr), "%s", strerror(errno)) - -static void -cok(struct shared_info *info, const char *descr) +static pid_t +child_fork(void) { - - info->failed = 0; - strlcpy(info->tag, descr, sizeof(info->tag)); - exit(0); + shared_page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | + MAP_SHARED, -1, 0); + ATF_REQUIRE_MSG(shared_page != MAP_FAILED, "mmap: %s", strerror(errno)); + return (atf_utils_fork()); } static void -cfail(struct shared_info *info, const char *descr, const char *fmt, ...) +child_wait(pid_t pid) { - va_list ap; - - info->failed = 1; - strlcpy(info->tag, descr, sizeof(info->tag)); - if (fmt) { - va_start(ap, fmt); - vsprintf(info->message, fmt, ap); - va_end(ap); - } - exit(0); + atf_utils_wait(pid, 0, "", ""); + if (shared_page[0] != '\0') + atf_tc_fail("%s", shared_page); } -#define cfail_err(info, descr) cfail((info), (descr), "%s", strerror(errno)) - /* * Use kinfo_getfile() to fetch the list of file descriptors and figure out * the highest open file descriptor. @@ -116,9 +106,8 @@ highest_fd(void) int cnt, i, highest; kif = kinfo_getfile(getpid(), &cnt); - if (kif == NULL) - fail_err("kinfo_getfile"); - highest = INT_MIN; + ATF_REQUIRE_MSG(kif != NULL, "kinfo_getfile: %s", strerror(errno)); + highest = -1; for (i = 0; i < cnt; i++) if (kif[i].kf_fd > highest) highest = kif[i].kf_fd; @@ -132,262 +121,253 @@ devnull(void) int fd; fd = open(_PATH_DEVNULL, O_RDONLY); - if (fd < 0) - fail_err("open(\" "_PATH_DEVNULL" \")"); + ATF_REQUIRE_MSG(fd != -1, "open(\" "_PATH_DEVNULL" \"): %s", + strerror(errno)); return (fd); } -int -main(void) +ATF_TC_WITHOUT_HEAD(closefrom_simple); +ATF_TC_BODY(closefrom_simple, tc) { - struct shared_info *info; - pid_t pid; - int fd, flags, i, start; - - printf("1..22\n"); + int fd, start; /* We'd better start up with fd's 0, 1, and 2 open. */ - start = devnull(); - if (start == -1) - fail("open", "bad descriptor %d", start); - ok("open"); + start = highest_fd(); + ATF_REQUIRE(start >= 2); + + fd = devnull(); + ATF_REQUIRE(fd > start); /* Make sure highest_fd() works. */ - fd = highest_fd(); - if (start != fd) - fail("highest_fd", "bad descriptor %d != %d", start, fd); - ok("highest_fd"); - - /* Try to use closefrom() for just closing fd 3. */ - closefrom(start + 1); - fd = highest_fd(); - if (fd != start) - fail("closefrom", "highest fd %d", fd); - ok("closefrom"); + ATF_REQUIRE_INTEQ(fd, highest_fd()); + + /* Try to use closefrom() to close just the new fd. */ + closefrom(fd); + ATF_REQUIRE_INTEQ(start, highest_fd()); +} + +ATF_TC_WITHOUT_HEAD(closefrom_with_holes); +ATF_TC_BODY(closefrom_with_holes, tc) +{ + int i, start; + + start = highest_fd(); /* Eat up 16 descriptors. */ for (i = 0; i < 16; i++) (void)devnull(); - fd = highest_fd(); - if (fd != start + 16) - fail("open 16", "highest fd %d", fd); - ok("open 16"); + + ATF_REQUIRE_INTEQ(start + 16, highest_fd()); /* Close half of them. */ - closefrom(11); - fd = highest_fd(); - if (fd != 10) - fail("closefrom", "highest fd %d", fd); - ok("closefrom"); - - /* Explicitly close descriptors 6 and 8 to create holes. */ - if (close(6) < 0 || close(8) < 0) - fail_err("close2 "); - ok("close 2"); - - /* Verify that close on 6 and 8 fails with EBADF. */ - if (close(6) == 0) - fail("close(6)", "did not fail"); - if (errno != EBADF) - fail_err("close(6)"); - ok("close(6)"); - if (close(8) == 0) - fail("close(8)", "did not fail"); - if (errno != EBADF) - fail_err("close(8)"); - ok("close(8)"); - - /* Close from 4 on. */ - closefrom(4); - fd = highest_fd(); - if (fd != 3) - fail("closefrom", "highest fd %d", fd); - ok("closefrom"); - - /* Allocate a small SHM region for IPC with our child. */ - info = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON | - MAP_SHARED, -1, 0); - if (info == MAP_FAILED) - fail_err("mmap"); - ok("mmap"); - - /* Fork a child process to test closefrom(0). */ - pid = fork(); - if (pid < 0) - fail_err("fork"); + closefrom(start + 9); + ATF_REQUIRE_INTEQ(start + 8, highest_fd()); + + /* Explicitly close two descriptors to create holes. */ + ATF_REQUIRE_MSG(close(start + 3) == 0, "close(start + 3): %s", + strerror(errno)); + ATF_REQUIRE_MSG(close(start + 5) == 0, "close(start + 5): %s", + strerror(errno)); + + /* Verify that close on the closed descriptors fails with EBADF. */ + ATF_REQUIRE_ERRNO(EBADF, close(start + 3) == -1); + ATF_REQUIRE_ERRNO(EBADF, close(start + 5) == -1); + + /* Close most remaining descriptors. */ + closefrom(start + 2); + ATF_REQUIRE_INTEQ(start + 1, highest_fd()); +} + +ATF_TC_WITHOUT_HEAD(closefrom_zero); +ATF_TC_BODY(closefrom_zero, tc) +{ + pid_t pid; + int fd; + + /* Ensure standard descriptors are open. */ + ATF_REQUIRE(highest_fd() >= 2); + + pid = child_fork(); if (pid == 0) { /* Child. */ closefrom(0); fd = highest_fd(); - if (fd >= 0) - cfail(info, "closefrom(0)", "highest fd %d", fd); - cok(info, "closefrom(0)"); + CHILD_REQUIRE(fd == -1); + exit(0); } - if (wait(NULL) < 0) - fail_err("wait"); - if (info->failed) - fail(info->tag, "%s", info->message); - ok(info->tag); - - /* Fork a child process to test closefrom(-1). */ - pid = fork(); - if (pid < 0) - fail_err("fork"); + + child_wait(pid); +} + +ATF_TC_WITHOUT_HEAD(closefrom_negative_one); +ATF_TC_BODY(closefrom_negative_one, tc) +{ + pid_t pid; + int fd; + + /* Ensure standard descriptors are open. */ + ATF_REQUIRE(highest_fd() >= 2); + + pid = child_fork(); if (pid == 0) { /* Child. */ closefrom(-1); fd = highest_fd(); - if (fd >= 0) - cfail(info, "closefrom(-1)", "highest fd %d", fd); - cok(info, "closefrom(-1)"); + CHILD_REQUIRE(fd == -1); + exit(0); } - if (wait(NULL) < 0) - fail_err("wait"); - if (info->failed) - fail(info->tag, "%s", info->message); - ok(info->tag); - - /* Dup stdout to 6. */ - if (dup2(1, 6) < 0) - fail_err("dup2"); - fd = highest_fd(); - if (fd != 6) - fail("dup2", "highest fd %d", fd); - ok("dup2"); + + child_wait(pid); +} + +ATF_TC_WITHOUT_HEAD(closefrom_in_holes); +ATF_TC_BODY(closefrom_in_holes, tc) +{ + int start; + + start = highest_fd(); + ATF_REQUIRE(start >= 2); + + /* Dup stdout to a higher fd. */ + ATF_REQUIRE_INTEQ(start + 4, dup2(1, start + 4)); + ATF_REQUIRE_INTEQ(start + 4, highest_fd()); /* Do a closefrom() starting in a hole. */ - closefrom(4); - fd = highest_fd(); - if (fd != 3) - fail("closefrom", "highest fd %d", fd); - ok("closefrom"); + closefrom(start + 2); + ATF_REQUIRE_INTEQ(start, highest_fd()); /* Do a closefrom() beyond our highest open fd. */ - closefrom(32); - fd = highest_fd(); - if (fd != 3) - fail("closefrom", "highest fd %d", fd); - ok("closefrom"); + closefrom(start + 32); + ATF_REQUIRE_INTEQ(start, highest_fd()); +} + +ATF_TC_WITHOUT_HEAD(closerange_basic); +ATF_TC_BODY(closerange_basic, tc) +{ + struct stat sb; + int i, start; - /* Chew up another 8 fd */ + start = highest_fd(); + + /* Open 8 file descriptors */ for (i = 0; i < 8; i++) (void)devnull(); - fd = highest_fd(); - start = fd - 7; + ATF_REQUIRE_INTEQ(start + 8, highest_fd()); /* close_range() a hole in the middle */ - close_range(start + 3, start + 5, 0); - for (i = start + 3; i < start + 6; ++i) { - if (close(i) == 0 || errno != EBADF) { - --i; - break; - } - } - if (i != start + 6) - fail("close_range", "failed to close at %d in %d - %d", i + 1, - start + 3, start + 6); - ok("close_range"); + ATF_REQUIRE_INTEQ(0, close_range(start + 3, start + 5, 0)); + for (i = start + 3; i < start + 6; ++i) + ATF_REQUIRE_ERRNO(EBADF, fstat(i, &sb) == -1); /* close_range from the middle of the hole */ - close_range(start + 4, start + 6, 0); - if ((i = highest_fd()) != fd) - fail("close_range", "highest fd %d", i); - ok("close_range"); + ATF_REQUIRE_INTEQ(0, close_range(start + 4, start + 6, 0)); + ATF_REQUIRE_INTEQ(start + 8, highest_fd()); /* close_range to the end; effectively closefrom(2) */ - close_range(start + 3, ~0L, 0); - if ((i = highest_fd()) != start + 2) - fail("close_range", "highest fd %d", i); - ok("close_range"); + ATF_REQUIRE_INTEQ(0, close_range(start + 3, ~0L, 0)); + ATF_REQUIRE_INTEQ(start + 2, highest_fd()); /* Now close the rest */ - close_range(start, start + 4, 0); - fd = highest_fd(); - if (fd != 3) - fail("close_range", "highest fd %d", fd); - ok("close_range"); - - /* Fork a child process to test closefrom(0) twice. */ - pid = fork(); - if (pid < 0) - fail_err("fork"); + ATF_REQUIRE_INTEQ(0, close_range(start + 1, start + 4, 0)); + ATF_REQUIRE_INTEQ(start, highest_fd()); +} + +ATF_TC_WITHOUT_HEAD(closefrom_zero_twice); +ATF_TC_BODY(closefrom_zero_twice, tc) +{ + pid_t pid; + int fd; + + /* Ensure standard descriptors are open. */ + ATF_REQUIRE(highest_fd() >= 2); + + pid = child_fork(); if (pid == 0) { /* Child. */ closefrom(0); + fd = highest_fd(); + CHILD_REQUIRE(fd == -1); closefrom(0); - cok(info, "closefrom(0)"); + fd = highest_fd(); + CHILD_REQUIRE(fd == -1); + exit(0); } - if (wait(NULL) < 0) - fail_err("wait"); - if (info->failed) - fail(info->tag, "%s", info->message); - ok(info->tag); - /* test CLOSE_RANGE_CLOEXEC */ + child_wait(pid); +} + +static void +require_fd_flag(int fd, const char *descr, const char *descr2, int flag, + bool set) +{ + int flags; + + flags = fcntl(fd, F_GETFD); + ATF_REQUIRE_MSG(flags >= 0, "fcntl(.., F_GETFD): %s", strerror(errno)); + + if (set) { + ATF_REQUIRE_MSG((flags & flag) == flag, + "%s did not set %s on fd %d", descr, descr2, fd); + } else { + ATF_REQUIRE_MSG((flags & flag) == 0, + "%s set %s when it should not have on fd %d", descr, descr2, + fd); + } +} + +ATF_TC_WITHOUT_HEAD(closerange_CLOEXEC); +ATF_TC_BODY(closerange_CLOEXEC, tc) +{ + int i, start; + + start = highest_fd(); + ATF_REQUIRE(start >= 2); + for (i = 0; i < 8; i++) (void)devnull(); - fd = highest_fd(); - start = fd - 8; - if (close_range(start + 1, start + 4, CLOSE_RANGE_CLOEXEC) < 0) - fail_err("close_range(..., CLOSE_RANGE_CLOEXEC)"); - flags = fcntl(start, F_GETFD); - if (flags < 0) - fail_err("fcntl(.., F_GETFD)"); - if ((flags & FD_CLOEXEC) != 0) - fail("close_range", "CLOSE_RANGE_CLOEXEC set close-on-exec " - "when it should not have on fd %d", start); - for (i = start + 1; i <= start + 4; i++) { - flags = fcntl(i, F_GETFD); - if (flags < 0) - fail_err("fcntl(.., F_GETFD)"); - if ((flags & FD_CLOEXEC) == 0) - fail("close_range", "CLOSE_RANGE_CLOEXEC did not set " - "close-on-exec on fd %d", i); - } - for (; i < start + 8; i++) { - flags = fcntl(i, F_GETFD); - if (flags < 0) - fail_err("fcntl(.., F_GETFD)"); - if ((flags & FD_CLOEXEC) != 0) - fail("close_range", "CLOSE_RANGE_CLOEXEC set close-on-exec " - "when it should not have on fd %d", i); + ATF_REQUIRE_INTEQ(start + 8, highest_fd()); + + ATF_REQUIRE_INTEQ(0, close_range(start + 2, start + 5, + CLOSE_RANGE_CLOEXEC)); + for (i = 1; i < 9; i++) { + require_fd_flag(start + i, "CLOSE_RANGE_CLOEXEC", + "close-on-exec", FD_CLOEXEC, i >= 2 && i <= 5); } - if (close_range(start, start + 8, 0) < 0) - fail_err("close_range"); - ok("close_range(..., CLOSE_RANGE_CLOEXEC)"); + ATF_REQUIRE_INTEQ(0, close_range(start + 1, start + 8, 0)); +} + +ATF_TC_WITHOUT_HEAD(closerange_CLOFORK); +ATF_TC_BODY(closerange_CLOFORK, tc) +{ + int i, start; + + start = highest_fd(); + ATF_REQUIRE(start >= 2); - /* test CLOSE_RANGE_CLOFORK */ for (i = 0; i < 8; i++) (void)devnull(); - fd = highest_fd(); - start = fd - 8; - if (close_range(start + 1, start + 4, CLOSE_RANGE_CLOFORK) < 0) - fail_err("close_range(..., CLOSE_RANGE_CLOFORK)"); - flags = fcntl(start, F_GETFD); - if (flags < 0) - fail_err("fcntl(.., F_GETFD)"); - if ((flags & FD_CLOFORK) != 0) - fail("close_range", "CLOSE_RANGE_CLOFORK set close-on-exec " - "when it should not have on fd %d", start); - for (i = start + 1; i <= start + 4; i++) { - flags = fcntl(i, F_GETFD); - if (flags < 0) - fail_err("fcntl(.., F_GETFD)"); - if ((flags & FD_CLOFORK) == 0) - fail("close_range", "CLOSE_RANGE_CLOFORK did not set " - "close-on-exec on fd %d", i); - } - for (; i < start + 8; i++) { - flags = fcntl(i, F_GETFD); - if (flags < 0) - fail_err("fcntl(.., F_GETFD)"); - if ((flags & FD_CLOFORK) != 0) - fail("close_range", "CLOSE_RANGE_CLOFORK set close-on-exec " - "when it should not have on fd %d", i); + ATF_REQUIRE_INTEQ(start + 8, highest_fd()); + + ATF_REQUIRE_INTEQ(0, close_range(start + 2, start + 5, + CLOSE_RANGE_CLOFORK)); + for (i = 1; i < 9; i++) { + require_fd_flag(start + i, "CLOSE_RANGE_CLOFORK", + "close-on-fork", FD_CLOFORK, i >= 2 && i <= 5); } - if (close_range(start, start + 8, 0) < 0) - fail_err("close_range"); - ok("close_range(..., CLOSE_RANGE_CLOFORK)"); + ATF_REQUIRE_INTEQ(0, close_range(start + 1, start + 8, 0)); +} - return (0); +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, closefrom_simple); + ATF_TP_ADD_TC(tp, closefrom_with_holes); + ATF_TP_ADD_TC(tp, closefrom_zero); + ATF_TP_ADD_TC(tp, closefrom_negative_one); + ATF_TP_ADD_TC(tp, closefrom_in_holes); + ATF_TP_ADD_TC(tp, closerange_basic); + ATF_TP_ADD_TC(tp, closefrom_zero_twice); + ATF_TP_ADD_TC(tp, closerange_CLOEXEC); + ATF_TP_ADD_TC(tp, closerange_CLOFORK); + + return (atf_no_error()); } |
