1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020-2021 Kyle Evans <kevans@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/param.h>
29 #include <sys/cpuset.h>
30 #include <sys/jail.h>
31 #include <sys/procdesc.h>
32 #include <sys/select.h>
33 #include <sys/socket.h>
34 #include <sys/uio.h>
35 #include <sys/wait.h>
36
37 #include <assert.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42
43 #include <atf-c.h>
44
45 #define SP_PARENT 0
46 #define SP_CHILD 1
47
48 struct jail_test_info {
49 cpuset_t jail_tidmask;
50 cpusetid_t jail_cpuset;
51 cpusetid_t jail_child_cpuset;
52 };
53
54 struct jail_test_cb_params {
55 struct jail_test_info info;
56 cpuset_t mask;
57 cpusetid_t rootid;
58 cpusetid_t setid;
59 };
60
61 typedef void (*jail_test_cb)(struct jail_test_cb_params *);
62
63 #define FAILURE_JAIL 42
64 #define FAILURE_MASK 43
65 #define FAILURE_JAILSET 44
66 #define FAILURE_PIDSET 45
67 #define FAILURE_SEND 46
68 #define FAILURE_DEADLK 47
69 #define FAILURE_ATTACH 48
70 #define FAILURE_BADAFFIN 49
71 #define FAILURE_SUCCESS 50
72
73 static const char *
do_jail_errstr(int error)74 do_jail_errstr(int error)
75 {
76
77 switch (error) {
78 case FAILURE_JAIL:
79 return ("jail_set(2) failed");
80 case FAILURE_MASK:
81 return ("Failed to get the thread cpuset mask");
82 case FAILURE_JAILSET:
83 return ("Failed to get the jail setid");
84 case FAILURE_PIDSET:
85 return ("Failed to get the pid setid");
86 case FAILURE_SEND:
87 return ("Failed to send(2) cpuset information");
88 case FAILURE_DEADLK:
89 return ("Deadlock hit trying to attach to jail");
90 case FAILURE_ATTACH:
91 return ("jail_attach(2) failed");
92 case FAILURE_BADAFFIN:
93 return ("Unexpected post-attach affinity");
94 case FAILURE_SUCCESS:
95 return ("jail_attach(2) succeeded, but should have failed.");
96 default:
97 return (NULL);
98 }
99 }
100
101 static void
skip_ltncpu(int ncpu,cpuset_t * mask)102 skip_ltncpu(int ncpu, cpuset_t *mask)
103 {
104
105 CPU_ZERO(mask);
106 ATF_REQUIRE_EQ(0, cpuset_getaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
107 -1, sizeof(*mask), mask));
108 if (CPU_COUNT(mask) < ncpu)
109 atf_tc_skip("Test requires %d or more cores.", ncpu);
110 }
111
112 static void
skip_ltncpu_root(int ncpu,cpuset_t * mask)113 skip_ltncpu_root(int ncpu, cpuset_t *mask)
114 {
115
116 CPU_ZERO(mask);
117 ATF_REQUIRE_EQ(0, cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID,
118 -1, sizeof(*mask), mask));
119 if (CPU_COUNT(mask) < ncpu) {
120 atf_tc_skip("Test requires cpuset root with %d or more cores.",
121 ncpu);
122 }
123 }
124
125 ATF_TC(newset);
ATF_TC_HEAD(newset,tc)126 ATF_TC_HEAD(newset, tc)
127 {
128 atf_tc_set_md_var(tc, "descr", "Test cpuset(2)");
129 }
ATF_TC_BODY(newset,tc)130 ATF_TC_BODY(newset, tc)
131 {
132 cpusetid_t nsetid, setid, qsetid;
133
134 /* Obtain our initial set id. */
135 ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
136 &setid));
137
138 /* Create a new one. */
139 ATF_REQUIRE_EQ(0, cpuset(&nsetid));
140 ATF_CHECK(nsetid != setid);
141
142 /* Query id again, make sure it's equal to the one we just got. */
143 ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
144 &qsetid));
145 ATF_CHECK_EQ(nsetid, qsetid);
146 }
147
148 ATF_TC(transient);
ATF_TC_HEAD(transient,tc)149 ATF_TC_HEAD(transient, tc)
150 {
151 atf_tc_set_md_var(tc, "descr",
152 "Test that transient cpusets are freed.");
153 }
ATF_TC_BODY(transient,tc)154 ATF_TC_BODY(transient, tc)
155 {
156 cpusetid_t isetid, scratch, setid;
157
158 ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1,
159 &isetid));
160
161 ATF_REQUIRE_EQ(0, cpuset(&setid));
162 ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
163 setid, &scratch));
164
165 /*
166 * Return back to our initial cpuset; the kernel should free the cpuset
167 * we just created.
168 */
169 ATF_REQUIRE_EQ(0, cpuset_setid(CPU_WHICH_PID, -1, isetid));
170 ATF_REQUIRE_EQ(-1, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
171 setid, &scratch));
172 ATF_CHECK_EQ(ESRCH, errno);
173 }
174
175 ATF_TC(deadlk);
ATF_TC_HEAD(deadlk,tc)176 ATF_TC_HEAD(deadlk, tc)
177 {
178 atf_tc_set_md_var(tc, "descr", "Test against disjoint cpusets.");
179 atf_tc_set_md_var(tc, "require.user", "root");
180 }
ATF_TC_BODY(deadlk,tc)181 ATF_TC_BODY(deadlk, tc)
182 {
183 cpusetid_t setid;
184 cpuset_t dismask, mask, omask;
185 int fcpu, i, found, ncpu, second;
186
187 /* Make sure we have 3 cpus, so we test partial overlap. */
188 skip_ltncpu(3, &omask);
189
190 ATF_REQUIRE_EQ(0, cpuset(&setid));
191 CPU_ZERO(&mask);
192 CPU_ZERO(&dismask);
193 CPU_COPY(&omask, &mask);
194 CPU_COPY(&omask, &dismask);
195 fcpu = CPU_FFS(&mask);
196 ncpu = CPU_COUNT(&mask);
197
198 /*
199 * Turn off all but the first two for mask, turn off the first for
200 * dismask and turn them all off for both after the third.
201 */
202 for (i = fcpu - 1, found = 0; i < CPU_MAXSIZE && found != ncpu; i++) {
203 if (CPU_ISSET(i, &omask)) {
204 found++;
205 if (found == 1) {
206 CPU_CLR(i, &dismask);
207 } else if (found == 2) {
208 second = i;
209 } else if (found >= 3) {
210 CPU_CLR(i, &mask);
211 if (found > 3)
212 CPU_CLR(i, &dismask);
213 }
214 }
215 }
216
217 ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
218 -1, sizeof(mask), &mask));
219
220 /* Must be a strict subset! */
221 ATF_REQUIRE_EQ(-1, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
222 -1, sizeof(dismask), &dismask));
223 ATF_REQUIRE_EQ(EINVAL, errno);
224
225 /*
226 * We'll set our anonymous set to the 0,1 set that currently matches
227 * the process. If we then set the process to the 1,2 set that's in
228 * dismask, we should then personally be restricted down to the single
229 * overlapping CPOU.
230 */
231 ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
232 -1, sizeof(mask), &mask));
233 ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
234 -1, sizeof(dismask), &dismask));
235 ATF_REQUIRE_EQ(0, cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
236 -1, sizeof(mask), &mask));
237 ATF_REQUIRE_EQ(1, CPU_COUNT(&mask));
238 ATF_REQUIRE(CPU_ISSET(second, &mask));
239
240 /*
241 * Finally, clearing the overlap and attempting to set the process
242 * cpuset to a completely disjoint mask should fail, because this
243 * process will then not have anything to run on.
244 */
245 CPU_CLR(second, &dismask);
246 ATF_REQUIRE_EQ(-1, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
247 -1, sizeof(dismask), &dismask));
248 ATF_REQUIRE_EQ(EDEADLK, errno);
249 }
250
251 static int
create_jail(void)252 create_jail(void)
253 {
254 struct iovec iov[2];
255 char *name;
256 int error;
257
258 if (asprintf(&name, "cpuset_%d", getpid()) == -1)
259 _exit(42);
260
261 iov[0].iov_base = "name";
262 iov[0].iov_len = 5;
263
264 iov[1].iov_base = name;
265 iov[1].iov_len = strlen(name) + 1;
266
267 error = jail_set(iov, 2, JAIL_CREATE | JAIL_ATTACH);
268 free(name);
269 if (error < 0)
270 return (FAILURE_JAIL);
271 return (0);
272 }
273
274 static int
do_jail(int sock)275 do_jail(int sock)
276 {
277 struct jail_test_info info;
278 int error;
279
280 error = create_jail();
281 if (error != 0)
282 return (error);
283
284 /* Record parameters, kick them over, then make a swift exit. */
285 CPU_ZERO(&info.jail_tidmask);
286 error = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
287 -1, sizeof(info.jail_tidmask), &info.jail_tidmask);
288 if (error != 0)
289 return (FAILURE_MASK);
290
291 error = cpuset_getid(CPU_LEVEL_ROOT, CPU_WHICH_TID, -1,
292 &info.jail_cpuset);
293 if (error != 0)
294 return (FAILURE_JAILSET);
295 error = cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
296 &info.jail_child_cpuset);
297 if (error != 0)
298 return (FAILURE_PIDSET);
299 if (send(sock, &info, sizeof(info), 0) != sizeof(info))
300 return (FAILURE_SEND);
301 return (0);
302 }
303
304 static void
do_jail_test(int ncpu,bool newset,jail_test_cb prologue,jail_test_cb epilogue)305 do_jail_test(int ncpu, bool newset, jail_test_cb prologue,
306 jail_test_cb epilogue)
307 {
308 struct jail_test_cb_params cbp;
309 const char *errstr;
310 pid_t pid;
311 int error, sock, sockpair[2], status;
312
313 memset(&cbp.info, '\0', sizeof(cbp.info));
314
315 skip_ltncpu(ncpu, &cbp.mask);
316
317 ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1,
318 &cbp.rootid));
319 if (newset)
320 ATF_REQUIRE_EQ(0, cpuset(&cbp.setid));
321 else
322 ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
323 -1, &cbp.setid));
324 /* Special hack for prison0; it uses cpuset 1 as the root. */
325 if (cbp.rootid == 0)
326 cbp.rootid = 1;
327
328 /* Not every test needs early setup. */
329 if (prologue != NULL)
330 (*prologue)(&cbp);
331
332 ATF_REQUIRE_EQ(0, socketpair(PF_UNIX, SOCK_STREAM, 0, sockpair));
333 ATF_REQUIRE((pid = fork()) != -1);
334
335 if (pid == 0) {
336 /* Child */
337 close(sockpair[SP_PARENT]);
338 sock = sockpair[SP_CHILD];
339
340 _exit(do_jail(sock));
341 } else {
342 /* Parent */
343 sock = sockpair[SP_PARENT];
344 close(sockpair[SP_CHILD]);
345
346 while ((error = waitpid(pid, &status, 0)) == -1 &&
347 errno == EINTR) {
348 }
349
350 ATF_REQUIRE_EQ(sizeof(cbp.info), recv(sock, &cbp.info,
351 sizeof(cbp.info), 0));
352
353 /* Sanity check the exit info. */
354 ATF_REQUIRE_EQ(pid, error);
355 ATF_REQUIRE(WIFEXITED(status));
356 if (WEXITSTATUS(status) != 0) {
357 errstr = do_jail_errstr(WEXITSTATUS(status));
358 if (errstr != NULL)
359 atf_tc_fail("%s", errstr);
360 else
361 atf_tc_fail("Unknown error '%d'",
362 WEXITSTATUS(status));
363 }
364
365 epilogue(&cbp);
366 }
367 }
368
369 static void
jail_attach_mutate_pro(struct jail_test_cb_params * cbp)370 jail_attach_mutate_pro(struct jail_test_cb_params *cbp)
371 {
372 cpuset_t *mask;
373 int count;
374
375 mask = &cbp->mask;
376
377 /* Knock out the first cpu. */
378 count = CPU_COUNT(mask);
379 CPU_CLR(CPU_FFS(mask) - 1, mask);
380 ATF_REQUIRE_EQ(count - 1, CPU_COUNT(mask));
381 ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
382 -1, sizeof(*mask), mask));
383 }
384
385 static void
jail_attach_newbase_epi(struct jail_test_cb_params * cbp)386 jail_attach_newbase_epi(struct jail_test_cb_params *cbp)
387 {
388 struct jail_test_info *info;
389 cpuset_t *mask;
390
391 info = &cbp->info;
392 mask = &cbp->mask;
393
394 /*
395 * The rootid test has been thrown in because a bug was discovered
396 * where any newly derived cpuset during attach would be parented to
397 * the wrong cpuset. Otherwise, we should observe that a new cpuset
398 * has been created for this process.
399 */
400 ATF_REQUIRE(info->jail_cpuset != cbp->rootid);
401 ATF_REQUIRE(info->jail_cpuset != cbp->setid);
402 ATF_REQUIRE(info->jail_cpuset != info->jail_child_cpuset);
403 ATF_REQUIRE_EQ(0, CPU_CMP(mask, &info->jail_tidmask));
404 }
405
406 ATF_TC(jail_attach_newbase);
ATF_TC_HEAD(jail_attach_newbase,tc)407 ATF_TC_HEAD(jail_attach_newbase, tc)
408 {
409 atf_tc_set_md_var(tc, "descr",
410 "Test jail attachment effect on affinity with a new base cpuset.");
411 atf_tc_set_md_var(tc, "require.user", "root");
412 }
ATF_TC_BODY(jail_attach_newbase,tc)413 ATF_TC_BODY(jail_attach_newbase, tc)
414 {
415
416 /* Need >= 2 cpus to test restriction. */
417 do_jail_test(2, true, &jail_attach_mutate_pro,
418 &jail_attach_newbase_epi);
419 }
420
421 ATF_TC(jail_attach_newbase_plain);
ATF_TC_HEAD(jail_attach_newbase_plain,tc)422 ATF_TC_HEAD(jail_attach_newbase_plain, tc)
423 {
424 atf_tc_set_md_var(tc, "descr",
425 "Test jail attachment effect on affinity with a new, unmodified base cpuset.");
426 atf_tc_set_md_var(tc, "require.user", "root");
427 }
ATF_TC_BODY(jail_attach_newbase_plain,tc)428 ATF_TC_BODY(jail_attach_newbase_plain, tc)
429 {
430
431 do_jail_test(2, true, NULL, &jail_attach_newbase_epi);
432 }
433
434 /*
435 * Generic epilogue for tests that are expecting to use the jail's root cpuset
436 * with their own mask, whether that's been modified or not.
437 */
438 static void
jail_attach_jset_epi(struct jail_test_cb_params * cbp)439 jail_attach_jset_epi(struct jail_test_cb_params *cbp)
440 {
441 struct jail_test_info *info;
442 cpuset_t *mask;
443
444 info = &cbp->info;
445 mask = &cbp->mask;
446
447 ATF_REQUIRE(info->jail_cpuset != cbp->setid);
448 ATF_REQUIRE_EQ(info->jail_cpuset, info->jail_child_cpuset);
449 ATF_REQUIRE_EQ(0, CPU_CMP(mask, &info->jail_tidmask));
450 }
451
452 ATF_TC(jail_attach_prevbase);
ATF_TC_HEAD(jail_attach_prevbase,tc)453 ATF_TC_HEAD(jail_attach_prevbase, tc)
454 {
455 atf_tc_set_md_var(tc, "descr",
456 "Test jail attachment effect on affinity without a new base.");
457 atf_tc_set_md_var(tc, "require.user", "root");
458 }
ATF_TC_BODY(jail_attach_prevbase,tc)459 ATF_TC_BODY(jail_attach_prevbase, tc)
460 {
461
462 do_jail_test(2, false, &jail_attach_mutate_pro, &jail_attach_jset_epi);
463 }
464
465 static void
jail_attach_plain_pro(struct jail_test_cb_params * cbp)466 jail_attach_plain_pro(struct jail_test_cb_params *cbp)
467 {
468
469 if (cbp->setid != cbp->rootid)
470 atf_tc_skip("Must be running with the root cpuset.");
471 }
472
473 ATF_TC(jail_attach_plain);
ATF_TC_HEAD(jail_attach_plain,tc)474 ATF_TC_HEAD(jail_attach_plain, tc)
475 {
476 atf_tc_set_md_var(tc, "descr",
477 "Test jail attachment effect on affinity without specialization.");
478 atf_tc_set_md_var(tc, "require.user", "root");
479 }
ATF_TC_BODY(jail_attach_plain,tc)480 ATF_TC_BODY(jail_attach_plain, tc)
481 {
482
483 do_jail_test(1, false, &jail_attach_plain_pro, &jail_attach_jset_epi);
484 }
485
486 static int
jail_attach_disjoint_newjail(int fd)487 jail_attach_disjoint_newjail(int fd)
488 {
489 struct iovec iov[2];
490 char *name;
491 int jid;
492
493 if (asprintf(&name, "cpuset_%d", getpid()) == -1)
494 _exit(42);
495
496 iov[0].iov_base = "name";
497 iov[0].iov_len = sizeof("name");
498
499 iov[1].iov_base = name;
500 iov[1].iov_len = strlen(name) + 1;
501
502 if ((jid = jail_set(iov, 2, JAIL_CREATE | JAIL_ATTACH)) < 0)
503 return (FAILURE_JAIL);
504
505 /* Signal that we're ready. */
506 write(fd, &jid, sizeof(jid));
507 for (;;) {
508 /* Spin */
509 }
510 }
511
512 static int
wait_jail(int fd,int pfd)513 wait_jail(int fd, int pfd)
514 {
515 fd_set lset;
516 struct timeval tv;
517 int error, jid, maxfd;
518
519 FD_ZERO(&lset);
520 FD_SET(fd, &lset);
521 FD_SET(pfd, &lset);
522
523 maxfd = MAX(fd, pfd);
524
525 tv.tv_sec = 5;
526 tv.tv_usec = 0;
527
528 /* Wait for jid to be written. */
529 do {
530 error = select(maxfd + 1, &lset, NULL, NULL, &tv);
531 } while (error == -1 && errno == EINTR);
532
533 if (error == 0) {
534 atf_tc_fail("Jail creator did not respond in time.");
535 }
536
537 ATF_REQUIRE_MSG(error > 0, "Unexpected error %d from select()", errno);
538
539 if (FD_ISSET(pfd, &lset)) {
540 /* Process died */
541 atf_tc_fail("Jail creator died unexpectedly.");
542 }
543
544 ATF_REQUIRE(FD_ISSET(fd, &lset));
545 ATF_REQUIRE_EQ(sizeof(jid), recv(fd, &jid, sizeof(jid), 0));
546
547 return (jid);
548 }
549
550 static int
try_attach_child(int jid,cpuset_t * expected_mask)551 try_attach_child(int jid, cpuset_t *expected_mask)
552 {
553 cpuset_t mask;
554
555 if (jail_attach(jid) == -1) {
556 if (errno == EDEADLK)
557 return (FAILURE_DEADLK);
558 return (FAILURE_ATTACH);
559 }
560
561 if (expected_mask == NULL)
562 return (FAILURE_SUCCESS);
563
564 /* If we had an expected mask, check it against the new process mask. */
565 CPU_ZERO(&mask);
566 if (cpuset_getaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
567 -1, sizeof(mask), &mask) != 0) {
568 return (FAILURE_MASK);
569 }
570
571 if (CPU_CMP(expected_mask, &mask) != 0)
572 return (FAILURE_BADAFFIN);
573
574 return (0);
575 }
576
577 static void
try_attach(int jid,cpuset_t * expected_mask)578 try_attach(int jid, cpuset_t *expected_mask)
579 {
580 const char *errstr;
581 pid_t pid;
582 int error, fail, status;
583
584 ATF_REQUIRE(expected_mask != NULL);
585 ATF_REQUIRE((pid = fork()) != -1);
586 if (pid == 0)
587 _exit(try_attach_child(jid, expected_mask));
588
589 while ((error = waitpid(pid, &status, 0)) == -1 && errno == EINTR) {
590 /* Try again. */
591 }
592
593 /* Sanity check the exit info. */
594 ATF_REQUIRE_EQ(pid, error);
595 ATF_REQUIRE(WIFEXITED(status));
596 if ((fail = WEXITSTATUS(status)) != 0) {
597 errstr = do_jail_errstr(fail);
598 if (errstr != NULL)
599 atf_tc_fail("%s", errstr);
600 else
601 atf_tc_fail("Unknown error '%d'", WEXITSTATUS(status));
602 }
603 }
604
605 ATF_TC(jail_attach_disjoint);
ATF_TC_HEAD(jail_attach_disjoint,tc)606 ATF_TC_HEAD(jail_attach_disjoint, tc)
607 {
608 atf_tc_set_md_var(tc, "descr",
609 "Test root attachment into completely disjoint jail cpuset.");
610 atf_tc_set_md_var(tc, "require.user", "root");
611 }
ATF_TC_BODY(jail_attach_disjoint,tc)612 ATF_TC_BODY(jail_attach_disjoint, tc)
613 {
614 cpuset_t smask, jmask;
615 int sockpair[2];
616 cpusetid_t setid;
617 pid_t pid;
618 int fcpu, jid, pfd, sock, scpu;
619
620 ATF_REQUIRE_EQ(0, cpuset(&setid));
621
622 skip_ltncpu(2, &jmask);
623 fcpu = CPU_FFS(&jmask) - 1;
624 ATF_REQUIRE_EQ(0, socketpair(PF_UNIX, SOCK_STREAM, 0, sockpair));
625
626 /* We'll wait on the procdesc, too, so we can fail faster if it dies. */
627 ATF_REQUIRE((pid = pdfork(&pfd, 0)) != -1);
628
629 if (pid == 0) {
630 /* First child sets up the jail. */
631 sock = sockpair[SP_CHILD];
632 close(sockpair[SP_PARENT]);
633
634 _exit(jail_attach_disjoint_newjail(sock));
635 }
636
637 close(sockpair[SP_CHILD]);
638 sock = sockpair[SP_PARENT];
639
640 ATF_REQUIRE((jid = wait_jail(sock, pfd)) > 0);
641
642 /*
643 * This process will be clamped down to the first cpu, while the jail
644 * will simply have the first CPU removed to make it a completely
645 * disjoint operation.
646 */
647 CPU_ZERO(&smask);
648 CPU_SET(fcpu, &smask);
649 CPU_CLR(fcpu, &jmask);
650
651 /*
652 * We'll test with the first and second cpu set as well. Only the
653 * second cpu should be used.
654 */
655 scpu = CPU_FFS(&jmask) - 1;
656
657 ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_ROOT, CPU_WHICH_JAIL,
658 jid, sizeof(jmask), &jmask));
659 ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
660 setid, sizeof(smask), &smask));
661
662 try_attach(jid, &jmask);
663
664 CPU_SET(scpu, &smask);
665 ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
666 setid, sizeof(smask), &smask));
667
668 CPU_CLR(fcpu, &smask);
669 try_attach(jid, &smask);
670 }
671
672 struct nproc_info {
673 long nproc_init;
674 long nproc_final;
675 long nproc_global;
676 };
677
678 ATF_TC(jail_nproc);
ATF_TC_HEAD(jail_nproc,tc)679 ATF_TC_HEAD(jail_nproc, tc)
680 {
681 atf_tc_set_md_var(tc, "descr",
682 "Test that _SC_PROCESSORS_ONLN reflects jail cpuset constraints");
683 }
ATF_TC_BODY(jail_nproc,tc)684 ATF_TC_BODY(jail_nproc, tc)
685 {
686 cpuset_t jmask;
687 struct nproc_info ninfo = { };
688 int sockpair[2];
689 cpusetid_t setid;
690 ssize_t readsz;
691 pid_t pid;
692 int fcpu, error, pfd, sock;
693 char okb = 0x7f, rcvb;
694
695 skip_ltncpu_root(2, &jmask);
696 fcpu = CPU_FFS(&jmask) - 1;
697
698 /*
699 * Just adjusting our affinity should not affect the number of
700 * processors considered online- we want to be sure that it's only
701 * adjusted if our jail's root set is.
702 */
703 CPU_CLR(fcpu, &jmask);
704 error = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,
705 sizeof(jmask), &jmask);
706 ATF_REQUIRE_EQ(0, error);
707 ATF_REQUIRE(sysconf(_SC_NPROCESSORS_ONLN) > CPU_COUNT(&jmask));
708
709 ATF_REQUIRE_EQ(0, socketpair(PF_UNIX, SOCK_STREAM, 0, sockpair));
710
711 /* We'll wait on the procdesc, too, so we can fail faster if it dies. */
712 ATF_REQUIRE((pid = pdfork(&pfd, 0)) != -1);
713
714 if (pid == 0) {
715 /* First child sets up the jail. */
716 sock = sockpair[SP_CHILD];
717 close(sockpair[SP_PARENT]);
718
719 error = create_jail();
720 if (error != 0)
721 _exit(error);
722
723 ninfo.nproc_init = sysconf(_SC_NPROCESSORS_ONLN);
724
725 /* Signal the parent that we're jailed. */
726 readsz = write(sock, &okb, sizeof(okb));
727 assert(readsz == sizeof(okb));
728
729 /* Wait for parent to adjust our mask and signal OK. */
730 readsz = read(sock, &rcvb, sizeof(rcvb));
731 assert(readsz == sizeof(rcvb));
732 assert(rcvb == okb);
733
734 ninfo.nproc_final = sysconf(_SC_NPROCESSORS_ONLN);
735 ninfo.nproc_global = sysconf(_SC_NPROCESSORS_CONF);
736 readsz = write(sock, &ninfo, sizeof(ninfo));
737 assert(readsz == sizeof(ninfo));
738
739 _exit(0);
740 }
741
742 close(sockpair[SP_CHILD]);
743 sock = sockpair[SP_PARENT];
744
745 /* Wait for signal that they are jailed. */
746 readsz = read(sock, &rcvb, sizeof(rcvb));
747 assert(readsz == sizeof(rcvb));
748 assert(rcvb == okb);
749
750 /* Grab the cpuset id and adjust it. */
751 error = cpuset_getid(CPU_LEVEL_ROOT, CPU_WHICH_PID, pid, &setid);
752 ATF_REQUIRE_EQ(0, error);
753 error = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_CPUSET,
754 setid, sizeof(jmask), &jmask);
755 ATF_REQUIRE_EQ(0, error);
756
757 /* Signal OK to proceed. */
758 readsz = write(sock, &okb, sizeof(okb));
759 ATF_REQUIRE_EQ(sizeof(okb), readsz);
760
761 /* Grab our final nproc info. */
762 readsz = read(sock, &ninfo, sizeof(ninfo));
763 ATF_REQUIRE_EQ(sizeof(ninfo), readsz);
764
765 /*
766 * We set our own affinity to jmask, which is derived from *our* root
767 * set, at the beginning of the test. The jail would inherit from this
768 * set, so we just re-use that mask here to confirm that
769 * _SC_NPROCESSORS_ONLN did actually drop in response to us limiting the
770 * jail, and that its _SC_NPROCESSORS_CONF did not.
771 */
772 ATF_REQUIRE_EQ(CPU_COUNT(&jmask) + 1, ninfo.nproc_init);
773 ATF_REQUIRE_EQ(CPU_COUNT(&jmask) + 1, ninfo.nproc_global);
774 ATF_REQUIRE_EQ(CPU_COUNT(&jmask), ninfo.nproc_final);
775 }
776
777 ATF_TC(badparent);
ATF_TC_HEAD(badparent,tc)778 ATF_TC_HEAD(badparent, tc)
779 {
780 atf_tc_set_md_var(tc, "descr",
781 "Test parent assignment when assigning a new cpuset.");
782 }
ATF_TC_BODY(badparent,tc)783 ATF_TC_BODY(badparent, tc)
784 {
785 cpuset_t mask;
786 cpusetid_t finalsetid, origsetid, setid;
787
788 /* Need to mask off at least one CPU. */
789 skip_ltncpu(2, &mask);
790
791 ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
792 &origsetid));
793
794 ATF_REQUIRE_EQ(0, cpuset(&setid));
795
796 /*
797 * Mask off the first CPU, then we'll reparent ourselves to our original
798 * set.
799 */
800 CPU_CLR(CPU_FFS(&mask) - 1, &mask);
801 ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
802 -1, sizeof(mask), &mask));
803
804 ATF_REQUIRE_EQ(0, cpuset_setid(CPU_WHICH_PID, -1, origsetid));
805 ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
806 &finalsetid));
807
808 ATF_REQUIRE_EQ(finalsetid, origsetid);
809 }
810
ATF_TP_ADD_TCS(tp)811 ATF_TP_ADD_TCS(tp)
812 {
813
814 ATF_TP_ADD_TC(tp, newset);
815 ATF_TP_ADD_TC(tp, transient);
816 ATF_TP_ADD_TC(tp, deadlk);
817 ATF_TP_ADD_TC(tp, jail_attach_newbase);
818 ATF_TP_ADD_TC(tp, jail_attach_newbase_plain);
819 ATF_TP_ADD_TC(tp, jail_attach_prevbase);
820 ATF_TP_ADD_TC(tp, jail_attach_plain);
821 ATF_TP_ADD_TC(tp, jail_attach_disjoint);
822 ATF_TP_ADD_TC(tp, jail_nproc);
823 ATF_TP_ADD_TC(tp, badparent);
824 return (atf_no_error());
825 }
826