1 /*-
2 * Copyright (c) 2026 Klara, Inc.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7 #include <sys/wait.h>
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <pthread.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16
17 #include <atf-c.h>
18
19 ATF_TC(system_true);
ATF_TC_HEAD(system_true,tc)20 ATF_TC_HEAD(system_true, tc)
21 {
22 atf_tc_set_md_var(tc, "descr", "system(\"true\")");
23 }
ATF_TC_BODY(system_true,tc)24 ATF_TC_BODY(system_true, tc)
25 {
26 ATF_REQUIRE_EQ(W_EXITCODE(0, 0), system("true"));
27 }
28
29 ATF_TC(system_false);
ATF_TC_HEAD(system_false,tc)30 ATF_TC_HEAD(system_false, tc)
31 {
32 atf_tc_set_md_var(tc, "descr", "system(\"false\")");
33 }
ATF_TC_BODY(system_false,tc)34 ATF_TC_BODY(system_false, tc)
35 {
36 ATF_REQUIRE_EQ(W_EXITCODE(1, 0), system("false"));
37 }
38
39 ATF_TC(system_touch);
ATF_TC_HEAD(system_touch,tc)40 ATF_TC_HEAD(system_touch, tc)
41 {
42 atf_tc_set_md_var(tc, "descr", "system(\"touch file\")");
43 }
ATF_TC_BODY(system_touch,tc)44 ATF_TC_BODY(system_touch, tc)
45 {
46 /* The file does not exist */
47 ATF_CHECK_ERRNO(ENOENT, unlink("file"));
48
49 /* Run a command that creates it */
50 ATF_REQUIRE_EQ(W_EXITCODE(0, 0), system("touch file"));
51
52 /* Now the file exists */
53 ATF_CHECK_EQ(0, unlink("file"));
54 }
55
56 ATF_TC(system_null);
ATF_TC_HEAD(system_null,tc)57 ATF_TC_HEAD(system_null, tc)
58 {
59 atf_tc_set_md_var(tc, "descr", "system(NULL)");
60 atf_tc_set_md_var(tc, "require.user", "root");
61 }
ATF_TC_BODY(system_null,tc)62 ATF_TC_BODY(system_null, tc)
63 {
64 /* First, test in a normal environment */
65 ATF_REQUIRE_EQ(1, system(NULL));
66
67 /* Now enter an empty chroot */
68 ATF_REQUIRE_EQ(0, chroot("."));
69 ATF_REQUIRE_EQ(0, chdir("/"));
70
71 /* Test again with no shell available */
72 ATF_REQUIRE_EQ(0, system(NULL));
73 ATF_REQUIRE_EQ(W_EXITCODE(127, 0), system("true"));
74 }
75
76 /*
77 * Define PROCMASK_IS_THREADMASK if sigprocmask() gets / sets the thread
78 * mask in multithreaded programs, which makes it impossible to verify
79 * that system(3) correctly blocks and unblocks SIGCHLD.
80 */
81 #ifdef __FreeBSD__
82 #define PROCMASK_IS_THREADMASK 1
83 #endif
84
85 static void *
system_thread(void * arg)86 system_thread(void *arg)
87 {
88 char cmd[64];
89 int i = (int)(intptr_t)arg;
90
91 snprintf(cmd, sizeof(cmd), "rm flag%d ; lockf -ns lock%d true", i, i);
92 return ((void *)(intptr_t)system(cmd));
93 }
94
95 ATF_TC(system_concurrent);
ATF_TC_HEAD(system_concurrent,tc)96 ATF_TC_HEAD(system_concurrent, tc)
97 {
98 atf_tc_set_md_var(tc, "descr", "Concurrent calls");
99 }
ATF_TC_BODY(system_concurrent,tc)100 ATF_TC_BODY(system_concurrent, tc)
101 {
102 #define N 3
103 sigset_t normset, sigset;
104 pthread_t thr[N];
105 char fn[8];
106 int fd[N];
107 void *arg, *ret;
108
109 /* Create and lock the locks */
110 for (int i = 0; i < N; i++) {
111 snprintf(fn, sizeof(fn), "lock%d", i);
112 fd[i] = open(fn, O_CREAT|O_EXCL|O_EXLOCK|O_CLOEXEC, 0644);
113 ATF_REQUIRE_MSG(fd[i] >= 0, "%s: %m", fn);
114 }
115
116 /* Create the flags */
117 for (int i = 0; i < N; i++) {
118 snprintf(fn, sizeof(fn), "flag%d", i);
119 ATF_REQUIRE_EQ(0, symlink(fn, fn));
120 }
121
122 /* Get the current and expected signal mask */
123 sigprocmask(0, NULL, &normset);
124
125 /* Spawn threads which block on these files */
126 for (int i = 0; i < N; i++) {
127 arg = (void *)(intptr_t)i;
128 ATF_REQUIRE_INTEQ(0,
129 pthread_create(&thr[i], NULL, system_thread, arg));
130 }
131
132 /* Wait until the flags are gone */
133 for (int i = 0; i < N; i++) {
134 snprintf(fn, sizeof(fn), "flag%d", i);
135 while (readlink(fn, fn, sizeof(fn)) > 0)
136 usleep(10000);
137 ATF_REQUIRE_EQ(ENOENT, errno);
138 }
139
140 /* Release the locks */
141 for (int i = 0; i < N; i++) {
142 /* Check the signal dispositions */
143 ATF_CHECK_EQ(SIG_IGN, signal(SIGINT, SIG_IGN));
144 ATF_CHECK_EQ(SIG_IGN, signal(SIGQUIT, SIG_IGN));
145 #ifndef PROCMASK_IS_THREADMASK
146 sigprocmask(0, NULL, &sigset);
147 ATF_CHECK(sigismember(&sigset, SIGCHLD));
148 #endif
149
150 /* Close the file, releasing the lock */
151 ATF_REQUIRE_INTEQ(0, close(fd[i]));
152
153 /* Join the thread and check the return value */
154 ATF_CHECK_INTEQ(0, pthread_join(thr[i], &ret));
155 ATF_CHECK_INTEQ(W_EXITCODE(0, 0), (int)(intptr_t)ret);
156 }
157
158 /* Check the signal dispositions */
159 ATF_CHECK_EQ(SIG_DFL, signal(SIGINT, SIG_DFL));
160 ATF_CHECK_EQ(SIG_DFL, signal(SIGQUIT, SIG_DFL));
161 sigprocmask(0, NULL, &sigset);
162 ATF_CHECK_EQ(0, memcmp(&sigset, &normset, sizeof(sigset_t)));
163 #undef N
164 }
165
ATF_TP_ADD_TCS(tp)166 ATF_TP_ADD_TCS(tp)
167 {
168 ATF_TP_ADD_TC(tp, system_true);
169 ATF_TP_ADD_TC(tp, system_false);
170 ATF_TP_ADD_TC(tp, system_touch);
171 ATF_TP_ADD_TC(tp, system_null);
172 ATF_TP_ADD_TC(tp, system_concurrent);
173 return (atf_no_error());
174 }
175