Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/capsicum-test/capsicum-test.h
39475 views
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
283