xref: /freebsd/contrib/capsicum-test/capsicum-test.h (revision 8eb2bee6c0f4957c6c1cea826e59cda4d18a2a64)
1 /* -*- C++ -*- */
2 #ifndef CAPSICUM_TEST_H
3 #define CAPSICUM_TEST_H
4 
5 #include <errno.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8 #include <sys/resource.h>
9 #include <signal.h>
10 
11 #include <ios>
12 #include <ostream>
13 #include <string>
14 
15 #include "gtest/gtest.h"
16 
17 extern bool verbose;
18 extern std::string tmpdir;
19 extern bool tmpdir_on_tmpfs;
20 extern bool force_mt;
21 extern bool force_nofork;
22 extern uid_t other_uid;
23 
24 static inline void *WaitingThreadFn(void *) {
25   // Loop until cancelled
26   while (true) {
27     usleep(10000);
28     pthread_testcancel();
29   }
30   return NULL;
31 }
32 
33 // If force_mt is set, run another thread in parallel with the test.  This forces
34 // the kernel into multi-threaded mode.
35 template <typename T, typename Function>
36 void MaybeRunWithThread(T *self, Function fn) {
37   pthread_t subthread;
38   if (force_mt) {
39     pthread_create(&subthread, NULL, WaitingThreadFn, NULL);
40   }
41   (self->*fn)();
42   if (force_mt) {
43     pthread_cancel(subthread);
44     pthread_join(subthread, NULL);
45   }
46 }
47 template <typename Function>
48 void MaybeRunWithThread(Function fn) {
49   pthread_t subthread;
50   if (force_mt) {
51     pthread_create(&subthread, NULL, WaitingThreadFn, NULL);
52   }
53   (fn)();
54   if (force_mt) {
55     pthread_cancel(subthread);
56     pthread_join(subthread, NULL);
57   }
58 }
59 
60 // Return the absolute path of a filename in the temp directory, `tmpdir`,
61 // with the given pathname, e.g., "/tmp/<pathname>", if `tmpdir` was set to
62 // "/tmp".
63 const char *TmpFile(const char *pathname);
64 
65 // Run the given test function in a forked process, so that trapdoor
66 // entry doesn't affect other tests, and watch out for hung processes.
67 // Implemented as a macro to allow access to the test case instance's
68 // HasFailure() method, which is reported as the forked process's
69 // exit status.
70 #define _RUN_FORKED(INNERCODE, TESTCASENAME, TESTNAME)         \
71     pid_t pid = force_nofork ? 0 : fork();                     \
72     if (pid == 0) {                                            \
73       INNERCODE;                                               \
74       if (!force_nofork) {                                     \
75         exit(HasFailure());                                    \
76       }                                                        \
77     } else if (pid > 0) {                                      \
78       int rc, status;                                          \
79       int remaining_us = 30000000;                             \
80       while (remaining_us > 0) {                               \
81         status = 0;                                            \
82         rc = waitpid(pid, &status, WNOHANG);                   \
83         if (rc != 0) break;                                    \
84         remaining_us -= 10000;                                 \
85         usleep(10000);                                         \
86       }                                                        \
87       if (remaining_us <= 0) {                                 \
88         fprintf(stderr, "Warning: killing unresponsive test "  \
89                         "%s.%s (pid %d)\n",                    \
90                         TESTCASENAME, TESTNAME, pid);          \
91         kill(pid, SIGKILL);                                    \
92         ADD_FAILURE() << "Test hung";                          \
93       } else if (rc < 0) {                                     \
94         fprintf(stderr, "Warning: waitpid error %s (%d)\n",    \
95                         strerror(errno), errno);               \
96         ADD_FAILURE() << "Failed to wait for child";           \
97       } else {                                                 \
98         int rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1; \
99         EXPECT_EQ(0, rc);                                      \
100       }                                                        \
101     }
102 #define _RUN_FORKED_MEM(THIS, TESTFN, TESTCASENAME, TESTNAME)  \
103   _RUN_FORKED(MaybeRunWithThread(THIS, &TESTFN), TESTCASENAME, TESTNAME);
104 #define _RUN_FORKED_FN(TESTFN, TESTCASENAME, TESTNAME)   \
105   _RUN_FORKED(MaybeRunWithThread(&TESTFN), TESTCASENAME, TESTNAME);
106 
107 // Run a test case in a forked process, possibly cleaning up a
108 // test file after completion
109 #define FORK_TEST_ON(test_case_name, test_name, test_file)     \
110     static void test_case_name##_##test_name##_ForkTest();     \
111     TEST(test_case_name, test_name ## Forked) {                \
112       _RUN_FORKED_FN(test_case_name##_##test_name##_ForkTest,  \
113                      #test_case_name, #test_name);             \
114       const char *filename = test_file;                        \
115       if (filename) unlink(filename);                          \
116     }                                                          \
117     static void test_case_name##_##test_name##_ForkTest()
118 
119 #define FORK_TEST(test_case_name, test_name) FORK_TEST_ON(test_case_name, test_name, NULL)
120 
121 // Run a test case fixture in a forked process, so that trapdoors don't
122 // affect other tests.
123 #define ICLASS_NAME(test_case_name, test_name) Forked##test_case_name##_##test_name
124 #define FORK_TEST_F(test_case_name, test_name)                \
125   class ICLASS_NAME(test_case_name, test_name) : public test_case_name { \
126     public:                                                    \
127       ICLASS_NAME(test_case_name, test_name)() {}              \
128       void InnerTestBody();                                    \
129     };                                                         \
130     TEST_F(ICLASS_NAME(test_case_name, test_name), _) {        \
131       _RUN_FORKED_MEM(this,                                    \
132                       ICLASS_NAME(test_case_name, test_name)::InnerTestBody,  \
133                       #test_case_name, #test_name);            \
134     }                                                          \
135     void ICLASS_NAME(test_case_name, test_name)::InnerTestBody()
136 
137 // Emit errno information on failure
138 #define EXPECT_OK(v) EXPECT_LE(0, v) << "   errno " << errno << " " << strerror(errno)
139 
140 // Expect a syscall to fail with the given error.
141 #define EXPECT_SYSCALL_FAIL(E, C) \
142     do { \
143       SCOPED_TRACE(#C); \
144       EXPECT_GT(0, C); \
145       EXPECT_EQ(E, errno) << "expected '" << strerror(E) \
146                           << "' but got '" << strerror(errno) << "'"; \
147     } while (0)
148 
149 // Expect a syscall to fail with anything other than the given error.
150 #define EXPECT_SYSCALL_FAIL_NOT(E, C) \
151     do { \
152       EXPECT_GT(0, C); \
153       EXPECT_NE(E, errno) << strerror(E); \
154     } while (0)
155 
156 // Expect a void syscall to fail with anything other than the given error.
157 #define EXPECT_VOID_SYSCALL_FAIL_NOT(E, C)   \
158     do { \
159       errno = 0; \
160       C; \
161       EXPECT_NE(E, errno) << #C << " failed with ECAPMODE"; \
162     } while (0)
163 
164 // Expect a system call to fail due to path traversal; exact error
165 // code is OS-specific.
166 #ifdef O_BENEATH
167 #define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \
168     do {                                              \
169       SCOPED_TRACE(GTEST_STRINGIFY_(openat((fd), (path), (flags)))); \
170       const int result = openat((fd), (path), (flags)); \
171       if (((flags) & O_BENEATH) == O_BENEATH) { \
172         EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_O_BENEATH, result); \
173       } else { \
174         EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
175       } \
176       if (result >= 0) { close(result); } \
177     } while (0)
178 #else
179 #define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \
180     do { \
181       SCOPED_TRACE(GTEST_STRINGIFY_(openat((fd), (path), (flags)))); \
182       const int result = openat((fd), (path), (flags)); \
183       EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
184       if (result >= 0) { close(result); } \
185     } while (0)
186 #endif
187 
188 // Expect a system call to fail with ECAPMODE.
189 #define EXPECT_CAPMODE(C) EXPECT_SYSCALL_FAIL(ECAPMODE, C)
190 
191 // Expect a system call to fail, but not with ECAPMODE.
192 #define EXPECT_FAIL_NOT_CAPMODE(C) EXPECT_SYSCALL_FAIL_NOT(ECAPMODE, C)
193 #define EXPECT_FAIL_VOID_NOT_CAPMODE(C) EXPECT_VOID_SYSCALL_FAIL_NOT(ECAPMODE, C)
194 
195 // Expect a system call to fail with ENOTCAPABLE.
196 #define EXPECT_NOTCAPABLE(C) EXPECT_SYSCALL_FAIL(ENOTCAPABLE, C)
197 
198 // Expect a system call to fail, but not with ENOTCAPABLE.
199 #define EXPECT_FAIL_NOT_NOTCAPABLE(C) EXPECT_SYSCALL_FAIL_NOT(ENOTCAPABLE, C)
200 
201 // Expect a system call to fail with either ENOTCAPABLE or ECAPMODE.
202 #define EXPECT_CAPFAIL(C) \
203     do { \
204       int rc = C; \
205       EXPECT_GT(0, rc); \
206       EXPECT_TRUE(errno == ECAPMODE || errno == ENOTCAPABLE) \
207         << #C << " did not fail with ECAPMODE/ENOTCAPABLE but " << errno \
208         << "(" << strerror(errno) << ")"; \
209     } while (0)
210 
211 // Ensure that 'rights' are a subset of 'max'.
212 #define EXPECT_RIGHTS_IN(rights, max) \
213     EXPECT_TRUE(cap_rights_contains((max), (rights)))  \
214     << "rights " << std::hex << *(rights) \
215     << " not a subset of " << std::hex << *(max)
216 
217 // Ensure rights are identical
218 #define EXPECT_RIGHTS_EQ(a, b) \
219   do { \
220     EXPECT_RIGHTS_IN((a), (b)); \
221     EXPECT_RIGHTS_IN((b), (a)); \
222   } while (0)
223 
224 // Get the state of a process as a single character.
225 //  - 'D': disk wait
226 //  - 'R': runnable
227 //  - 'S': sleeping/idle
228 //  - 'T': stopped
229 //  - 'Z': zombie
230 // On error, return either '?' or '\0'.
231 char ProcessState(int pid);
232 
233 // Check process state reaches a particular expected state (or two).
234 // Retries a few times to allow for timing issues.
235 #define EXPECT_PID_REACHES_STATES(pid, expected1, expected2) { \
236   int counter = 5; \
237   char state; \
238   do { \
239     state = ProcessState(pid); \
240     if (state == expected1 || state == expected2) break; \
241     usleep(100000); \
242   } while (--counter > 0); \
243   EXPECT_TRUE(state == expected1 || state == expected2) \
244       << " pid " << pid << " in state " << state; \
245 }
246 
247 #define EXPECT_PID_ALIVE(pid)   EXPECT_PID_REACHES_STATES(pid, 'R', 'S')
248 #define EXPECT_PID_DEAD(pid)    EXPECT_PID_REACHES_STATES(pid, 'Z', '\0')
249 #define EXPECT_PID_ZOMBIE(pid)  EXPECT_PID_REACHES_STATES(pid, 'Z', 'Z');
250 #define EXPECT_PID_GONE(pid)    EXPECT_PID_REACHES_STATES(pid, '\0', '\0');
251 
252 enum {
253   // Magic numbers for messages sent by child processes.
254   MSG_CHILD_STARTED = 1234,
255   MSG_CHILD_FD_RECEIVED = 4321,
256   // Magic numbers for messages sent by parent processes.
257   MSG_PARENT_REQUEST_CHILD_EXIT = 9999,
258   MSG_PARENT_CLOSED_FD = 10000,
259   MSG_PARENT_CHILD_SHOULD_RUN = 10001,
260 };
261 
262 #define SEND_INT_MESSAGE(fd, message) \
263   do { \
264     int _msg = message; \
265     EXPECT_EQ(sizeof(_msg), (size_t)write(fd, &_msg, sizeof(_msg))); \
266   } while (0)
267 
268 #define AWAIT_INT_MESSAGE(fd, expected) \
269   do {                                  \
270     int _msg = 0; \
271     EXPECT_EQ(sizeof(_msg), (size_t)read(fd, &_msg, sizeof(_msg))); \
272     EXPECT_EQ(expected, _msg); \
273   } while (0)
274 
275 // Mark a test that can only be run as root.
276 #define GTEST_SKIP_IF_NOT_ROOT() \
277   if (getuid() != 0) { GTEST_SKIP() << "requires root"; }
278 
279 extern std::string capsicum_test_bindir;
280 
281 #endif  // CAPSICUM_TEST_H
282