17a1ade51SDag-Erling Smørgrav /*-
27a1ade51SDag-Erling Smørgrav * Copyright (c) 2026 Klara, Inc.
37a1ade51SDag-Erling Smørgrav *
47a1ade51SDag-Erling Smørgrav * SPDX-License-Identifier: BSD-2-Clause
57a1ade51SDag-Erling Smørgrav */
67a1ade51SDag-Erling Smørgrav
77a1ade51SDag-Erling Smørgrav #include <sys/wait.h>
87a1ade51SDag-Erling Smørgrav
97a1ade51SDag-Erling Smørgrav #include <errno.h>
107a1ade51SDag-Erling Smørgrav #include <fcntl.h>
117a1ade51SDag-Erling Smørgrav #include <pthread.h>
127a1ade51SDag-Erling Smørgrav #include <signal.h>
137a1ade51SDag-Erling Smørgrav #include <stdio.h>
147a1ade51SDag-Erling Smørgrav #include <stdlib.h>
157a1ade51SDag-Erling Smørgrav #include <unistd.h>
167a1ade51SDag-Erling Smørgrav
177a1ade51SDag-Erling Smørgrav #include <atf-c.h>
187a1ade51SDag-Erling Smørgrav
197a1ade51SDag-Erling Smørgrav ATF_TC(system_true);
ATF_TC_HEAD(system_true,tc)207a1ade51SDag-Erling Smørgrav ATF_TC_HEAD(system_true, tc)
217a1ade51SDag-Erling Smørgrav {
227a1ade51SDag-Erling Smørgrav atf_tc_set_md_var(tc, "descr", "system(\"true\")");
237a1ade51SDag-Erling Smørgrav }
ATF_TC_BODY(system_true,tc)247a1ade51SDag-Erling Smørgrav ATF_TC_BODY(system_true, tc)
257a1ade51SDag-Erling Smørgrav {
267a1ade51SDag-Erling Smørgrav ATF_REQUIRE_EQ(W_EXITCODE(0, 0), system("true"));
277a1ade51SDag-Erling Smørgrav }
287a1ade51SDag-Erling Smørgrav
297a1ade51SDag-Erling Smørgrav ATF_TC(system_false);
ATF_TC_HEAD(system_false,tc)307a1ade51SDag-Erling Smørgrav ATF_TC_HEAD(system_false, tc)
317a1ade51SDag-Erling Smørgrav {
327a1ade51SDag-Erling Smørgrav atf_tc_set_md_var(tc, "descr", "system(\"false\")");
337a1ade51SDag-Erling Smørgrav }
ATF_TC_BODY(system_false,tc)347a1ade51SDag-Erling Smørgrav ATF_TC_BODY(system_false, tc)
357a1ade51SDag-Erling Smørgrav {
367a1ade51SDag-Erling Smørgrav ATF_REQUIRE_EQ(W_EXITCODE(1, 0), system("false"));
377a1ade51SDag-Erling Smørgrav }
387a1ade51SDag-Erling Smørgrav
397a1ade51SDag-Erling Smørgrav ATF_TC(system_touch);
ATF_TC_HEAD(system_touch,tc)407a1ade51SDag-Erling Smørgrav ATF_TC_HEAD(system_touch, tc)
417a1ade51SDag-Erling Smørgrav {
427a1ade51SDag-Erling Smørgrav atf_tc_set_md_var(tc, "descr", "system(\"touch file\")");
437a1ade51SDag-Erling Smørgrav }
ATF_TC_BODY(system_touch,tc)447a1ade51SDag-Erling Smørgrav ATF_TC_BODY(system_touch, tc)
457a1ade51SDag-Erling Smørgrav {
467a1ade51SDag-Erling Smørgrav /* The file does not exist */
477a1ade51SDag-Erling Smørgrav ATF_CHECK_ERRNO(ENOENT, unlink("file"));
487a1ade51SDag-Erling Smørgrav
497a1ade51SDag-Erling Smørgrav /* Run a command that creates it */
507a1ade51SDag-Erling Smørgrav ATF_REQUIRE_EQ(W_EXITCODE(0, 0), system("touch file"));
517a1ade51SDag-Erling Smørgrav
527a1ade51SDag-Erling Smørgrav /* Now the file exists */
537a1ade51SDag-Erling Smørgrav ATF_CHECK_EQ(0, unlink("file"));
547a1ade51SDag-Erling Smørgrav }
557a1ade51SDag-Erling Smørgrav
567a1ade51SDag-Erling Smørgrav ATF_TC(system_null);
ATF_TC_HEAD(system_null,tc)577a1ade51SDag-Erling Smørgrav ATF_TC_HEAD(system_null, tc)
587a1ade51SDag-Erling Smørgrav {
597a1ade51SDag-Erling Smørgrav atf_tc_set_md_var(tc, "descr", "system(NULL)");
608ae3f449SDag-Erling Smørgrav atf_tc_set_md_var(tc, "require.user", "root");
617a1ade51SDag-Erling Smørgrav }
ATF_TC_BODY(system_null,tc)627a1ade51SDag-Erling Smørgrav ATF_TC_BODY(system_null, tc)
637a1ade51SDag-Erling Smørgrav {
648ae3f449SDag-Erling Smørgrav /* First, test in a normal environment */
657a1ade51SDag-Erling Smørgrav ATF_REQUIRE_EQ(1, system(NULL));
668ae3f449SDag-Erling Smørgrav
678ae3f449SDag-Erling Smørgrav /* Now enter an empty chroot */
688ae3f449SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, chroot("."));
698ae3f449SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, chdir("/"));
708ae3f449SDag-Erling Smørgrav
718ae3f449SDag-Erling Smørgrav /* Test again with no shell available */
728ae3f449SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, system(NULL));
738ae3f449SDag-Erling Smørgrav ATF_REQUIRE_EQ(W_EXITCODE(127, 0), system("true"));
747a1ade51SDag-Erling Smørgrav }
757a1ade51SDag-Erling Smørgrav
767a1ade51SDag-Erling Smørgrav /*
777a1ade51SDag-Erling Smørgrav * Define PROCMASK_IS_THREADMASK if sigprocmask() gets / sets the thread
787a1ade51SDag-Erling Smørgrav * mask in multithreaded programs, which makes it impossible to verify
797a1ade51SDag-Erling Smørgrav * that system(3) correctly blocks and unblocks SIGCHLD.
807a1ade51SDag-Erling Smørgrav */
817a1ade51SDag-Erling Smørgrav #ifdef __FreeBSD__
827a1ade51SDag-Erling Smørgrav #define PROCMASK_IS_THREADMASK 1
837a1ade51SDag-Erling Smørgrav #endif
847a1ade51SDag-Erling Smørgrav
857a1ade51SDag-Erling Smørgrav static void *
system_thread(void * arg)867a1ade51SDag-Erling Smørgrav system_thread(void *arg)
877a1ade51SDag-Erling Smørgrav {
887a1ade51SDag-Erling Smørgrav char cmd[64];
897a1ade51SDag-Erling Smørgrav int i = (int)(intptr_t)arg;
907a1ade51SDag-Erling Smørgrav
917a1ade51SDag-Erling Smørgrav snprintf(cmd, sizeof(cmd), "rm flag%d ; lockf -ns lock%d true", i, i);
927a1ade51SDag-Erling Smørgrav return ((void *)(intptr_t)system(cmd));
937a1ade51SDag-Erling Smørgrav }
947a1ade51SDag-Erling Smørgrav
9548368f70SDag-Erling Smørgrav static inline int
sigcmpset(const sigset_t * a,const sigset_t * b)9648368f70SDag-Erling Smørgrav sigcmpset(const sigset_t *a, const sigset_t *b)
9748368f70SDag-Erling Smørgrav {
9848368f70SDag-Erling Smørgrav return (memcmp(a, b, sizeof(sigset_t)));
9948368f70SDag-Erling Smørgrav }
10048368f70SDag-Erling Smørgrav
1017a1ade51SDag-Erling Smørgrav ATF_TC(system_concurrent);
ATF_TC_HEAD(system_concurrent,tc)1027a1ade51SDag-Erling Smørgrav ATF_TC_HEAD(system_concurrent, tc)
1037a1ade51SDag-Erling Smørgrav {
1047a1ade51SDag-Erling Smørgrav atf_tc_set_md_var(tc, "descr", "Concurrent calls");
1057a1ade51SDag-Erling Smørgrav }
ATF_TC_BODY(system_concurrent,tc)1067a1ade51SDag-Erling Smørgrav ATF_TC_BODY(system_concurrent, tc)
1077a1ade51SDag-Erling Smørgrav {
10848368f70SDag-Erling Smørgrav enum { N = 3 };
10948368f70SDag-Erling Smørgrav struct sigaction sigint, sigquit, sigact;
1107a1ade51SDag-Erling Smørgrav sigset_t normset, sigset;
1117a1ade51SDag-Erling Smørgrav pthread_t thr[N];
1127a1ade51SDag-Erling Smørgrav char fn[8];
1137a1ade51SDag-Erling Smørgrav int fd[N];
1147a1ade51SDag-Erling Smørgrav void *arg, *ret;
1157a1ade51SDag-Erling Smørgrav
1167a1ade51SDag-Erling Smørgrav /* Create and lock the locks */
1177a1ade51SDag-Erling Smørgrav for (int i = 0; i < N; i++) {
1187a1ade51SDag-Erling Smørgrav snprintf(fn, sizeof(fn), "lock%d", i);
1197a1ade51SDag-Erling Smørgrav fd[i] = open(fn, O_CREAT|O_EXCL|O_EXLOCK|O_CLOEXEC, 0644);
1207a1ade51SDag-Erling Smørgrav ATF_REQUIRE_MSG(fd[i] >= 0, "%s: %m", fn);
1217a1ade51SDag-Erling Smørgrav }
1227a1ade51SDag-Erling Smørgrav
1237a1ade51SDag-Erling Smørgrav /* Create the flags */
1247a1ade51SDag-Erling Smørgrav for (int i = 0; i < N; i++) {
1257a1ade51SDag-Erling Smørgrav snprintf(fn, sizeof(fn), "flag%d", i);
1267a1ade51SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, symlink(fn, fn));
1277a1ade51SDag-Erling Smørgrav }
1287a1ade51SDag-Erling Smørgrav
12948368f70SDag-Erling Smørgrav /* Save the current signal dispositions */
13048368f70SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, sigaction(SIGINT, NULL, &sigint));
131*863b5c13SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, sigaction(SIGQUIT, NULL, &sigquit));
13248368f70SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, sigprocmask(0, NULL, &normset));
1337a1ade51SDag-Erling Smørgrav
1347a1ade51SDag-Erling Smørgrav /* Spawn threads which block on these files */
1357a1ade51SDag-Erling Smørgrav for (int i = 0; i < N; i++) {
1367a1ade51SDag-Erling Smørgrav arg = (void *)(intptr_t)i;
1377a1ade51SDag-Erling Smørgrav ATF_REQUIRE_INTEQ(0,
1387a1ade51SDag-Erling Smørgrav pthread_create(&thr[i], NULL, system_thread, arg));
1397a1ade51SDag-Erling Smørgrav }
1407a1ade51SDag-Erling Smørgrav
1417a1ade51SDag-Erling Smørgrav /* Wait until the flags are gone */
1427a1ade51SDag-Erling Smørgrav for (int i = 0; i < N; i++) {
1437a1ade51SDag-Erling Smørgrav snprintf(fn, sizeof(fn), "flag%d", i);
1447a1ade51SDag-Erling Smørgrav while (readlink(fn, fn, sizeof(fn)) > 0)
1457a1ade51SDag-Erling Smørgrav usleep(10000);
1467a1ade51SDag-Erling Smørgrav ATF_REQUIRE_EQ(ENOENT, errno);
1477a1ade51SDag-Erling Smørgrav }
1487a1ade51SDag-Erling Smørgrav
1497a1ade51SDag-Erling Smørgrav /* Release the locks */
1507a1ade51SDag-Erling Smørgrav for (int i = 0; i < N; i++) {
1517a1ade51SDag-Erling Smørgrav /* Check the signal dispositions */
15248368f70SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, sigaction(SIGINT, NULL, &sigact));
15348368f70SDag-Erling Smørgrav ATF_CHECK_EQ(SIG_IGN, sigact.sa_handler);
154*863b5c13SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, sigaction(SIGQUIT, NULL, &sigact));
155*863b5c13SDag-Erling Smørgrav ATF_CHECK_EQ(SIG_IGN, sigact.sa_handler);
1567a1ade51SDag-Erling Smørgrav #ifndef PROCMASK_IS_THREADMASK
15748368f70SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, sigprocmask(0, NULL, &sigset));
1587a1ade51SDag-Erling Smørgrav ATF_CHECK(sigismember(&sigset, SIGCHLD));
1597a1ade51SDag-Erling Smørgrav #endif
1607a1ade51SDag-Erling Smørgrav
1617a1ade51SDag-Erling Smørgrav /* Close the file, releasing the lock */
1627a1ade51SDag-Erling Smørgrav ATF_REQUIRE_INTEQ(0, close(fd[i]));
1637a1ade51SDag-Erling Smørgrav
1647a1ade51SDag-Erling Smørgrav /* Join the thread and check the return value */
1657a1ade51SDag-Erling Smørgrav ATF_CHECK_INTEQ(0, pthread_join(thr[i], &ret));
1667a1ade51SDag-Erling Smørgrav ATF_CHECK_INTEQ(W_EXITCODE(0, 0), (int)(intptr_t)ret);
1677a1ade51SDag-Erling Smørgrav }
1687a1ade51SDag-Erling Smørgrav
1697a1ade51SDag-Erling Smørgrav /* Check the signal dispositions */
17048368f70SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, sigaction(SIGINT, NULL, &sigact));
17148368f70SDag-Erling Smørgrav ATF_CHECK_EQ(sigint.sa_handler, sigact.sa_handler);
17248368f70SDag-Erling Smørgrav ATF_CHECK_EQ(sigint.sa_flags, sigact.sa_flags);
17348368f70SDag-Erling Smørgrav ATF_CHECK_EQ(0, sigcmpset(&sigint.sa_mask, &sigact.sa_mask));
174*863b5c13SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, sigaction(SIGQUIT, NULL, &sigact));
175*863b5c13SDag-Erling Smørgrav ATF_CHECK_EQ(sigquit.sa_handler, sigact.sa_handler);
176*863b5c13SDag-Erling Smørgrav ATF_CHECK_EQ(sigquit.sa_flags, sigact.sa_flags);
177*863b5c13SDag-Erling Smørgrav ATF_CHECK_EQ(0, sigcmpset(&sigquit.sa_mask, &sigact.sa_mask));
17848368f70SDag-Erling Smørgrav ATF_REQUIRE_EQ(0, sigprocmask(0, NULL, &sigset));
17948368f70SDag-Erling Smørgrav ATF_CHECK_EQ(0, sigcmpset(&sigset, &normset));
1807a1ade51SDag-Erling Smørgrav }
1817a1ade51SDag-Erling Smørgrav
ATF_TP_ADD_TCS(tp)1827a1ade51SDag-Erling Smørgrav ATF_TP_ADD_TCS(tp)
1837a1ade51SDag-Erling Smørgrav {
1847a1ade51SDag-Erling Smørgrav ATF_TP_ADD_TC(tp, system_true);
1857a1ade51SDag-Erling Smørgrav ATF_TP_ADD_TC(tp, system_false);
1867a1ade51SDag-Erling Smørgrav ATF_TP_ADD_TC(tp, system_touch);
1877a1ade51SDag-Erling Smørgrav ATF_TP_ADD_TC(tp, system_null);
1887a1ade51SDag-Erling Smørgrav ATF_TP_ADD_TC(tp, system_concurrent);
1897a1ade51SDag-Erling Smørgrav return (atf_no_error());
1907a1ade51SDag-Erling Smørgrav }
191