diff options
Diffstat (limited to 'tests')
29 files changed, 1145 insertions, 310 deletions
diff --git a/tests/atf_python/ktest.py b/tests/atf_python/ktest.py index a18f47d1dd06..a671aaa1fd4c 100644 --- a/tests/atf_python/ktest.py +++ b/tests/atf_python/ktest.py @@ -67,6 +67,10 @@ class KtestLoader(object): def __init__(self, module_name: str, autoload: bool): self.module_name = module_name self.autoload = autoload + # Ensure the base ktest.ko module is loaded + result = libc.kldload("ktest") + if result != 0 and result != 17: # 17 is EEXIST (already loaded) + logger.debug(f"Failed to load base ktest module (error {result})") self.helper = NlHelper() self.nlsock = Nlsock(NlConst.NETLINK_GENERIC, self.helper) self.family_id = self._get_family_id() @@ -76,7 +80,9 @@ class KtestLoader(object): family_id = self.nlsock.get_genl_family_id(NETLINK_FAMILY) except ValueError: if self.autoload: - libc.kldload(self.module_name) + result = libc.kldload(self.module_name) + if result != 0 and result != 17: # 17 is EEXIST (already loaded) + raise RuntimeError(f"Failed to load kernel module '{self.module_name}' (error {result})") family_id = self.nlsock.get_genl_family_id(NETLINK_FAMILY) else: raise @@ -103,7 +109,9 @@ class KtestLoader(object): def load_ktests(self): ret = self._load_ktests() if not ret and self.autoload: - libc.kldload(self.module_name) + result = libc.kldload(self.module_name) + if result != 0 and result != 17: # 17 is EEXIST (already loaded) + raise RuntimeError(f"Failed to load kernel module '{self.module_name}' (error {result})") ret = self._load_ktests() return ret diff --git a/tests/sys/file/Makefile b/tests/sys/file/Makefile index beb4452359b7..c1fcef68d08e 100644 --- a/tests/sys/file/Makefile +++ b/tests/sys/file/Makefile @@ -3,7 +3,7 @@ TESTSDIR= ${TESTSBASE}/sys/file BINDIR= ${TESTSDIR} ATF_TESTS_C+= path_test -TAP_TESTS_C+= closefrom_test +ATF_TESTS_C+= closefrom_test TAP_TESTS_C+= dup_test ATF_TESTS_C+= fcntlflags_test TAP_TESTS_SH+= flock_test 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()); } diff --git a/tests/sys/fs/fusefs/bad_server.cc b/tests/sys/fs/fusefs/bad_server.cc index af2ca146e431..825523cac2bb 100644 --- a/tests/sys/fs/fusefs/bad_server.cc +++ b/tests/sys/fs/fusefs/bad_server.cc @@ -64,6 +64,11 @@ TEST_F(BadServer, ShortWrite) out.header.error = 0; out.header.unique = 0; // Asynchronous notification out.expected_errno = EINVAL; + /* + * Tell the event loop to quit. The kernel will disconnect us + * because of the short write. + */ + m_mock->m_expect_unmount = true; m_mock->write_response(out); } @@ -93,7 +98,7 @@ TEST_F(BadServer, ErrorWithPayload) out.push_back(std::move(out1)); // The kernel may disconnect us for bad behavior, so don't try - // to read any more. + // to read or write any more. m_mock->m_quit = true; })); diff --git a/tests/sys/fs/fusefs/bmap.cc b/tests/sys/fs/fusefs/bmap.cc index 30612079657d..e61dadb6d79e 100644 --- a/tests/sys/fs/fusefs/bmap.cc +++ b/tests/sys/fs/fusefs/bmap.cc @@ -178,6 +178,93 @@ TEST_F(Bmap, default_) } /* + * The server returns an error for some reason for FUSE_BMAP. fusefs should + * faithfully report that error up to the caller. + */ +TEST_F(Bmap, einval) +{ + struct fiobmap2_arg arg; + const off_t filesize = 1 << 30; + int64_t lbn = 100; + const ino_t ino = 42; + int fd; + + expect_lookup(RELPATH, 42, filesize); + expect_open(ino, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_BMAP && + in.header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EINVAL))); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + + arg.bn = lbn; + arg.runp = -1; + arg.runb = -1; + ASSERT_EQ(-1, ioctl(fd, FIOBMAP2, &arg)); + EXPECT_EQ(EINVAL, errno); + + leak(fd); +} + +/* + * Even if the server returns EINVAL during VOP_BMAP, we should still be able + * to successfully read a block. This is a regression test for + * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=264196 . The bug did not + * lie in fusefs, but this is a convenient place for a regression test. + */ +TEST_F(Bmap, spurious_einval) +{ + const off_t filesize = 4ull << 30; + const ino_t ino = 42; + int fd, r; + char buf[1]; + + expect_lookup(RELPATH, 42, filesize); + expect_open(ino, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_BMAP && + in.header.nodeid == ino); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnErrno(EINVAL))); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in.header.opcode == FUSE_READ && + in.header.nodeid == ino && + in.body.read.offset == 0 && + in.body.read.size == (uint64_t)m_maxbcachebuf); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) { + size_t osize = in.body.read.size; + + assert(osize < sizeof(out.body.bytes)); + out.header.len = sizeof(struct fuse_out_header) + osize; + bzero(out.body.bytes, osize); + }))); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + + /* + * Read the same block multiple times. On a system affected by PR + * 264196 , the second read will fail. + */ + r = read(fd, buf, sizeof(buf)); + EXPECT_EQ(r, 1) << strerror(errno); + r = read(fd, buf, sizeof(buf)); + EXPECT_EQ(r, 1) << strerror(errno); + r = read(fd, buf, sizeof(buf)); + EXPECT_EQ(r, 1) << strerror(errno); +} + +/* * VOP_BMAP should not query the server for the file's size, even if its cached * attributes have expired. * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937 diff --git a/tests/sys/fs/fusefs/mockfs.cc b/tests/sys/fs/fusefs/mockfs.cc index e8081dea9604..b6a32d9b60af 100644 --- a/tests/sys/fs/fusefs/mockfs.cc +++ b/tests/sys/fs/fusefs/mockfs.cc @@ -433,7 +433,8 @@ MockFS::MockFS(int max_read, int max_readahead, bool allow_other, m_child_pid(-1), m_maxwrite(MIN(max_write, max_max_write)), m_nready(-1), - m_quit(false) + m_quit(false), + m_expect_unmount(false) { struct sigaction sa; struct iovec *iov = NULL; @@ -827,10 +828,12 @@ void MockFS::loop() { } } -int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen) +int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen, + int expected_errno) { std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); + out->expected_errno = expected_errno; out->header.unique = 0; /* 0 means asynchronous notification */ out->header.error = FUSE_NOTIFY_INVAL_ENTRY; out->body.inval_entry.parent = parent; @@ -977,7 +980,7 @@ void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) { } res = read(m_fuse_fd, &in, sizeof(in)); - if (res < 0 && !m_quit) { + if (res < 0 && errno != EBADF && !m_quit && !m_expect_unmount) { m_quit = true; FAIL() << "read: " << strerror(errno); } diff --git a/tests/sys/fs/fusefs/mockfs.hh b/tests/sys/fs/fusefs/mockfs.hh index ba6f7fded9d0..f98a5337c9d1 100644 --- a/tests/sys/fs/fusefs/mockfs.hh +++ b/tests/sys/fs/fusefs/mockfs.hh @@ -360,6 +360,9 @@ class MockFS { /* Tell the daemon to shut down ASAP */ bool m_quit; + /* Tell the daemon that the server might forcibly unmount us */ + bool m_expect_unmount; + /* Create a new mockfs and mount it to a tempdir */ MockFS(int max_read, int max_readahead, bool allow_other, bool default_permissions, bool push_symlinks_in, bool ro, @@ -390,8 +393,10 @@ class MockFS { * @param parent Parent directory's inode number * @param name name of dirent to invalidate * @param namelen size of name, including the NUL + * @param expected_errno The error that write() should return */ - int notify_inval_entry(ino_t parent, const char *name, size_t namelen); + int notify_inval_entry(ino_t parent, const char *name, size_t namelen, + int expected_errno = 0); /* * Send an asynchronous notification to invalidate an inode's cached diff --git a/tests/sys/fs/fusefs/notify.cc b/tests/sys/fs/fusefs/notify.cc index 1e22bde13db7..d370a1e6e706 100644 --- a/tests/sys/fs/fusefs/notify.cc +++ b/tests/sys/fs/fusefs/notify.cc @@ -385,6 +385,27 @@ TEST_F(Notify, inval_inode_with_clean_cache) leak(fd); } +/* + * Attempting to invalidate an entry or inode after unmounting should fail, but + * nothing bad should happen. + * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=290519 + */ +TEST_F(Notify, notify_after_unmount) +{ + const static char *name = "foo"; + struct inval_entry_args iea; + + expect_destroy(0); + + m_mock->unmount(); + + iea.mock = m_mock; + iea.parent = FUSE_ROOT_ID; + iea.name = name; + iea.namelen = strlen(name); + iea.mock->notify_inval_entry(iea.parent, iea.name, iea.namelen, ENODEV); +} + /* FUSE_NOTIFY_STORE with a file that's not in the entry cache */ /* disabled because FUSE_NOTIFY_STORE is not yet implemented */ TEST_F(Notify, DISABLED_store_nonexistent) @@ -544,3 +565,38 @@ TEST_F(NotifyWriteback, inval_inode_attrs_only) leak(fd); } + +/* + * Attempting asynchronous invalidation of an Entry before mounting the file + * system should fail, but nothing bad should happen. + * + * Note that invalidating an inode before mount goes through the same path, and + * is not separately tested. + * + * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=290519 + */ +TEST(PreMount, inval_entry_before_mount) +{ + const static char name[] = "foo"; + size_t namelen = strlen(name); + struct mockfs_buf_out *out; + int r; + int fuse_fd; + + fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); + ASSERT_GE(fuse_fd, 0) << strerror(errno); + + out = new mockfs_buf_out; + out->header.unique = 0; /* 0 means asynchronous notification */ + out->header.error = FUSE_NOTIFY_INVAL_ENTRY; + out->body.inval_entry.parent = FUSE_ROOT_ID; + out->body.inval_entry.namelen = namelen; + strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry), + name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry)); + out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) + + namelen; + r = write(fuse_fd, out, out->header.len); + EXPECT_EQ(-1, r); + EXPECT_EQ(ENODEV, errno); + delete out; +} diff --git a/tests/sys/geom/class/Makefile b/tests/sys/geom/class/Makefile index 3cf3a15273ac..c58ebc0f72a0 100644 --- a/tests/sys/geom/class/Makefile +++ b/tests/sys/geom/class/Makefile @@ -9,6 +9,7 @@ TESTS_SUBDIRS+= concat TESTS_SUBDIRS+= eli .endif TESTS_SUBDIRS+= gate +TESTS_SUBDIRS+= label TESTS_SUBDIRS+= mirror TESTS_SUBDIRS+= multipath TESTS_SUBDIRS+= nop diff --git a/tests/sys/geom/class/label/Makefile b/tests/sys/geom/class/label/Makefile new file mode 100644 index 000000000000..d0072a52c47c --- /dev/null +++ b/tests/sys/geom/class/label/Makefile @@ -0,0 +1,7 @@ +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/sys/geom/class/${.CURDIR:T} + +ATF_TESTS_SH+= basic + +.include <bsd.test.mk> diff --git a/tests/sys/geom/class/label/basic.sh b/tests/sys/geom/class/label/basic.sh new file mode 100755 index 000000000000..b67b41567c02 --- /dev/null +++ b/tests/sys/geom/class/label/basic.sh @@ -0,0 +1,59 @@ +#!/bin/sh +# +# SPDX-License-Identifier: BSD-2-Clause-FreeBSD +# +# Copyright (c) 2025 Brad Davis +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +class=label +. $(atf_get_srcdir)/../geom_subr.sh + +atf_test_case create cleanup +create_head() +{ + atf_set "descr" "Create and verify GEOM labels" + atf_set "require.user" "root" +} +create_body() +{ + geom_atf_test_setup + + f1=$(mktemp ${class}.XXXXXX) + atf_check truncate -s 32M "$f1" + attach_md md -t vnode -f "$f1" + + atf_check -s exit:0 -o match:"^Done." glabel create -v test "/dev/$md" + atf_check -s exit:0 -o match:"^label/test N/A $md$" glabel status "/dev/$md" + atf_check -s exit:0 -o match:"^/dev/label/test$" ls /dev/label/test + atf_check -s exit:0 glabel stop test +} +create_cleanup() +{ + geom_test_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case create +} diff --git a/tests/sys/kern/unix_stream.c b/tests/sys/kern/unix_stream.c index 49d621dc5b0a..442b766ac885 100644 --- a/tests/sys/kern/unix_stream.c +++ b/tests/sys/kern/unix_stream.c @@ -1,6 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * + * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org> * Copyright (c) 2018 Alan Somers * * Redistribution and use in source and binary forms, with or without @@ -30,6 +31,7 @@ #include <sys/event.h> #include <sys/select.h> #include <sys/sysctl.h> +#include <sys/time.h> #include <sys/un.h> #include <errno.h> #include <fcntl.h> @@ -490,6 +492,30 @@ ATF_TC_BODY(ourshutdown_kevent, tc) close(sv[1]); } +ATF_TC_WITHOUT_HEAD(SO_SNDTIMEO); +ATF_TC_BODY(SO_SNDTIMEO, tc) +{ + struct timespec tp1, tp2, rtp, sleep = { .tv_nsec = 100000000 }; + int sv[2]; + char buf[10]; + + full_socketpair(sv); + ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDTIMEO, + &(struct timeval){ .tv_usec = sleep.tv_nsec / 1000 }, + sizeof(struct timeval))); + ATF_REQUIRE_EQ(0, clock_gettime(CLOCK_MONOTONIC_PRECISE, &tp1)); + ATF_REQUIRE_EQ(-1, send(sv[0], buf, sizeof(buf), 0)); + ATF_REQUIRE(errno == EAGAIN); + ATF_REQUIRE_EQ(0, clock_gettime(CLOCK_MONOTONIC_PRECISE, &tp2)); + timespecsub(&tp2, &tp1, &rtp); + ATF_REQUIRE(timespeccmp(&rtp, &sleep, >=)); + ATF_REQUIRE_EQ(sizeof(buf), recv(sv[1], buf, sizeof(buf), 0)); + ATF_REQUIRE_EQ(sizeof(buf), send(sv[0], buf, sizeof(buf), 0)); + + close(sv[0]); + close(sv[1]); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, getpeereid); @@ -506,6 +532,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, peershutdown_wakeup_poll); ATF_TP_ADD_TC(tp, peershutdown_wakeup_kevent); ATF_TP_ADD_TC(tp, ourshutdown_kevent); + ATF_TP_ADD_TC(tp, SO_SNDTIMEO); return atf_no_error(); } diff --git a/tests/sys/netinet/Makefile b/tests/sys/netinet/Makefile index b742342beecb..9739221676ce 100644 --- a/tests/sys/netinet/Makefile +++ b/tests/sys/netinet/Makefile @@ -30,6 +30,7 @@ ATF_TESTS_SH= arp \ ATF_TESTS_PYTEST+= carp.py ATF_TESTS_PYTEST+= igmp.py +ATF_TESTS_PYTEST+= tcp_hpts_test.py LIBADD.so_reuseport_lb_test= pthread LIBADD.udp_bindings= pthread diff --git a/tests/sys/netinet/carp.sh b/tests/sys/netinet/carp.sh index 2aae2854826e..568d2beaf914 100755 --- a/tests/sys/netinet/carp.sh +++ b/tests/sys/netinet/carp.sh @@ -215,6 +215,9 @@ unicast_v4_body() unicast_v4_cleanup() { + jexec carp_uni_v4_one killall routed + jexec carp_uni_v4_two killall routed + jexec carp_uni_v4_three killall routed vnet_cleanup } diff --git a/tests/sys/netinet/multicast-receive.c b/tests/sys/netinet/multicast-receive.c index 81d0f10f5cfe..62fc68200dd6 100644 --- a/tests/sys/netinet/multicast-receive.c +++ b/tests/sys/netinet/multicast-receive.c @@ -36,6 +36,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sysexits.h> #include <limits.h> #include <err.h> @@ -93,8 +94,9 @@ usage: .imr_multiaddr = maddr, .imr_interface = ifaddr, }; - assert(setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, - sizeof(mreq)) == 0); + if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) != 0) + err(EX_OSERR, "setsockopt"); } else if (strcmp(argv[1], "ip_mreqn") == 0) { /* * ip_mreqn shall be used with index, but for testing @@ -105,8 +107,9 @@ usage: .imr_address = index ? (struct in_addr){ 0 } : ifaddr, .imr_ifindex = index ? ifindex : 0, }; - assert(setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, - sizeof(mreqn)) == 0); + if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, + sizeof(mreqn)) != 0) + err(EX_OSERR, "setsockopt"); } else if (strcmp(argv[1], "group_req") == 0) { if (!index) errx(1, "group_req expects index"); @@ -116,8 +119,9 @@ usage: gsa->sin_family = AF_INET; gsa->sin_len = sizeof(struct sockaddr_in); gsa->sin_addr = maddr; - assert(setsockopt(s, IPPROTO_IP, MCAST_JOIN_GROUP, &greq, - sizeof(greq)) == 0); + if (setsockopt(s, IPPROTO_IP, MCAST_JOIN_GROUP, &greq, + sizeof(greq)) != 0) + err(EX_OSERR, "setsockopt"); } else goto usage; diff --git a/tests/sys/netinet/multicast.sh b/tests/sys/netinet/multicast.sh index a3854fd2fd20..34094ff08705 100755 --- a/tests/sys/netinet/multicast.sh +++ b/tests/sys/netinet/multicast.sh @@ -45,6 +45,15 @@ multicast_vnet_init() jexec mjail2 ifconfig ${epair2}b 192.0.3.2/24 } +multicast_join() +{ + jexec mjail2 $(atf_get_srcdir)/multicast-receive \ + $1 233.252.0.1 6676 $2 > out & pid=$! + while ! jexec mjail2 ifmcstat | grep -q 233\.252\.0\.1; do + sleep 0.01 + done +} + atf_test_case "IP_ADD_MEMBERSHIP_ip_mreq" "cleanup" IP_ADD_MEMBERSHIP_ip_mreq_head() { @@ -56,8 +65,7 @@ IP_ADD_MEMBERSHIP_ip_mreq_body() multicast_vnet_init # join group on interface with IP address 192.0.2.2 - jexec mjail2 $(atf_get_srcdir)/multicast-receive \ - ip_mreq 233.252.0.1 6676 192.0.2.2 > out & pid=$! + multicast_join ip_mreq 192.0.2.2 atf_check -s exit:0 -o empty \ jexec mjail1 $(atf_get_srcdir)/multicast-send \ 0.0.0.0 6676 233.252.0.1 6676 192.0.2.1 hello @@ -65,8 +73,28 @@ IP_ADD_MEMBERSHIP_ip_mreq_body() atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out # join group on interface with IP address 192.0.3.2 - jexec mjail2 $(atf_get_srcdir)/multicast-receive \ - ip_mreq 233.252.0.1 6676 192.0.3.2 > out & pid=$! + multicast_join ip_mreq 192.0.3.2 + atf_check -s exit:0 -o empty \ + jexec mjail1 $(atf_get_srcdir)/multicast-send \ + 0.0.0.0 6676 233.252.0.1 6676 192.0.3.1 hello + atf_check -s exit:0 sh -c "wait $pid; exit $?" + atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out + + # join group on the first multicast capable interface (epair1a) + multicast_join ip_mreq 0.0.0.0 + atf_check -s exit:0 -o empty \ + jexec mjail1 $(atf_get_srcdir)/multicast-send \ + 0.0.0.0 6676 233.252.0.1 6676 192.0.2.1 hello + atf_check -s exit:0 sh -c "wait $pid; exit $?" + atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out + + # Set up the receiving jail so that first multicast capable interface + # is epair1a and default route points into epair2a. This will allow us + # to exercise both branches of inp_lookup_mcast_ifp(). + jexec mjail2 route add default 192.0.3.254 + + # join group on the interface determined by the route lookup + multicast_join ip_mreq 0.0.0.0 atf_check -s exit:0 -o empty \ jexec mjail1 $(atf_get_srcdir)/multicast-send \ 0.0.0.0 6676 233.252.0.1 6676 192.0.3.1 hello @@ -90,8 +118,7 @@ IP_ADD_MEMBERSHIP_ip_mreqn_body() multicast_vnet_init # join group on interface epair2 - jexec mjail2 $(atf_get_srcdir)/multicast-receive \ - ip_mreqn 233.252.0.1 6676 ${epair1}b > out & pid=$! + multicast_join ip_mreqn ${epair1}b atf_check -s exit:0 -o empty \ jexec mjail1 $(atf_get_srcdir)/multicast-send \ 0.0.0.0 6676 233.252.0.1 6676 ${epair1}a hello @@ -99,13 +126,25 @@ IP_ADD_MEMBERSHIP_ip_mreqn_body() atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out # join group on interface epair2 - jexec mjail2 $(atf_get_srcdir)/multicast-receive \ - ip_mreqn 233.252.0.1 6676 ${epair2}b > out & pid=$! + multicast_join ip_mreqn ${epair2}b atf_check -s exit:0 -o empty \ jexec mjail1 $(atf_get_srcdir)/multicast-send \ 0.0.0.0 6676 233.252.0.1 6676 ${epair2}a hello atf_check -s exit:0 sh -c "wait $pid; exit $?" atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out + + # try to join group on the interface determined by the route lookup + atf_check -s exit:71 -e inline:"multicast-receive: setsockopt: Can't assign requested address\n" \ + jexec mjail2 $(atf_get_srcdir)/multicast-receive \ + ip_mreqn 233.252.0.1 6676 0 + # add route and try again + jexec mjail2 route add default 192.0.3.254 + multicast_join ip_mreqn 0 + atf_check -s exit:0 -o empty \ + jexec mjail1 $(atf_get_srcdir)/multicast-send \ + 0.0.0.0 6676 233.252.0.1 6676 192.0.3.1 hello + atf_check -s exit:0 sh -c "wait $pid; exit $?" + atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out } IP_ADD_MEMBERSHIP_ip_mreqn_cleanup() { @@ -123,9 +162,8 @@ MCAST_JOIN_GROUP_body() { multicast_vnet_init - # join group on interface epair2 - jexec mjail2 $(atf_get_srcdir)/multicast-receive \ - group_req 233.252.0.1 6676 ${epair1}b > out & pid=$! + # join group on interface epair1 + multicast_join group_req ${epair1}b atf_check -s exit:0 -o empty \ jexec mjail1 $(atf_get_srcdir)/multicast-send \ 0.0.0.0 6676 233.252.0.1 6676 ${epair1}a hello @@ -133,13 +171,25 @@ MCAST_JOIN_GROUP_body() atf_check -s exit:0 -o inline:"192.0.2.1:6676 hello\n" cat out # join group on interface epair2 - jexec mjail2 $(atf_get_srcdir)/multicast-receive \ - group_req 233.252.0.1 6676 ${epair2}b > out & pid=$! + multicast_join group_req ${epair2}b atf_check -s exit:0 -o empty \ jexec mjail1 $(atf_get_srcdir)/multicast-send \ 0.0.0.0 6676 233.252.0.1 6676 ${epair2}a hello atf_check -s exit:0 sh -c "wait $pid; exit $?" atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out + + # try to join group on the interface determined by the route lookup + atf_check -s exit:71 -e inline:"multicast-receive: setsockopt: Can't assign requested address\n" \ + jexec mjail2 $(atf_get_srcdir)/multicast-receive \ + group_req 233.252.0.1 6676 0 + # add route and try again + jexec mjail2 route add default 192.0.3.254 + multicast_join group_req 0 + atf_check -s exit:0 -o empty \ + jexec mjail1 $(atf_get_srcdir)/multicast-send \ + 0.0.0.0 6676 233.252.0.1 6676 192.0.3.1 hello + atf_check -s exit:0 sh -c "wait $pid; exit $?" + atf_check -s exit:0 -o inline:"192.0.3.1:6676 hello\n" cat out } MCAST_JOIN_GROUP_cleanup() { diff --git a/tests/sys/netinet/so_reuseport_lb_test.c b/tests/sys/netinet/so_reuseport_lb_test.c index 0479bd070ca6..393a626af5a4 100644 --- a/tests/sys/netinet/so_reuseport_lb_test.c +++ b/tests/sys/netinet/so_reuseport_lb_test.c @@ -29,6 +29,8 @@ #include <sys/param.h> #include <sys/event.h> +#include <sys/filio.h> +#include <sys/ioccom.h> #include <sys/socket.h> #include <netinet/in.h> @@ -556,6 +558,150 @@ ATF_TC_BODY(connect_bound, tc) close(s); } +/* + * The kernel erroneously permits calling connect() on a UDP socket with + * SO_REUSEPORT_LB set. Verify that packets sent to the bound address are + * dropped unless they come from the connected address. + */ +ATF_TC_WITHOUT_HEAD(connect_udp); +ATF_TC_BODY(connect_udp, tc) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_len = sizeof(sin), + .sin_addr = { htonl(INADDR_LOOPBACK) }, + }; + ssize_t n; + int error, len, s1, s2, s3; + char ch; + + s1 = socket(PF_INET, SOCK_DGRAM, 0); + ATF_REQUIRE(s1 >= 0); + s2 = socket(PF_INET, SOCK_DGRAM, 0); + ATF_REQUIRE(s2 >= 0); + s3 = socket(PF_INET, SOCK_DGRAM, 0); + ATF_REQUIRE(s3 >= 0); + + error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1}, + sizeof(int)); + ATF_REQUIRE_MSG(error == 0, + "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno)); + error = bind(s1, (struct sockaddr *)&sin, sizeof(sin)); + ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno)); + + error = bind(s2, (struct sockaddr *)&sin, sizeof(sin)); + ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno)); + + error = bind(s3, (struct sockaddr *)&sin, sizeof(sin)); + ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno)); + + /* Connect to an address not owned by s2. */ + error = getsockname(s3, (struct sockaddr *)&sin, + (socklen_t[]){sizeof(sin)}); + ATF_REQUIRE(error == 0); + error = connect(s1, (struct sockaddr *)&sin, sizeof(sin)); + ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno)); + + /* Try to send a packet to s1 from s2. */ + error = getsockname(s1, (struct sockaddr *)&sin, + (socklen_t[]){sizeof(sin)}); + ATF_REQUIRE(error == 0); + + ch = 42; + n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin, + sizeof(sin)); + ATF_REQUIRE(n == 1); + + /* Give the packet some time to arrive. */ + usleep(100000); + + /* s1 is connected to s3 and shouldn't receive from s2. */ + error = ioctl(s1, FIONREAD, &len); + ATF_REQUIRE(error == 0); + ATF_REQUIRE_MSG(len == 0, "unexpected data available"); + + /* ... but s3 can of course send to s1. */ + n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin, + sizeof(sin)); + ATF_REQUIRE(n == 1); + usleep(100000); + error = ioctl(s1, FIONREAD, &len); + ATF_REQUIRE(error == 0); + ATF_REQUIRE_MSG(len == 1, "expected data available"); +} + +/* + * The kernel erroneously permits calling connect() on a UDP socket with + * SO_REUSEPORT_LB set. Verify that packets sent to the bound address are + * dropped unless they come from the connected address. + */ +ATF_TC_WITHOUT_HEAD(connect_udp6); +ATF_TC_BODY(connect_udp6, tc) +{ + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(sin6), + .sin6_addr = IN6ADDR_LOOPBACK_INIT, + }; + ssize_t n; + int error, len, s1, s2, s3; + char ch; + + s1 = socket(PF_INET6, SOCK_DGRAM, 0); + ATF_REQUIRE(s1 >= 0); + s2 = socket(PF_INET6, SOCK_DGRAM, 0); + ATF_REQUIRE(s2 >= 0); + s3 = socket(PF_INET6, SOCK_DGRAM, 0); + ATF_REQUIRE(s3 >= 0); + + error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1}, + sizeof(int)); + ATF_REQUIRE_MSG(error == 0, + "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno)); + error = bind(s1, (struct sockaddr *)&sin6, sizeof(sin6)); + ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno)); + + error = bind(s2, (struct sockaddr *)&sin6, sizeof(sin6)); + ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno)); + + error = bind(s3, (struct sockaddr *)&sin6, sizeof(sin6)); + ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno)); + + /* Connect to an address not owned by s2. */ + error = getsockname(s3, (struct sockaddr *)&sin6, + (socklen_t[]){sizeof(sin6)}); + ATF_REQUIRE(error == 0); + error = connect(s1, (struct sockaddr *)&sin6, sizeof(sin6)); + ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno)); + + /* Try to send a packet to s1 from s2. */ + error = getsockname(s1, (struct sockaddr *)&sin6, + (socklen_t[]){sizeof(sin6)}); + ATF_REQUIRE(error == 0); + + ch = 42; + n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6, + sizeof(sin6)); + ATF_REQUIRE(n == 1); + + /* Give the packet some time to arrive. */ + usleep(100000); + + /* s1 is connected to s3 and shouldn't receive from s2. */ + error = ioctl(s1, FIONREAD, &len); + ATF_REQUIRE(error == 0); + ATF_REQUIRE_MSG(len == 0, "unexpected data available"); + + /* ... but s3 can of course send to s1. */ + n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6, + sizeof(sin6)); + ATF_REQUIRE(n == 1); + usleep(100000); + error = ioctl(s1, FIONREAD, &len); + ATF_REQUIRE(error == 0); + ATF_REQUIRE_MSG(len == 1, "expected data available"); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, basic_ipv4); @@ -566,6 +712,8 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, bind_without_listen); ATF_TP_ADD_TC(tp, connect_not_bound); ATF_TP_ADD_TC(tp, connect_bound); + ATF_TP_ADD_TC(tp, connect_udp); + ATF_TP_ADD_TC(tp, connect_udp6); return (atf_no_error()); } diff --git a/tests/sys/netinet/tcp_hpts_test.py b/tests/sys/netinet/tcp_hpts_test.py new file mode 100644 index 000000000000..c56383fb310f --- /dev/null +++ b/tests/sys/netinet/tcp_hpts_test.py @@ -0,0 +1,4 @@ +from atf_python.ktest import BaseKernelTest + +class TestTcpHpts(BaseKernelTest): + KTEST_MODULE_NAME = "ktest_tcphpts" diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile index b363e0b17c76..9416f6abbdf1 100644 --- a/tests/sys/netpfil/pf/Makefile +++ b/tests/sys/netpfil/pf/Makefile @@ -90,6 +90,8 @@ ${PACKAGE}FILES+= \ pft_ether.py \ pft_read_ipfix.py \ rdr-srcport.py \ + tftpd_inetd.conf \ + tftpd_proxy_inetd.conf \ utils.subr \ utils.py diff --git a/tests/sys/netpfil/pf/anchor.sh b/tests/sys/netpfil/pf/anchor.sh index 64ca84b34c3d..f321c742788e 100644 --- a/tests/sys/netpfil/pf/anchor.sh +++ b/tests/sys/netpfil/pf/anchor.sh @@ -123,6 +123,51 @@ nested_anchor_cleanup() pft_cleanup } +atf_test_case "deeply_nested" "cleanup" +deeply_nested_head() +{ + atf_set descr 'Test setting and retrieving deeply nested anchors' + atf_set require.user root +} + +deeply_nested_body() +{ + pft_init + + epair=$(vnet_mkepair) + vnet_mkjail alcatraz ${epair}a + + pft_set_rules alcatraz \ + "anchor \"foo\" { \n\ + anchor \"bar\" { \n\ + anchor \"foobar\" { \n\ + pass on ${epair}a \n\ + } \n\ + anchor \"quux\" { \n\ + pass on ${epair}a \n\ + } \n\ + } \n\ + anchor \"baz\" { \n\ + pass on ${epair}a \n\ + } \n\ + anchor \"qux\" { \n\ + pass on ${epair}a \n\ + } \n\ + }" + + atf_check -s exit:0 -o \ + inline:" foo\n foo/bar\n foo/bar/foobar\n foo/bar/quux\n foo/baz\n foo/qux\n" \ + jexec alcatraz pfctl -sA + + atf_check -s exit:0 -o inline:" foo/bar/foobar\n foo/bar/quux\n" \ + jexec alcatraz pfctl -a foo/bar -sA +} + +deeply_nested_cleanup() +{ + pft_cleanup +} + atf_test_case "wildcard" "cleanup" wildcard_head() { @@ -498,6 +543,7 @@ atf_init_test_cases() atf_add_test_case "pr183198" atf_add_test_case "pr279225" atf_add_test_case "nested_anchor" + atf_add_test_case "deeply_nested" atf_add_test_case "wildcard" atf_add_test_case "nested_label" atf_add_test_case "quick" diff --git a/tests/sys/netpfil/pf/ioctl/validation.c b/tests/sys/netpfil/pf/ioctl/validation.c index 3e03163cc752..bb060e22f3a0 100644 --- a/tests/sys/netpfil/pf/ioctl/validation.c +++ b/tests/sys/netpfil/pf/ioctl/validation.c @@ -194,6 +194,38 @@ ATF_TC_CLEANUP(gettables, tc) COMMON_CLEANUP(); } +ATF_TC_WITH_CLEANUP(clrtables); +ATF_TC_HEAD(clrtables, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); + atf_tc_set_md_var(tc, "require.kmods", "pf"); +} + +ATF_TC_BODY(clrtables, tc) +{ + struct pfioc_table io; + struct pfr_table tbl; + int flags; + + COMMON_HEAD(); + + flags = 0; + + memset(&io, '/', sizeof(io)); + io.pfrio_flags = flags; + io.pfrio_buffer = &tbl; + io.pfrio_esize = 0; + io.pfrio_size = 1; + + if (ioctl(dev, DIOCRCLRTABLES, &io) == 0) + atf_tc_fail("Request with unterminated anchor name succeeded"); +} + +ATF_TC_CLEANUP(clrtables, tc) +{ + COMMON_CLEANUP(); +} + ATF_TC_WITH_CLEANUP(gettstats); ATF_TC_HEAD(gettstats, tc) { @@ -949,11 +981,36 @@ ATF_TC_CLEANUP(natlook, tc) COMMON_CLEANUP(); } +ATF_TC_WITH_CLEANUP(addstate); +ATF_TC_HEAD(addstate, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); + atf_tc_set_md_var(tc, "require.kmods", "pfsync"); +} + +ATF_TC_BODY(addstate, tc) +{ + struct pfioc_state st; + + COMMON_HEAD(); + + memset(&st, 'a', sizeof(st)); + st.state.timeout = PFTM_TCP_FIRST_PACKET; + + ATF_CHECK_ERRNO(EINVAL, ioctl(dev, DIOCADDSTATE, &st) == -1); +} + +ATF_TC_CLEANUP(addstate, tc) +{ + COMMON_CLEANUP(); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, addtables); ATF_TP_ADD_TC(tp, deltables); ATF_TP_ADD_TC(tp, gettables); + ATF_TP_ADD_TC(tp, clrtables); ATF_TP_ADD_TC(tp, getastats); ATF_TP_ADD_TC(tp, gettstats); ATF_TP_ADD_TC(tp, clrtstats); @@ -974,6 +1031,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, rpool_mtx); ATF_TP_ADD_TC(tp, rpool_mtx2); ATF_TP_ADD_TC(tp, natlook); + ATF_TP_ADD_TC(tp, addstate); return (atf_no_error()); } diff --git a/tests/sys/netpfil/pf/nat.sh b/tests/sys/netpfil/pf/nat.sh index 1ef87cee3598..0824671fa0f1 100644 --- a/tests/sys/netpfil/pf/nat.sh +++ b/tests/sys/netpfil/pf/nat.sh @@ -477,15 +477,49 @@ no_addrs_random_cleanup() pft_cleanup } -atf_test_case "nat_pass" "cleanup" -nat_pass_head() +atf_test_case "nat_pass_in" "cleanup" +nat_pass_in_head() { - atf_set descr 'IPv4 NAT on pass rule' + atf_set descr 'IPv4 NAT on inbound pass rule' atf_set require.user root atf_set require.progs scapy } -nat_pass_body() +nat_pass_in_body() +{ + setup_router_server_ipv4 + # Delete the route back to make sure that the traffic has been NAT-ed + jexec server route del -net ${net_tester} ${net_server_host_router} + # Provide routing back to the NAT address + jexec server route add 203.0.113.0/24 ${net_server_host_router} + jexec router route add 203.0.113.0/24 -iface ${epair_tester}b + + pft_set_rules router \ + "block" \ + "pass in on ${epair_tester}b inet proto tcp nat-to 203.0.113.0 keep state" \ + "pass out on ${epair_server}a inet proto tcp keep state" + + ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 + + jexec router pfctl -qvvsr + jexec router pfctl -qvvss + jexec router ifconfig + jexec router netstat -rn +} + +nat_pass_in_cleanup() +{ + pft_cleanup +} + +nat_pass_out_head() +{ + atf_set descr 'IPv4 NAT on outbound pass rule' + atf_set require.user root + atf_set require.progs scapy +} + +nat_pass_out_body() { setup_router_server_ipv4 # Delete the route back to make sure that the traffic has been NAT-ed @@ -504,7 +538,7 @@ nat_pass_body() jexec router netstat -rn } -nat_pass_cleanup() +nat_pass_out_cleanup() { pft_cleanup } @@ -874,7 +908,8 @@ atf_init_test_cases() atf_add_test_case "no_addrs_random" atf_add_test_case "map_e_compat" atf_add_test_case "map_e_pass" - atf_add_test_case "nat_pass" + atf_add_test_case "nat_pass_in" + atf_add_test_case "nat_pass_out" atf_add_test_case "nat_match" atf_add_test_case "binat_compat" atf_add_test_case "binat_match" diff --git a/tests/sys/netpfil/pf/proxy.sh b/tests/sys/netpfil/pf/proxy.sh index 78ce25930c04..a07bb259b544 100644 --- a/tests/sys/netpfil/pf/proxy.sh +++ b/tests/sys/netpfil/pf/proxy.sh @@ -88,7 +88,68 @@ ftp_cleanup() pft_cleanup } +atf_test_case "tftp" "cleanup" +tftp_head() +{ + atf_set descr 'Test tftp-proxy' + atf_set require.user root +} + +tftp_body() +{ + pft_init + + epair_client=$(vnet_mkepair) + epair_link=$(vnet_mkepair) + + ifconfig ${epair_client}a 192.0.2.2/24 up + route add -net 198.51.100.0/24 192.0.2.1 + + vnet_mkjail fwd ${epair_client}b ${epair_link}a + jexec fwd ifconfig lo0 127.0.0.1/8 up + jexec fwd ifconfig ${epair_client}b 192.0.2.1/24 up + jexec fwd ifconfig ${epair_link}a 198.51.100.1/24 up + jexec fwd ifconfig lo0 127.0.0.1/8 up + jexec fwd sysctl net.inet.ip.forwarding=1 + + vnet_mkjail srv ${epair_link}b + jexec srv ifconfig ${epair_link}b 198.51.100.2/24 up + jexec srv route add default 198.51.100.1 + + # Start tftp server in srv + jexec srv /usr/sbin/inetd -p ${PWD}/inetd-srv.pid \ + $(atf_get_srcdir)/tftpd_inetd.conf + + jexec fwd /usr/sbin/inetd -p ${PWD}/inetd-fwd.pid \ + $(atf_get_srcdir)/tftpd_proxy_inetd.conf + + jexec fwd pfctl -e + pft_set_rules fwd \ + "nat on ${epair_link}a inet from 192.0.2.0/24 to any -> (${epair_link}a)" \ + "nat-anchor \"tftp-proxy/*\"" \ + "rdr-anchor \"tftp-proxy/*\"" \ + "rdr pass on ${epair_client}b proto udp from 192.0.2.0/24 to any port 69 -> 127.0.0.1 port 69" \ + "anchor \"tftp-proxy/*\"" \ + "pass out proto udp from 127.0.0.1 to any port 69" + + # Create a dummy file to download + echo 'foo' > /tmp/remote.txt + echo 'get remote.txt local.txt' | tftp 198.51.100.2 + + # Compare the downloaded file to the original + if ! diff -q local.txt /tmp/remote.txt; + then + atf_fail 'Failed to retrieve file' + fi +} + +tftp_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "ftp" + atf_add_test_case "tftp" } diff --git a/tests/sys/netpfil/pf/rdr.sh b/tests/sys/netpfil/pf/rdr.sh index 24b95b2047f4..b0f0e6d13d0f 100644 --- a/tests/sys/netpfil/pf/rdr.sh +++ b/tests/sys/netpfil/pf/rdr.sh @@ -338,6 +338,56 @@ natpass_cleanup() pft_cleanup } +atf_test_case "pr290177" "cleanup" +pr290177_head() +{ + atf_set descr 'Test PR290177' + atf_set require.user root +} + +pr290177_body() +{ + pft_init + + epair=$(vnet_mkepair) + + ifconfig ${epair}a 192.0.2.2/24 up + ifconfig ${epair}a inet alias 192.0.2.3/24 up + + vnet_mkjail alcatraz ${epair}b + jexec alcatraz ifconfig ${epair}b 192.0.2.1/24 up + jexec alcatraz ifconfig lo0 127.0.0.1/8 up + + # Sanity check + atf_check -s exit:0 -o ignore \ + ping -c 1 192.0.2.1 + + jexec alcatraz pfctl -e + pft_set_rules alcatraz \ + "table <white> { 192.0.2.2 }" \ + "no rdr inet proto tcp from <white> to any port 25" \ + "rdr pass inet proto tcp from any to any port 25 -> 127.0.0.1 port 2500" + + echo foo | jexec alcatraz nc -N -l 2500 & + sleep 1 + + reply=$(nc -w 3 -s 192.0.2.2 192.0.2.1 25) + if [ "${reply}" == "foo" ] + then + atf_fail "no rdr rule failed" + fi + reply=$(nc -w 3 -s 192.0.2.3 192.0.2.1 25) + if [ "${reply}" != "foo" ] + then + atf_fail "rdr rule failed" + fi +} + +pr290177_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "natpass" @@ -345,4 +395,5 @@ atf_init_test_cases() atf_add_test_case "tcp_v6_pass" atf_add_test_case "srcport_compat" atf_add_test_case "srcport_pass" + atf_add_test_case "pr290177" } diff --git a/tests/sys/netpfil/pf/sctp.sh b/tests/sys/netpfil/pf/sctp.sh index 78055f5a9dd2..47bf40181b1b 100644 --- a/tests/sys/netpfil/pf/sctp.sh +++ b/tests/sys/netpfil/pf/sctp.sh @@ -29,9 +29,6 @@ sctp_init() { pft_init - if ! kldstat -q -m sctp; then - atf_skip "This test requires SCTP" - fi } atf_test_case "basic_v4" "cleanup" @@ -39,6 +36,7 @@ basic_v4_head() { atf_set descr 'Basic SCTP connection over IPv4 passthrough' atf_set require.user root + atf_set require.kmods sctp } basic_v4_body() @@ -112,6 +110,7 @@ basic_v6_head() { atf_set descr 'Basic SCTP connection over IPv6' atf_set require.user root + atf_set require.kmods sctp } basic_v6_body() @@ -186,6 +185,7 @@ reuse_head() { atf_set descr 'Test handling dumb clients that reuse source ports' atf_set require.user root + atf_set require.kmods sctp } reuse_body() @@ -244,6 +244,7 @@ abort_v4_head() { atf_set descr 'Test sending ABORT messages' atf_set require.user root + atf_set require.kmods sctp } abort_v4_body() @@ -302,6 +303,7 @@ abort_v6_head() { atf_set descr 'Test sending ABORT messages over IPv6' atf_set require.user root + atf_set require.kmods sctp } abort_v6_body() @@ -360,6 +362,7 @@ nat_v4_head() { atf_set descr 'Test NAT-ing SCTP over IPv4' atf_set require.user root + atf_set require.kmods sctp } nat_v4_body() @@ -412,6 +415,7 @@ nat_v6_head() { atf_set descr 'Test NAT-ing SCTP over IPv6' atf_set require.user root + atf_set require.kmods sctp } nat_v6_body() @@ -464,6 +468,7 @@ rdr_v4_head() { atf_set descr 'Test rdr SCTP over IPv4' atf_set require.user root + atf_set require.kmods sctp } rdr_v4_body() @@ -531,6 +536,7 @@ pfsync_head() { atf_set descr 'Test pfsync-ing SCTP connections' atf_set require.user root + atf_set require.kmods carp sctp } pfsync_body() @@ -563,10 +569,6 @@ pfsync_body() sctp_init pfsynct_init vnet_init_bridge - if ! kldstat -q -m carp - then - atf_skip "This test requires carp" - fi j="sctp:pfsync" @@ -722,6 +724,7 @@ timeout_head() { atf_set descr 'Test setting and retrieving timeout values' atf_set require.user root + atf_set require.kmods sctp } timeout_body() @@ -753,6 +756,7 @@ related_icmp_head() { atf_set descr 'Verify that ICMP messages related to an SCTP connection are allowed' atf_set require.user root + atf_set require.kmods sctp } related_icmp_body() diff --git a/tests/sys/netpfil/pf/src_track.sh b/tests/sys/netpfil/pf/src_track.sh index d86b4ce6c466..1b09030f6174 100755 --- a/tests/sys/netpfil/pf/src_track.sh +++ b/tests/sys/netpfil/pf/src_track.sh @@ -588,6 +588,79 @@ mixed_af_cleanup() pft_cleanup } +atf_test_case "check_valid" "cleanup" +check_valid_head() +{ + atf_set descr 'Test if source node is invalidated on change in redirection pool' + atf_set require.user root + atf_set require.progs python3 scapy +} + +check_valid_body() +{ + setup_router_server_nat64 + + # Clients will connect from another network behind the router. + # This allows for using multiple source addresses. + jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester} + + jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:1/128 alias + jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:2/128 alias + + jexec router pfctl -e + pft_set_rules router \ + "set debug loud " \ + "set state-policy if-bound" \ + "table <targets> { ${net_server1_6}::42:1 }" \ + "pass in on ${epair_tester}b \ + route-to { (${epair_server1}a <targets>) } \ + sticky-address \ + proto tcp \ + keep state" + + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ + --ping-type=tcp3way --send-sport=4201 + + # A source node is created using the original redirection target + nodes=$(mktemp) || exit 1 + jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes + node_regexp='2001:db8:44::1 -> 2001:db8:4201::42:1 .* states 1,.* route sticky-address' + grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'" + + # Change contents of the redirection table + echo ${net_server1_6}::42:2 | jexec router pfctl -Tr -t targets -f - + + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ + --ping-type=tcp3way --send-sport=4202 + + # The original source node was deleted, a new one was created. + # It has 1 states. + jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes + node_regexp='2001:db8:44::1 -> 2001:db8:4201::42:2 .* states 1,.* route sticky-address' + grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'" + + atf_check -s exit:0 ${common_dir}/pft_ping.py \ + --sendif ${epair_tester}a --replyif ${epair_tester}a \ + --fromaddr ${net_clients_6}::1 --to ${host_server_6} \ + --ping-type=tcp3way --send-sport=4203 + + # Without redirection table change the source node is reused. + # It has 2 states. + jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes + node_regexp='2001:db8:44::1 -> 2001:db8:4201::42:2 .* states 2,.* route sticky-address' + grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'" +} + +check_valid_cleanup() +{ + pft_cleanup +} + + atf_init_test_cases() { atf_add_test_case "source_track" @@ -598,4 +671,5 @@ atf_init_test_cases() atf_add_test_case "sn_types_compat" atf_add_test_case "sn_types_pass" atf_add_test_case "mixed_af" + atf_add_test_case "check_valid" } diff --git a/tests/sys/netpfil/pf/tftpd_inetd.conf b/tests/sys/netpfil/pf/tftpd_inetd.conf new file mode 100644 index 000000000000..3554d0a7fb08 --- /dev/null +++ b/tests/sys/netpfil/pf/tftpd_inetd.conf @@ -0,0 +1,28 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2025 Rubicon Communications, LLC (Netgate) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +tftp dgram udp wait root /usr/libexec/tftpd tftpd -l -S -s /tmp +tftp dgram udp6 wait root /usr/libexec/tftpd tftpd -l -S -s /tmp diff --git a/tests/sys/netpfil/pf/tftpd_proxy_inetd.conf b/tests/sys/netpfil/pf/tftpd_proxy_inetd.conf new file mode 100644 index 000000000000..aa5f000f3bba --- /dev/null +++ b/tests/sys/netpfil/pf/tftpd_proxy_inetd.conf @@ -0,0 +1,27 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2025 Rubicon Communications, LLC (Netgate) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +tftp dgram udp wait root /usr/libexec/tftp-proxy tftp-proxy diff --git a/tests/sys/vm/mmap_test.c b/tests/sys/vm/mmap_test.c index 6bc30f73ca95..27d02ae667fb 100644 --- a/tests/sys/vm/mmap_test.c +++ b/tests/sys/vm/mmap_test.c @@ -36,21 +36,6 @@ #include <stdio.h> #include <stdlib.h> -static const struct { - void *addr; - int ok[2]; /* Depending on security.bsd.map_at_zero {0, !=0}. */ -} map_at_zero_tests[] = { - { (void *)0, { 0, 1 } }, /* Test sysctl. */ - { (void *)1, { 0, 0 } }, - { (void *)(PAGE_SIZE - 1), { 0, 0 } }, - { (void *)PAGE_SIZE, { 1, 1 } }, - { (void *)-1, { 0, 0 } }, - { (void *)(-PAGE_SIZE), { 0, 0 } }, - { (void *)(-1 - PAGE_SIZE), { 0, 0 } }, - { (void *)(-1 - PAGE_SIZE - 1), { 0, 0 } }, - { (void *)(0x1000 * PAGE_SIZE), { 1, 1 } }, -}; - #define MAP_AT_ZERO "security.bsd.map_at_zero" #ifdef __LP64__ @@ -68,6 +53,22 @@ ATF_TC_BODY(mmap__map_at_zero, tc) int map_at_zero; bool allow_wx; int prot_flags; + size_t pgsz = getpagesize(); + + const struct { + void *addr; + int ok[2]; /* Depending on security.bsd.map_at_zero {0, !=0}. */ + } map_at_zero_tests[] = { + { (void *)0, { 0, 1 } }, /* Test sysctl. */ + { (void *)1, { 0, 0 } }, + { (void *)(pgsz - 1), { 0, 0 } }, + { (void *)pgsz, { 1, 1 } }, + { (void *)-1, { 0, 0 } }, + { (void *)(-pgsz), { 0, 0 } }, + { (void *)(-1 - pgsz), { 0, 0 } }, + { (void *)(-1 - pgsz - 1), { 0, 0 } }, + { (void *)(0x1000 * pgsz), { 1, 1 } }, + }; len = sizeof(map_at_zero); if (sysctlbyname(MAP_AT_ZERO, &map_at_zero, &len, NULL, 0) == -1) { |
