]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test-seccomp-util: several cleanups
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 6 Sep 2025 19:50:04 +0000 (04:50 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 17 Sep 2025 13:20:42 +0000 (22:20 +0900)
- use safe_fork() with FORK_WAIT
- introduce CHECK_SECCOMP() macro about common checks,
- ignore ENOSYS from sched_setscheduler().

src/test/test-seccomp.c

index a1230ec27ca06438470113dfd5cc27b4c114d6d5..13c65a93cda9a8c662e51307f6e93357ac3c0bba 100644 (file)
 #  define SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 0
 #endif
 
-static bool have_seccomp_privs(void) {
-        return geteuid() == 0 && have_effective_cap(CAP_SYS_ADMIN) > 0; /* If we are root but CAP_SYS_ADMIN we can't do caps (unless we also do NNP) */
-}
+#define CHECK_SECCOMP(refuse_container) \
+        if (!is_seccomp_available())                                    \
+                return (void) log_tests_skipped("Seccomp not available"); \
+        if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0)   \
+                return (void) log_tests_skipped("Not privileged");      \
+        if (refuse_container && detect_container() > 0)                 \
+                return (void) log_tests_skipped("Running in container");
 
 TEST(parse_syscall_and_errno) {
         _cleanup_free_ char *n = NULL;
@@ -166,17 +170,11 @@ TEST(syscall_filter_set_find) {
 }
 
 TEST(filter_sets) {
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        int r;
+
+        CHECK_SECCOMP(/* skip_container = */ false);
 
         for (unsigned i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
-                pid_t pid;
 
 #if HAVE_VALGRIND_VALGRIND_H
                 if (RUNNING_ON_VALGRIND && IN_SET(i, SYSCALL_FILTER_SET_DEFAULT, SYSCALL_FILTER_SET_BASIC_IO, SYSCALL_FILTER_SET_SIGNAL)) {
@@ -195,11 +193,9 @@ TEST(filter_sets) {
 
                 log_info("Testing %s", syscall_filter_sets[i].name);
 
-                pid = fork();
-                assert_se(pid >= 0);
-
-                if (pid == 0) { /* Child? */
-                        int fd, r;
+                ASSERT_OK(r = safe_fork("(filter_sets)", FORK_LOG | FORK_WAIT, NULL));
+                if (r == 0) {
+                        int fd;
 
                         /* If we look at the default set (or one that includes it), allow-list instead of deny-list */
                         if (IN_SET(i, SYSCALL_FILTER_SET_DEFAULT,
@@ -222,8 +218,6 @@ TEST(filter_sets) {
 
                         _exit(EXIT_SUCCESS);
                 }
-
-                assert_se(wait_for_terminate_and_check(syscall_filter_sets[i].name, pid, WAIT_LOG) == EXIT_SUCCESS);
         }
 }
 
@@ -263,12 +257,10 @@ TEST(filter_sets_ordered) {
 TEST(restrict_namespace) {
         char *s = NULL;
         unsigned long ul;
-        pid_t pid;
+        int r;
 
-        if (!have_namespaces()) {
-                log_notice("Testing without namespaces, skipping %s", __func__);
-                return;
-        }
+        if (!have_namespaces())
+                return (void) log_tests_skipped("Testing without namespaces");
 
         assert_se(namespace_flags_to_string(0, &s) == 0 && isempty(s));
         s = mfree(s);
@@ -297,19 +289,10 @@ TEST(restrict_namespace) {
         assert_se(namespace_flags_from_string(s, &ul) == 0 && ul == NAMESPACE_FLAGS_ALL);
         s = mfree(s);
 
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping remaining tests in %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping remaining tests in %s", __func__);
-                return;
-        }
-
-        pid = fork();
-        assert_se(pid >= 0);
+        CHECK_SECCOMP(/* skip_container = */ false);
 
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(restrict-namespace)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
 
                 assert_se(seccomp_restrict_namespaces(CLONE_NEWNS|CLONE_NEWNET) >= 0);
 
@@ -337,7 +320,7 @@ TEST(restrict_namespace) {
                 assert_se(setns(0, 0) == -1);
                 assert_se(errno == EPERM);
 
-                pid = raw_clone(CLONE_NEWNS);
+                pid_t pid = raw_clone(CLONE_NEWNS);
                 assert_se(pid >= 0);
                 if (pid == 0)
                         _exit(EXIT_SUCCESS);
@@ -357,37 +340,21 @@ TEST(restrict_namespace) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("nsseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 TEST(protect_sysctl) {
-        pid_t pid;
-        _cleanup_free_ char *seccomp = NULL;
-
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        int r;
 
         /* in containers _sysctl() is likely missing anyway */
-        if (detect_container() > 0) {
-                log_notice("Testing in container, skipping %s", __func__);
-                return;
-        }
+        CHECK_SECCOMP(/* skip_container = */ true);
 
+        _cleanup_free_ char *seccomp = NULL;
         assert_se(get_proc_field("/proc/self/status", "Seccomp", &seccomp) == 0);
         if (!streq(seccomp, "0"))
                 log_warning("Warning: seccomp filter detected, results may be unreliable for %s", __func__);
 
-        pid = fork();
-        assert_se(pid >= 0);
-
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(protect-sysctl)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
 #if defined __NR__sysctl && __NR__sysctl >= 0
                 assert_se(syscall(__NR__sysctl, NULL) < 0);
                 assert_se(IN_SET(errno, EFAULT, ENOSYS));
@@ -409,32 +376,16 @@ TEST(protect_sysctl) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("sysctlseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 TEST(protect_syslog) {
-        pid_t pid;
-
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        int r;
 
         /* in containers syslog() is likely missing anyway */
-        if (detect_container() > 0) {
-                log_notice("Testing in container, skipping %s", __func__);
-                return;
-        }
-
-        pid = fork();
-        assert_se(pid >= 0);
+        CHECK_SECCOMP(/* skip_container = */ true);
 
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(protect-syslog)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
 #if defined __NR_syslog && __NR_syslog >= 0
                 assert_se(syscall(__NR_syslog, -1, NULL, 0) < 0);
                 assert_se(errno == EINVAL);
@@ -449,26 +400,15 @@ TEST(protect_syslog) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("syslogseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 TEST(restrict_address_families) {
-        pid_t pid;
-
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        int r;
 
-        pid = fork();
-        assert_se(pid >= 0);
+        CHECK_SECCOMP(/* skip_container = */ false);
 
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(restrict-address-families)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
                 int fd;
                 Set *s;
 
@@ -536,34 +476,23 @@ TEST(restrict_address_families) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("socketseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 TEST(restrict_realtime) {
-        pid_t pid;
-
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        int r;
 
         /* in containers RT privs are likely missing anyway */
-        if (detect_container() > 0) {
-                log_notice("Testing in container, skipping %s", __func__);
-                return;
-        }
+        CHECK_SECCOMP(/* skip_container = */ true);
 
-        pid = fork();
-        assert_se(pid >= 0);
-
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(restrict-realtime)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
                 /* On some CI environments, the restriction may be already enabled. */
                 if (sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) { .sched_priority = 1 }) < 0) {
+                        if (errno == ENOSYS) {
+                                log_tests_skipped("sched_setscheduler() is not available or already filtered");
+                                _exit(EXIT_SUCCESS);
+                        }
+
                         log_full_errno(errno == EPERM ? LOG_DEBUG : LOG_WARNING, errno,
                                        "Failed to set scheduler parameter for FIFO: %m");
                         assert(errno == EPERM);
@@ -591,21 +520,13 @@ TEST(restrict_realtime) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("realtimeseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 TEST(memory_deny_write_execute_mmap) {
-        pid_t pid;
+        int r;
+
+        CHECK_SECCOMP(/* skip_container = */ false);
 
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
 #if HAVE_VALGRIND_VALGRIND_H
         if (RUNNING_ON_VALGRIND) {
                 log_notice("Running on valgrind, skipping %s", __func__);
@@ -617,10 +538,8 @@ TEST(memory_deny_write_execute_mmap) {
         return;
 #endif
 
-        pid = fork();
-        assert_se(pid >= 0);
-
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(memory_deny_write_execute_mmap)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
                 void *p;
 
                 p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
@@ -649,14 +568,11 @@ TEST(memory_deny_write_execute_mmap) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("memoryseccomp-mmap", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 TEST(memory_deny_write_execute_shmat) {
-        int shmid;
-        pid_t pid;
         uint32_t arch;
+        int r, shmid;
 
         SECCOMP_FOREACH_LOCAL_ARCH(arch) {
                 log_debug("arch %s: SCMP_SYS(mmap) = %d", seccomp_arch_to_string(arch), SCMP_SYS(mmap));
@@ -666,14 +582,8 @@ TEST(memory_deny_write_execute_shmat) {
                 log_debug("arch %s: SCMP_SYS(shmdt) = %d", seccomp_arch_to_string(arch), SCMP_SYS(shmdt));
         }
 
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs() || have_effective_cap(CAP_IPC_OWNER) <= 0) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        CHECK_SECCOMP(/* skip_container = */ false);
+
 #if HAVE_VALGRIND_VALGRIND_H
         if (RUNNING_ON_VALGRIND) {
                 log_notice("Running on valgrind, skipping %s", __func__);
@@ -688,10 +598,8 @@ TEST(memory_deny_write_execute_shmat) {
         shmid = shmget(IPC_PRIVATE, page_size(), 0);
         assert_se(shmid >= 0);
 
-        pid = fork();
-        assert_se(pid >= 0);
-
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(memory-deny-write-execute)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
                 void *p;
 
                 p = shmat(shmid, NULL, 0);
@@ -722,26 +630,15 @@ TEST(memory_deny_write_execute_shmat) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("memoryseccomp-shmat", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 TEST(restrict_archs) {
-        pid_t pid;
-
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        int r;
 
-        pid = fork();
-        assert_se(pid >= 0);
+        CHECK_SECCOMP(/* skip_container = */ false);
 
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(restrict-archs)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
                 _cleanup_set_free_ Set *s = NULL;
 
                 assert_se(access("/", F_OK) >= 0);
@@ -760,26 +657,15 @@ TEST(restrict_archs) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("archseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 TEST(load_syscall_filter_set_raw) {
-        pid_t pid;
-
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        int r;
 
-        pid = fork();
-        assert_se(pid >= 0);
+        CHECK_SECCOMP(/* skip_container = */ false);
 
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(load-filter)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
                 _cleanup_hashmap_free_ Hashmap *s = NULL;
 
                 assert_se(access("/", F_OK) >= 0);
@@ -873,26 +759,15 @@ TEST(load_syscall_filter_set_raw) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("syscallrawseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 TEST(native_syscalls_filtered) {
-        pid_t pid;
-
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        int r;
 
-        pid = fork();
-        assert_se(pid >= 0);
+        CHECK_SECCOMP(/* skip_container = */ false);
 
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(native-syscalls)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
                 _cleanup_set_free_ Set *arch_s = NULL;
                 _cleanup_hashmap_free_ Hashmap *s = NULL;
 
@@ -931,32 +806,21 @@ TEST(native_syscalls_filtered) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("nativeseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 TEST(lock_personality) {
         unsigned long current_opinionated;
-        pid_t pid;
+        int r;
 
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        CHECK_SECCOMP(/* skip_container = */ false);
 
         assert_se(opinionated_personality(&current_opinionated) >= 0);
 
         log_info("current personality=0x%lX", (unsigned long) safe_personality(PERSONALITY_INVALID));
         log_info("current opinionated personality=0x%lX", current_opinionated);
 
-        pid = fork();
-        assert_se(pid >= 0);
-
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(lock-personality)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
                 unsigned long current;
 
                 assert_se(seccomp_lock_personality(current_opinionated) >= 0);
@@ -988,8 +852,6 @@ TEST(lock_personality) {
                 assert_se((current & OPINIONATED_PERSONALITY_MASK) == current_opinionated);
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("lockpersonalityseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 static int real_open(const char *path, int flags, mode_t mode) {
@@ -1023,21 +885,12 @@ static int try_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) {
 }
 
 TEST(restrict_suid_sgid) {
-        pid_t pid;
-
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        int r;
 
-        pid = fork();
-        assert_se(pid >= 0);
+        CHECK_SECCOMP(/* skip_container = */ false);
 
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(suid-sgid)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
                 char path[] = "/tmp/suidsgidXXXXXX", dir[] = "/tmp/suidsgiddirXXXXXX";
                 int fd = -EBADF, k = -EBADF;
                 const char *z;
@@ -1224,8 +1077,6 @@ TEST(restrict_suid_sgid) {
 
                 _exit(EXIT_SUCCESS);
         }
-
-        assert_se(wait_for_terminate_and_check("suidsgidseccomp", pid, WAIT_LOG) == EXIT_SUCCESS);
 }
 
 static void test_seccomp_suppress_sync_child(void) {
@@ -1258,25 +1109,15 @@ static void test_seccomp_suppress_sync_child(void) {
 }
 
 TEST(seccomp_suppress_sync) {
-        pid_t pid;
-
-        if (!is_seccomp_available()) {
-                log_notice("Seccomp not available, skipping %s", __func__);
-                return;
-        }
-        if (!have_seccomp_privs()) {
-                log_notice("Not privileged, skipping %s", __func__);
-                return;
-        }
+        int r;
 
-        ASSERT_OK_ERRNO(pid = fork());
+        CHECK_SECCOMP(/* skip_container = */ false);
 
-        if (pid == 0) {
+        ASSERT_OK(r = safe_fork("(suppress-sync)", FORK_LOG | FORK_WAIT, NULL));
+        if (r == 0) {
                 test_seccomp_suppress_sync_child();
                 _exit(EXIT_SUCCESS);
         }
-
-        ASSERT_EQ(wait_for_terminate_and_check("seccomp_suppress_sync", pid, WAIT_LOG), EXIT_SUCCESS);
 }
 
 DEFINE_TEST_MAIN(LOG_DEBUG);