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); 20 ATF_TC_HEAD(system_true, tc) 21 { 22 atf_tc_set_md_var(tc, "descr", "system(\"true\")"); 23 } 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); 30 ATF_TC_HEAD(system_false, tc) 31 { 32 atf_tc_set_md_var(tc, "descr", "system(\"false\")"); 33 } 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); 40 ATF_TC_HEAD(system_touch, tc) 41 { 42 atf_tc_set_md_var(tc, "descr", "system(\"touch file\")"); 43 } 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); 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 } 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 * 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 static inline int 96 sigcmpset(const sigset_t *a, const sigset_t *b) 97 { 98 return (memcmp(a, b, sizeof(sigset_t))); 99 } 100 101 ATF_TC(system_concurrent); 102 ATF_TC_HEAD(system_concurrent, tc) 103 { 104 atf_tc_set_md_var(tc, "descr", "Concurrent calls"); 105 } 106 ATF_TC_BODY(system_concurrent, tc) 107 { 108 enum { N = 3 }; 109 struct sigaction sigint, sigquit, sigact; 110 sigset_t normset, sigset; 111 pthread_t thr[N]; 112 char fn[8]; 113 int fd[N]; 114 void *arg, *ret; 115 116 /* Create and lock the locks */ 117 for (int i = 0; i < N; i++) { 118 snprintf(fn, sizeof(fn), "lock%d", i); 119 fd[i] = open(fn, O_CREAT|O_EXCL|O_EXLOCK|O_CLOEXEC, 0644); 120 ATF_REQUIRE_MSG(fd[i] >= 0, "%s: %m", fn); 121 } 122 123 /* Create the flags */ 124 for (int i = 0; i < N; i++) { 125 snprintf(fn, sizeof(fn), "flag%d", i); 126 ATF_REQUIRE_EQ(0, symlink(fn, fn)); 127 } 128 129 /* Save the current signal dispositions */ 130 ATF_REQUIRE_EQ(0, sigaction(SIGINT, NULL, &sigint)); 131 ATF_REQUIRE_EQ(0, sigaction(SIGQUIT, NULL, &sigquit)); 132 ATF_REQUIRE_EQ(0, sigprocmask(0, NULL, &normset)); 133 134 /* Spawn threads which block on these files */ 135 for (int i = 0; i < N; i++) { 136 arg = (void *)(intptr_t)i; 137 ATF_REQUIRE_INTEQ(0, 138 pthread_create(&thr[i], NULL, system_thread, arg)); 139 } 140 141 /* Wait until the flags are gone */ 142 for (int i = 0; i < N; i++) { 143 snprintf(fn, sizeof(fn), "flag%d", i); 144 while (readlink(fn, fn, sizeof(fn)) > 0) 145 usleep(10000); 146 ATF_REQUIRE_EQ(ENOENT, errno); 147 } 148 149 /* Release the locks */ 150 for (int i = 0; i < N; i++) { 151 /* Check the signal dispositions */ 152 ATF_REQUIRE_EQ(0, sigaction(SIGINT, NULL, &sigact)); 153 ATF_CHECK_EQ(SIG_IGN, sigact.sa_handler); 154 ATF_REQUIRE_EQ(0, sigaction(SIGQUIT, NULL, &sigact)); 155 ATF_CHECK_EQ(SIG_IGN, sigact.sa_handler); 156 #ifndef PROCMASK_IS_THREADMASK 157 ATF_REQUIRE_EQ(0, sigprocmask(0, NULL, &sigset)); 158 ATF_CHECK(sigismember(&sigset, SIGCHLD)); 159 #endif 160 161 /* Close the file, releasing the lock */ 162 ATF_REQUIRE_INTEQ(0, close(fd[i])); 163 164 /* Join the thread and check the return value */ 165 ATF_CHECK_INTEQ(0, pthread_join(thr[i], &ret)); 166 ATF_CHECK_INTEQ(W_EXITCODE(0, 0), (int)(intptr_t)ret); 167 } 168 169 /* Check the signal dispositions */ 170 ATF_REQUIRE_EQ(0, sigaction(SIGINT, NULL, &sigact)); 171 ATF_CHECK_EQ(sigint.sa_handler, sigact.sa_handler); 172 ATF_CHECK_EQ(sigint.sa_flags, sigact.sa_flags); 173 ATF_CHECK_EQ(0, sigcmpset(&sigint.sa_mask, &sigact.sa_mask)); 174 ATF_REQUIRE_EQ(0, sigaction(SIGQUIT, NULL, &sigact)); 175 ATF_CHECK_EQ(sigquit.sa_handler, sigact.sa_handler); 176 ATF_CHECK_EQ(sigquit.sa_flags, sigact.sa_flags); 177 ATF_CHECK_EQ(0, sigcmpset(&sigquit.sa_mask, &sigact.sa_mask)); 178 ATF_REQUIRE_EQ(0, sigprocmask(0, NULL, &sigset)); 179 ATF_CHECK_EQ(0, sigcmpset(&sigset, &normset)); 180 } 181 182 ATF_TP_ADD_TCS(tp) 183 { 184 ATF_TP_ADD_TC(tp, system_true); 185 ATF_TP_ADD_TC(tp, system_false); 186 ATF_TP_ADD_TC(tp, system_touch); 187 ATF_TP_ADD_TC(tp, system_null); 188 ATF_TP_ADD_TC(tp, system_concurrent); 189 return (atf_no_error()); 190 } 191