]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/capsicum-test/capsicum-test.h
ncurses: import version 6.2-20210220
[FreeBSD/FreeBSD.git] / contrib / capsicum-test / capsicum-test.h
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       EXPECT_GT(0, C); \
144       EXPECT_EQ(E, errno); \
145     } while (0)
146
147 // Expect a syscall to fail with anything other than the given error.
148 #define EXPECT_SYSCALL_FAIL_NOT(E, C) \
149     do { \
150       EXPECT_GT(0, C); \
151       EXPECT_NE(E, errno); \
152     } while (0)
153
154 // Expect a void syscall to fail with anything other than the given error.
155 #define EXPECT_VOID_SYSCALL_FAIL_NOT(E, C)   \
156     do { \
157       errno = 0; \
158       C; \
159       EXPECT_NE(E, errno) << #C << " failed with ECAPMODE"; \
160     } while (0)
161
162 // Expect a system call to fail due to path traversal; exact error
163 // code is OS-specific.
164 #ifdef O_BENEATH
165 #define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \
166     do { \
167       const int result = openat((fd), (path), (flags)); \
168       if (((flags) & O_BENEATH) == O_BENEATH) { \
169         EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_O_BENEATH, result); \
170       } else { \
171         EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
172       } \
173       if (result >= 0) { close(result); } \
174     } while (0)
175 #else
176 #define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \
177     do { \
178       const int result = openat((fd), (path), (flags)); \
179       EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
180       if (result >= 0) { close(result); } \
181     } while (0)
182 #endif
183
184 // Expect a system call to fail with ECAPMODE.
185 #define EXPECT_CAPMODE(C) EXPECT_SYSCALL_FAIL(ECAPMODE, C)
186
187 // Expect a system call to fail, but not with ECAPMODE.
188 #define EXPECT_FAIL_NOT_CAPMODE(C) EXPECT_SYSCALL_FAIL_NOT(ECAPMODE, C)
189 #define EXPECT_FAIL_VOID_NOT_CAPMODE(C) EXPECT_VOID_SYSCALL_FAIL_NOT(ECAPMODE, C)
190
191 // Expect a system call to fail with ENOTCAPABLE.
192 #define EXPECT_NOTCAPABLE(C) EXPECT_SYSCALL_FAIL(ENOTCAPABLE, C)
193
194 // Expect a system call to fail, but not with ENOTCAPABLE.
195 #define EXPECT_FAIL_NOT_NOTCAPABLE(C) EXPECT_SYSCALL_FAIL_NOT(ENOTCAPABLE, C)
196
197 // Expect a system call to fail with either ENOTCAPABLE or ECAPMODE.
198 #define EXPECT_CAPFAIL(C) \
199     do { \
200       int rc = C; \
201       EXPECT_GT(0, rc); \
202       EXPECT_TRUE(errno == ECAPMODE || errno == ENOTCAPABLE) \
203         << #C << " did not fail with ECAPMODE/ENOTCAPABLE but " << errno; \
204     } while (0)
205
206 // Ensure that 'rights' are a subset of 'max'.
207 #define EXPECT_RIGHTS_IN(rights, max) \
208     EXPECT_TRUE(cap_rights_contains((max), (rights)))  \
209     << "rights " << std::hex << *(rights) \
210     << " not a subset of " << std::hex << *(max)
211
212 // Ensure rights are identical
213 #define EXPECT_RIGHTS_EQ(a, b) \
214   do { \
215     EXPECT_RIGHTS_IN((a), (b)); \
216     EXPECT_RIGHTS_IN((b), (a)); \
217   } while (0)
218
219 // Get the state of a process as a single character.
220 //  - 'D': disk wait
221 //  - 'R': runnable
222 //  - 'S': sleeping/idle
223 //  - 'T': stopped
224 //  - 'Z': zombie
225 // On error, return either '?' or '\0'.
226 char ProcessState(int pid);
227
228 // Check process state reaches a particular expected state (or two).
229 // Retries a few times to allow for timing issues.
230 #define EXPECT_PID_REACHES_STATES(pid, expected1, expected2) { \
231   int counter = 5; \
232   char state; \
233   do { \
234     state = ProcessState(pid); \
235     if (state == expected1 || state == expected2) break; \
236     usleep(100000); \
237   } while (--counter > 0); \
238   EXPECT_TRUE(state == expected1 || state == expected2) \
239       << " pid " << pid << " in state " << state; \
240 }
241
242 #define EXPECT_PID_ALIVE(pid)   EXPECT_PID_REACHES_STATES(pid, 'R', 'S')
243 #define EXPECT_PID_DEAD(pid)    EXPECT_PID_REACHES_STATES(pid, 'Z', '\0')
244 #define EXPECT_PID_ZOMBIE(pid)  EXPECT_PID_REACHES_STATES(pid, 'Z', 'Z');
245 #define EXPECT_PID_GONE(pid)    EXPECT_PID_REACHES_STATES(pid, '\0', '\0');
246
247 // Mark a test that can only be run as root.
248 #define GTEST_SKIP_IF_NOT_ROOT() \
249   if (getuid() != 0) { GTEST_SKIP() << "requires root"; }
250
251 extern std::string capsicum_test_bindir;
252
253 #endif  // CAPSICUM_TEST_H