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