Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/kern/jaildesc.c
288951 views
1
/*
2
* Copyright (c) 2026 Mark Johnston <[email protected]>
3
*
4
* SPDX-License-Identifier: BSD-2-Clause
5
*/
6
7
#include <sys/param.h>
8
#include <sys/jail.h>
9
#include <sys/sysctl.h>
10
#include <sys/wait.h>
11
#include <sys/uio.h>
12
#include <sys/user.h>
13
14
#include <atf-c.h>
15
#include <errno.h>
16
#include <fcntl.h>
17
#include <poll.h>
18
#include <pthread.h>
19
#include <pwd.h>
20
#include <signal.h>
21
#include <string.h>
22
#include <unistd.h>
23
24
#include <kvm.h>
25
26
/*
27
* Block until a thread in the specified process is sleeping in the specified
28
* wait message.
29
*/
30
static void
31
wait_for_naptime(pid_t pid, const char *wmesg)
32
{
33
kvm_t *kd;
34
int count;
35
36
kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, NULL);
37
ATF_REQUIRE(kd != NULL);
38
for (;;) {
39
struct kinfo_proc *kip;
40
int i;
41
42
usleep(1000);
43
kip = kvm_getprocs(kd, KERN_PROC_PID | KERN_PROC_INC_THREAD,
44
pid, &count);
45
ATF_REQUIRE(kip != NULL);
46
for (i = 0; i < count; i++) {
47
ATF_REQUIRE(kip[i].ki_stat != SZOMB);
48
if (kip[i].ki_stat == SSLEEP &&
49
strcmp(kip[i].ki_wmesg, wmesg) == 0)
50
break;
51
}
52
if (i < count)
53
break;
54
}
55
56
kvm_close(kd);
57
}
58
59
/*
60
* Create a persistent jail and return an owning descriptor for it.
61
* The jail is removed when the returned descriptor is closed.
62
*/
63
static int
64
create_jail(const char *name)
65
{
66
struct iovec iov[8];
67
int desc, jid, n;
68
69
desc = -1;
70
n = 0;
71
iov[n].iov_base = __DECONST(void *, "name");
72
iov[n++].iov_len = strlen("name") + 1;
73
iov[n].iov_base = __DECONST(void *, name);
74
iov[n++].iov_len = strlen(name) + 1;
75
iov[n].iov_base = __DECONST(void *, "path");
76
iov[n++].iov_len = strlen("path") + 1;
77
iov[n].iov_base = __DECONST(void *, "/");
78
iov[n++].iov_len = strlen("/") + 1;
79
iov[n].iov_base = __DECONST(void *, "persist");
80
iov[n++].iov_len = strlen("persist") + 1;
81
iov[n].iov_base = NULL;
82
iov[n++].iov_len = 0;
83
iov[n].iov_base = __DECONST(void *, "desc");
84
iov[n++].iov_len = strlen("desc") + 1;
85
iov[n].iov_base = &desc;
86
iov[n++].iov_len = sizeof(desc);
87
jid = jail_set(iov, n, JAIL_CREATE | JAIL_OWN_DESC);
88
ATF_REQUIRE_MSG(jid >= 0, "jail_set: %s", strerror(errno));
89
return (desc);
90
}
91
92
static void *
93
poll_jaildesc(void *arg)
94
{
95
struct pollfd pfd;
96
97
pfd.fd = *(int *)arg;
98
pfd.events = POLLHUP;
99
(void)poll(&pfd, 1, 5000);
100
return ((void *)(uintptr_t)pfd.revents);
101
}
102
103
/*
104
* Regression test for the case where a jail descriptor is closed while a
105
* thread is blocking in poll(2) on it.
106
*/
107
ATF_TC(poll_close_race);
108
ATF_TC_HEAD(poll_close_race, tc)
109
{
110
atf_tc_set_md_var(tc, "require.user", "root");
111
}
112
ATF_TC_BODY(poll_close_race, tc)
113
{
114
pthread_t thr;
115
uintptr_t revents;
116
int error, jd;
117
118
jd = create_jail("jaildesc_poll_close_race");
119
120
error = pthread_create(&thr, NULL, poll_jaildesc, &jd);
121
ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error));
122
123
wait_for_naptime(getpid(), "select");
124
125
ATF_REQUIRE_MSG(close(jd) == 0, "close: %s", strerror(errno));
126
127
error = pthread_join(thr, (void *)&revents);
128
ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error));
129
ATF_REQUIRE_EQ(revents, POLLNVAL);
130
}
131
132
/*
133
* Verify that poll(2) of a jail descriptor returns POLLHUP when the jail
134
* is removed.
135
*/
136
ATF_TC(poll_remove_wakeup);
137
ATF_TC_HEAD(poll_remove_wakeup, tc)
138
{
139
atf_tc_set_md_var(tc, "require.user", "root");
140
}
141
ATF_TC_BODY(poll_remove_wakeup, tc)
142
{
143
pthread_t thr;
144
uintptr_t revents;
145
int error, jd;
146
147
jd = create_jail("jaildesc_poll_remove_wakeup");
148
149
error = pthread_create(&thr, NULL, poll_jaildesc, &jd);
150
ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error));
151
152
wait_for_naptime(getpid(), "select");
153
154
ATF_REQUIRE_MSG(jail_remove_jd(jd) == 0,
155
"jail_remove_jd: %s", strerror(errno));
156
157
error = pthread_join(thr, (void *)&revents);
158
ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error));
159
ATF_REQUIRE_EQ(revents, POLLHUP);
160
161
ATF_REQUIRE_MSG(close(jd) == 0, "close: %s", strerror(errno));
162
}
163
164
static int
165
get_jaildesc(const char *name)
166
{
167
struct iovec iov[4];
168
char namebuf[MAXHOSTNAMELEN];
169
int desc, jid, n;
170
171
strlcpy(namebuf, name, sizeof(namebuf));
172
desc = -1;
173
n = 0;
174
iov[n].iov_base = __DECONST(void *, "name");
175
iov[n++].iov_len = strlen("name") + 1;
176
iov[n].iov_base = namebuf;
177
iov[n++].iov_len = sizeof(namebuf);
178
iov[n].iov_base = __DECONST(void *, "desc");
179
iov[n++].iov_len = strlen("desc") + 1;
180
iov[n].iov_base = &desc;
181
iov[n++].iov_len = sizeof(desc);
182
jid = jail_get(iov, n, JAIL_GET_DESC);
183
ATF_REQUIRE_MSG(jid >= 0, "jail_get: %s", strerror(errno));
184
return (desc);
185
}
186
187
/*
188
* Regression test for the same use-after-free as poll_close_race, but with a
189
* non-owning JAIL_GET_DESC descriptor obtained without root privileges.
190
*/
191
ATF_TC(poll_close_race_get_desc);
192
ATF_TC_HEAD(poll_close_race_get_desc, tc)
193
{
194
atf_tc_set_md_var(tc, "require.user", "root");
195
}
196
ATF_TC_BODY(poll_close_race_get_desc, tc)
197
{
198
struct passwd *pw;
199
pthread_t thr;
200
uintptr_t revents;
201
int error, jd, owning_jd;
202
203
/* Create the jail as root; keep the owning descriptor for cleanup. */
204
owning_jd = create_jail("jaildesc_poll_close_get_desc");
205
206
/*
207
* Drop root privileges. jail_get(2) with JAIL_GET_DESC does not
208
* require PRIV_JAIL_REMOVE, so a non-root process in the host prison
209
* can obtain a read-only descriptor for any visible jail.
210
*/
211
pw = getpwnam("nobody");
212
ATF_REQUIRE_MSG(pw != NULL, "getpwnam: %s", strerror(errno));
213
ATF_REQUIRE_MSG(setuid(pw->pw_uid) == 0, "setuid: %s", strerror(errno));
214
215
jd = get_jaildesc("jaildesc_poll_close_get_desc");
216
217
error = pthread_create(&thr, NULL, poll_jaildesc, &jd);
218
ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error));
219
220
wait_for_naptime(getpid(), "select");
221
222
ATF_REQUIRE_MSG(close(jd) == 0, "close: %s", strerror(errno));
223
224
error = pthread_join(thr, (void *)&revents);
225
ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error));
226
ATF_REQUIRE_EQ(revents, POLLNVAL);
227
228
ATF_REQUIRE_MSG(close(owning_jd) == 0, "close: %s", strerror(errno));
229
}
230
231
/*
232
* Verify that a process inside a jail cannot obtain a jail descriptor for
233
* its own jail.
234
*/
235
ATF_TC(curjail_get);
236
ATF_TC_HEAD(curjail_get, tc)
237
{
238
atf_tc_set_md_var(tc, "require.user", "root");
239
}
240
ATF_TC_BODY(curjail_get, tc)
241
{
242
char namebuf[MAXHOSTNAMELEN];
243
struct iovec iov[4];
244
int desc, error, jid, n;
245
246
(void)create_jail("jaildesc_get_desc_current_jail");
247
248
strlcpy(namebuf, "jaildesc_get_desc_current_jail", sizeof(namebuf));
249
jid = -1;
250
n = 0;
251
iov[n].iov_base = __DECONST(void *, "name");
252
iov[n++].iov_len = sizeof("name");
253
iov[n].iov_base = namebuf;
254
iov[n++].iov_len = strlen(namebuf) + 1;
255
jid = jail_get(iov, n, 0);
256
ATF_REQUIRE_MSG(jid >= 0, "jail_get: %s", strerror(errno));
257
258
error = jail_attach(jid);
259
ATF_REQUIRE_MSG(error == 0, "jail_attach: %s", strerror(errno));
260
261
/*
262
* Now that we are inside the jail, verify that we cannot obtain a
263
* descriptor for it.
264
*/
265
strlcpy(namebuf, "jaildesc_get_desc_current_jail", sizeof(namebuf));
266
desc = -1;
267
n = 0;
268
iov[n].iov_base = __DECONST(void *, "name");
269
iov[n++].iov_len = sizeof("name");
270
iov[n].iov_base = namebuf;
271
iov[n++].iov_len = strlen(namebuf) + 1;
272
iov[n].iov_base = __DECONST(void *, "desc");
273
iov[n++].iov_len = sizeof("desc");
274
iov[n].iov_base = &desc;
275
iov[n++].iov_len = sizeof(desc);
276
ATF_REQUIRE_MSG(jail_get(iov, n, JAIL_GET_DESC) == -1,
277
"jail_get succeeded unexpectedly");
278
ATF_REQUIRE_MSG(jail_get(iov, n, JAIL_OWN_DESC) == -1,
279
"jail_get succeeded unexpectedly");
280
}
281
282
static void
283
nop(int signum __unused)
284
{
285
}
286
287
/*
288
* Close an owning descriptor while in the corresponding jail.
289
*/
290
ATF_TC(self_destruct);
291
ATF_TC_HEAD(self_destruct, tc)
292
{
293
atf_tc_set_md_var(tc, "require.user", "root");
294
}
295
ATF_TC_BODY(self_destruct, tc)
296
{
297
int error, jd, status;
298
pid_t pid;
299
300
jd = create_jail("jaildesc_self_destruct");
301
302
pid = fork();
303
ATF_REQUIRE_MSG(pid >= 0, "fork: %s", strerror(errno));
304
if (pid == 0) {
305
struct sigaction sa;
306
307
error = jail_attach_jd(jd);
308
if (error != 0)
309
_exit(1);
310
311
sa.sa_handler = nop;
312
sa.sa_flags = 0;
313
sigemptyset(&sa.sa_mask);
314
error = sigaction(SIGALRM, &sa, NULL);
315
if (error != 0)
316
_exit(2);
317
318
pause();
319
close(jd);
320
/* NOTREACHED? */
321
_exit(3);
322
}
323
324
wait_for_naptime(pid, "sigsusp");
325
326
error = close(jd);
327
ATF_REQUIRE_MSG(error == 0, "close: %s", strerror(errno));
328
329
error = kill(pid, SIGALRM);
330
ATF_REQUIRE_MSG(error == 0, "kill: %s", strerror(errno));
331
332
pid = waitpid(pid, &status, 0);
333
ATF_REQUIRE_MSG(pid >= 0, "waitpid: %s", strerror(errno));
334
ATF_REQUIRE(WIFSIGNALED(status));
335
ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL);
336
}
337
338
/*
339
* Try to get an fd for a non-existent jail.
340
*/
341
ATF_TC(at_desc_bad_fd);
342
ATF_TC_HEAD(at_desc_bad_fd, tc)
343
{
344
atf_tc_set_md_var(tc, "require.user", "root");
345
}
346
ATF_TC_BODY(at_desc_bad_fd, tc)
347
{
348
int jd, status;
349
pid_t pid;
350
351
jd = create_jail("jaildesc_at_desc_bad_fd");
352
353
pid = fork();
354
ATF_REQUIRE_MSG(pid >= 0, "fork: %s", strerror(errno));
355
if (pid == 0) {
356
struct iovec iov[4];
357
char namebuf[MAXHOSTNAMELEN];
358
int desc, i, n;
359
360
if (jail_attach_jd(jd) != 0)
361
_exit(1);
362
363
/* Regression test: loop here to trigger a refcount leak. */
364
for (i = 0; i < 100; i++) {
365
strlcpy(namebuf, "nonexistent", sizeof(namebuf));
366
desc = STDIN_FILENO;
367
n = 0;
368
iov[n].iov_base = __DECONST(void *, "name");
369
iov[n++].iov_len = strlen("name") + 1;
370
iov[n].iov_base = namebuf;
371
iov[n++].iov_len = sizeof(namebuf);
372
iov[n].iov_base = __DECONST(void *, "desc");
373
iov[n++].iov_len = strlen("desc") + 1;
374
iov[n].iov_base = &desc;
375
iov[n++].iov_len = sizeof(desc);
376
if (jail_get(iov, n, JAIL_AT_DESC) != -1)
377
_exit(2);
378
if (errno != EINVAL)
379
_exit(3);
380
}
381
_exit(0);
382
}
383
384
pid = waitpid(pid, &status, 0);
385
ATF_REQUIRE_MSG(pid >= 0, "waitpid: %s", strerror(errno));
386
ATF_REQUIRE_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
387
"child failed with status %d", status);
388
389
ATF_REQUIRE_MSG(close(jd) == 0, "close: %s", strerror(errno));
390
}
391
392
ATF_TP_ADD_TCS(tp)
393
{
394
ATF_TP_ADD_TC(tp, poll_close_race);
395
ATF_TP_ADD_TC(tp, poll_remove_wakeup);
396
ATF_TP_ADD_TC(tp, poll_close_race_get_desc);
397
ATF_TP_ADD_TC(tp, curjail_get);
398
ATF_TP_ADD_TC(tp, self_destruct);
399
ATF_TP_ADD_TC(tp, at_desc_bad_fd);
400
401
return (atf_no_error());
402
}
403
404