.. | .. |
---|
1 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
---|
2 | 2 | |
---|
| 3 | +#define _GNU_SOURCE |
---|
3 | 4 | #include <linux/limits.h> |
---|
| 5 | +#include <linux/sched.h> |
---|
4 | 6 | #include <sys/types.h> |
---|
| 7 | +#include <sys/mman.h> |
---|
| 8 | +#include <sys/wait.h> |
---|
5 | 9 | #include <unistd.h> |
---|
| 10 | +#include <fcntl.h> |
---|
| 11 | +#include <sched.h> |
---|
6 | 12 | #include <stdio.h> |
---|
7 | 13 | #include <errno.h> |
---|
| 14 | +#include <signal.h> |
---|
| 15 | +#include <string.h> |
---|
| 16 | +#include <pthread.h> |
---|
8 | 17 | |
---|
9 | 18 | #include "../kselftest.h" |
---|
10 | 19 | #include "cgroup_util.h" |
---|
| 20 | + |
---|
| 21 | +static int touch_anon(char *buf, size_t size) |
---|
| 22 | +{ |
---|
| 23 | + int fd; |
---|
| 24 | + char *pos = buf; |
---|
| 25 | + |
---|
| 26 | + fd = open("/dev/urandom", O_RDONLY); |
---|
| 27 | + if (fd < 0) |
---|
| 28 | + return -1; |
---|
| 29 | + |
---|
| 30 | + while (size > 0) { |
---|
| 31 | + ssize_t ret = read(fd, pos, size); |
---|
| 32 | + |
---|
| 33 | + if (ret < 0) { |
---|
| 34 | + if (errno != EINTR) { |
---|
| 35 | + close(fd); |
---|
| 36 | + return -1; |
---|
| 37 | + } |
---|
| 38 | + } else { |
---|
| 39 | + pos += ret; |
---|
| 40 | + size -= ret; |
---|
| 41 | + } |
---|
| 42 | + } |
---|
| 43 | + close(fd); |
---|
| 44 | + |
---|
| 45 | + return 0; |
---|
| 46 | +} |
---|
| 47 | + |
---|
| 48 | +static int alloc_and_touch_anon_noexit(const char *cgroup, void *arg) |
---|
| 49 | +{ |
---|
| 50 | + int ppid = getppid(); |
---|
| 51 | + size_t size = (size_t)arg; |
---|
| 52 | + void *buf; |
---|
| 53 | + |
---|
| 54 | + buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, |
---|
| 55 | + 0, 0); |
---|
| 56 | + if (buf == MAP_FAILED) |
---|
| 57 | + return -1; |
---|
| 58 | + |
---|
| 59 | + if (touch_anon((char *)buf, size)) { |
---|
| 60 | + munmap(buf, size); |
---|
| 61 | + return -1; |
---|
| 62 | + } |
---|
| 63 | + |
---|
| 64 | + while (getppid() == ppid) |
---|
| 65 | + sleep(1); |
---|
| 66 | + |
---|
| 67 | + munmap(buf, size); |
---|
| 68 | + return 0; |
---|
| 69 | +} |
---|
| 70 | + |
---|
| 71 | +/* |
---|
| 72 | + * Create a child process that allocates and touches 100MB, then waits to be |
---|
| 73 | + * killed. Wait until the child is attached to the cgroup, kill all processes |
---|
| 74 | + * in that cgroup and wait until "cgroup.procs" is empty. At this point try to |
---|
| 75 | + * destroy the empty cgroup. The test helps detect race conditions between |
---|
| 76 | + * dying processes leaving the cgroup and cgroup destruction path. |
---|
| 77 | + */ |
---|
| 78 | +static int test_cgcore_destroy(const char *root) |
---|
| 79 | +{ |
---|
| 80 | + int ret = KSFT_FAIL; |
---|
| 81 | + char *cg_test = NULL; |
---|
| 82 | + int child_pid; |
---|
| 83 | + char buf[PAGE_SIZE]; |
---|
| 84 | + |
---|
| 85 | + cg_test = cg_name(root, "cg_test"); |
---|
| 86 | + |
---|
| 87 | + if (!cg_test) |
---|
| 88 | + goto cleanup; |
---|
| 89 | + |
---|
| 90 | + for (int i = 0; i < 10; i++) { |
---|
| 91 | + if (cg_create(cg_test)) |
---|
| 92 | + goto cleanup; |
---|
| 93 | + |
---|
| 94 | + child_pid = cg_run_nowait(cg_test, alloc_and_touch_anon_noexit, |
---|
| 95 | + (void *) MB(100)); |
---|
| 96 | + |
---|
| 97 | + if (child_pid < 0) |
---|
| 98 | + goto cleanup; |
---|
| 99 | + |
---|
| 100 | + /* wait for the child to enter cgroup */ |
---|
| 101 | + if (cg_wait_for_proc_count(cg_test, 1)) |
---|
| 102 | + goto cleanup; |
---|
| 103 | + |
---|
| 104 | + if (cg_killall(cg_test)) |
---|
| 105 | + goto cleanup; |
---|
| 106 | + |
---|
| 107 | + /* wait for cgroup to be empty */ |
---|
| 108 | + while (1) { |
---|
| 109 | + if (cg_read(cg_test, "cgroup.procs", buf, sizeof(buf))) |
---|
| 110 | + goto cleanup; |
---|
| 111 | + if (buf[0] == '\0') |
---|
| 112 | + break; |
---|
| 113 | + usleep(1000); |
---|
| 114 | + } |
---|
| 115 | + |
---|
| 116 | + if (rmdir(cg_test)) |
---|
| 117 | + goto cleanup; |
---|
| 118 | + |
---|
| 119 | + if (waitpid(child_pid, NULL, 0) < 0) |
---|
| 120 | + goto cleanup; |
---|
| 121 | + } |
---|
| 122 | + ret = KSFT_PASS; |
---|
| 123 | +cleanup: |
---|
| 124 | + if (cg_test) |
---|
| 125 | + cg_destroy(cg_test); |
---|
| 126 | + free(cg_test); |
---|
| 127 | + return ret; |
---|
| 128 | +} |
---|
11 | 129 | |
---|
12 | 130 | /* |
---|
13 | 131 | * A(0) - B(0) - C(1) |
---|
.. | .. |
---|
22 | 140 | static int test_cgcore_populated(const char *root) |
---|
23 | 141 | { |
---|
24 | 142 | int ret = KSFT_FAIL; |
---|
| 143 | + int err; |
---|
25 | 144 | char *cg_test_a = NULL, *cg_test_b = NULL; |
---|
26 | 145 | char *cg_test_c = NULL, *cg_test_d = NULL; |
---|
| 146 | + int cgroup_fd = -EBADF; |
---|
| 147 | + pid_t pid; |
---|
27 | 148 | |
---|
28 | 149 | cg_test_a = cg_name(root, "cg_test_a"); |
---|
29 | 150 | cg_test_b = cg_name(root, "cg_test_a/cg_test_b"); |
---|
.. | .. |
---|
75 | 196 | if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n")) |
---|
76 | 197 | goto cleanup; |
---|
77 | 198 | |
---|
| 199 | + /* Test that we can directly clone into a new cgroup. */ |
---|
| 200 | + cgroup_fd = dirfd_open_opath(cg_test_d); |
---|
| 201 | + if (cgroup_fd < 0) |
---|
| 202 | + goto cleanup; |
---|
| 203 | + |
---|
| 204 | + pid = clone_into_cgroup(cgroup_fd); |
---|
| 205 | + if (pid < 0) { |
---|
| 206 | + if (errno == ENOSYS) |
---|
| 207 | + goto cleanup_pass; |
---|
| 208 | + goto cleanup; |
---|
| 209 | + } |
---|
| 210 | + |
---|
| 211 | + if (pid == 0) { |
---|
| 212 | + if (raise(SIGSTOP)) |
---|
| 213 | + exit(EXIT_FAILURE); |
---|
| 214 | + exit(EXIT_SUCCESS); |
---|
| 215 | + } |
---|
| 216 | + |
---|
| 217 | + err = cg_read_strcmp(cg_test_d, "cgroup.events", "populated 1\n"); |
---|
| 218 | + |
---|
| 219 | + (void)clone_reap(pid, WSTOPPED); |
---|
| 220 | + (void)kill(pid, SIGCONT); |
---|
| 221 | + (void)clone_reap(pid, WEXITED); |
---|
| 222 | + |
---|
| 223 | + if (err) |
---|
| 224 | + goto cleanup; |
---|
| 225 | + |
---|
| 226 | + if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n")) |
---|
| 227 | + goto cleanup; |
---|
| 228 | + |
---|
| 229 | + /* Remove cgroup. */ |
---|
| 230 | + if (cg_test_d) { |
---|
| 231 | + cg_destroy(cg_test_d); |
---|
| 232 | + free(cg_test_d); |
---|
| 233 | + cg_test_d = NULL; |
---|
| 234 | + } |
---|
| 235 | + |
---|
| 236 | + pid = clone_into_cgroup(cgroup_fd); |
---|
| 237 | + if (pid < 0) |
---|
| 238 | + goto cleanup_pass; |
---|
| 239 | + if (pid == 0) |
---|
| 240 | + exit(EXIT_SUCCESS); |
---|
| 241 | + (void)clone_reap(pid, WEXITED); |
---|
| 242 | + goto cleanup; |
---|
| 243 | + |
---|
| 244 | +cleanup_pass: |
---|
78 | 245 | ret = KSFT_PASS; |
---|
79 | 246 | |
---|
80 | 247 | cleanup: |
---|
.. | .. |
---|
90 | 257 | free(cg_test_c); |
---|
91 | 258 | free(cg_test_b); |
---|
92 | 259 | free(cg_test_a); |
---|
| 260 | + if (cgroup_fd >= 0) |
---|
| 261 | + close(cgroup_fd); |
---|
93 | 262 | return ret; |
---|
94 | 263 | } |
---|
95 | 264 | |
---|
.. | .. |
---|
133 | 302 | if (errno != EOPNOTSUPP) |
---|
134 | 303 | goto cleanup; |
---|
135 | 304 | |
---|
| 305 | + if (!clone_into_cgroup_run_wait(child)) |
---|
| 306 | + goto cleanup; |
---|
| 307 | + |
---|
| 308 | + if (errno == ENOSYS) |
---|
| 309 | + goto cleanup_pass; |
---|
| 310 | + |
---|
| 311 | + if (errno != EOPNOTSUPP) |
---|
| 312 | + goto cleanup; |
---|
| 313 | + |
---|
| 314 | +cleanup_pass: |
---|
136 | 315 | ret = KSFT_PASS; |
---|
137 | 316 | |
---|
138 | 317 | cleanup: |
---|
.. | .. |
---|
342 | 521 | if (!cg_enter_current(parent)) |
---|
343 | 522 | goto cleanup; |
---|
344 | 523 | |
---|
| 524 | + if (!clone_into_cgroup_run_wait(parent)) |
---|
| 525 | + goto cleanup; |
---|
| 526 | + |
---|
345 | 527 | ret = KSFT_PASS; |
---|
346 | 528 | |
---|
347 | 529 | cleanup: |
---|
.. | .. |
---|
351 | 533 | cg_destroy(parent); |
---|
352 | 534 | free(child); |
---|
353 | 535 | free(parent); |
---|
| 536 | + return ret; |
---|
| 537 | +} |
---|
| 538 | + |
---|
| 539 | +static void *dummy_thread_fn(void *arg) |
---|
| 540 | +{ |
---|
| 541 | + return (void *)(size_t)pause(); |
---|
| 542 | +} |
---|
| 543 | + |
---|
| 544 | +/* |
---|
| 545 | + * Test threadgroup migration. |
---|
| 546 | + * All threads of a process are migrated together. |
---|
| 547 | + */ |
---|
| 548 | +static int test_cgcore_proc_migration(const char *root) |
---|
| 549 | +{ |
---|
| 550 | + int ret = KSFT_FAIL; |
---|
| 551 | + int t, c_threads = 0, n_threads = 13; |
---|
| 552 | + char *src = NULL, *dst = NULL; |
---|
| 553 | + pthread_t threads[n_threads]; |
---|
| 554 | + |
---|
| 555 | + src = cg_name(root, "cg_src"); |
---|
| 556 | + dst = cg_name(root, "cg_dst"); |
---|
| 557 | + if (!src || !dst) |
---|
| 558 | + goto cleanup; |
---|
| 559 | + |
---|
| 560 | + if (cg_create(src)) |
---|
| 561 | + goto cleanup; |
---|
| 562 | + if (cg_create(dst)) |
---|
| 563 | + goto cleanup; |
---|
| 564 | + |
---|
| 565 | + if (cg_enter_current(src)) |
---|
| 566 | + goto cleanup; |
---|
| 567 | + |
---|
| 568 | + for (c_threads = 0; c_threads < n_threads; ++c_threads) { |
---|
| 569 | + if (pthread_create(&threads[c_threads], NULL, dummy_thread_fn, NULL)) |
---|
| 570 | + goto cleanup; |
---|
| 571 | + } |
---|
| 572 | + |
---|
| 573 | + cg_enter_current(dst); |
---|
| 574 | + if (cg_read_lc(dst, "cgroup.threads") != n_threads + 1) |
---|
| 575 | + goto cleanup; |
---|
| 576 | + |
---|
| 577 | + ret = KSFT_PASS; |
---|
| 578 | + |
---|
| 579 | +cleanup: |
---|
| 580 | + for (t = 0; t < c_threads; ++t) { |
---|
| 581 | + pthread_cancel(threads[t]); |
---|
| 582 | + } |
---|
| 583 | + |
---|
| 584 | + for (t = 0; t < c_threads; ++t) { |
---|
| 585 | + pthread_join(threads[t], NULL); |
---|
| 586 | + } |
---|
| 587 | + |
---|
| 588 | + cg_enter_current(root); |
---|
| 589 | + |
---|
| 590 | + if (dst) |
---|
| 591 | + cg_destroy(dst); |
---|
| 592 | + if (src) |
---|
| 593 | + cg_destroy(src); |
---|
| 594 | + free(dst); |
---|
| 595 | + free(src); |
---|
| 596 | + return ret; |
---|
| 597 | +} |
---|
| 598 | + |
---|
| 599 | +static void *migrating_thread_fn(void *arg) |
---|
| 600 | +{ |
---|
| 601 | + int g, i, n_iterations = 1000; |
---|
| 602 | + char **grps = arg; |
---|
| 603 | + char lines[3][PATH_MAX]; |
---|
| 604 | + |
---|
| 605 | + for (g = 1; g < 3; ++g) |
---|
| 606 | + snprintf(lines[g], sizeof(lines[g]), "0::%s", grps[g] + strlen(grps[0])); |
---|
| 607 | + |
---|
| 608 | + for (i = 0; i < n_iterations; ++i) { |
---|
| 609 | + cg_enter_current_thread(grps[(i % 2) + 1]); |
---|
| 610 | + |
---|
| 611 | + if (proc_read_strstr(0, 1, "cgroup", lines[(i % 2) + 1])) |
---|
| 612 | + return (void *)-1; |
---|
| 613 | + } |
---|
| 614 | + return NULL; |
---|
| 615 | +} |
---|
| 616 | + |
---|
| 617 | +/* |
---|
| 618 | + * Test single thread migration. |
---|
| 619 | + * Threaded cgroups allow successful migration of a thread. |
---|
| 620 | + */ |
---|
| 621 | +static int test_cgcore_thread_migration(const char *root) |
---|
| 622 | +{ |
---|
| 623 | + int ret = KSFT_FAIL; |
---|
| 624 | + char *dom = NULL; |
---|
| 625 | + char line[PATH_MAX]; |
---|
| 626 | + char *grps[3] = { (char *)root, NULL, NULL }; |
---|
| 627 | + pthread_t thr; |
---|
| 628 | + void *retval; |
---|
| 629 | + |
---|
| 630 | + dom = cg_name(root, "cg_dom"); |
---|
| 631 | + grps[1] = cg_name(root, "cg_dom/cg_src"); |
---|
| 632 | + grps[2] = cg_name(root, "cg_dom/cg_dst"); |
---|
| 633 | + if (!grps[1] || !grps[2] || !dom) |
---|
| 634 | + goto cleanup; |
---|
| 635 | + |
---|
| 636 | + if (cg_create(dom)) |
---|
| 637 | + goto cleanup; |
---|
| 638 | + if (cg_create(grps[1])) |
---|
| 639 | + goto cleanup; |
---|
| 640 | + if (cg_create(grps[2])) |
---|
| 641 | + goto cleanup; |
---|
| 642 | + |
---|
| 643 | + if (cg_write(grps[1], "cgroup.type", "threaded")) |
---|
| 644 | + goto cleanup; |
---|
| 645 | + if (cg_write(grps[2], "cgroup.type", "threaded")) |
---|
| 646 | + goto cleanup; |
---|
| 647 | + |
---|
| 648 | + if (cg_enter_current(grps[1])) |
---|
| 649 | + goto cleanup; |
---|
| 650 | + |
---|
| 651 | + if (pthread_create(&thr, NULL, migrating_thread_fn, grps)) |
---|
| 652 | + goto cleanup; |
---|
| 653 | + |
---|
| 654 | + if (pthread_join(thr, &retval)) |
---|
| 655 | + goto cleanup; |
---|
| 656 | + |
---|
| 657 | + if (retval) |
---|
| 658 | + goto cleanup; |
---|
| 659 | + |
---|
| 660 | + snprintf(line, sizeof(line), "0::%s", grps[1] + strlen(grps[0])); |
---|
| 661 | + if (proc_read_strstr(0, 1, "cgroup", line)) |
---|
| 662 | + goto cleanup; |
---|
| 663 | + |
---|
| 664 | + ret = KSFT_PASS; |
---|
| 665 | + |
---|
| 666 | +cleanup: |
---|
| 667 | + cg_enter_current(root); |
---|
| 668 | + if (grps[2]) |
---|
| 669 | + cg_destroy(grps[2]); |
---|
| 670 | + if (grps[1]) |
---|
| 671 | + cg_destroy(grps[1]); |
---|
| 672 | + if (dom) |
---|
| 673 | + cg_destroy(dom); |
---|
| 674 | + free(grps[2]); |
---|
| 675 | + free(grps[1]); |
---|
| 676 | + free(dom); |
---|
| 677 | + return ret; |
---|
| 678 | +} |
---|
| 679 | + |
---|
| 680 | +/* |
---|
| 681 | + * cgroup migration permission check should be performed based on the |
---|
| 682 | + * credentials at the time of open instead of write. |
---|
| 683 | + */ |
---|
| 684 | +static int test_cgcore_lesser_euid_open(const char *root) |
---|
| 685 | +{ |
---|
| 686 | + const uid_t test_euid = 65534; /* usually nobody, any !root is fine */ |
---|
| 687 | + int ret = KSFT_FAIL; |
---|
| 688 | + char *cg_test_a = NULL, *cg_test_b = NULL; |
---|
| 689 | + char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL; |
---|
| 690 | + int cg_test_b_procs_fd = -1; |
---|
| 691 | + uid_t saved_uid; |
---|
| 692 | + |
---|
| 693 | + cg_test_a = cg_name(root, "cg_test_a"); |
---|
| 694 | + cg_test_b = cg_name(root, "cg_test_b"); |
---|
| 695 | + |
---|
| 696 | + if (!cg_test_a || !cg_test_b) |
---|
| 697 | + goto cleanup; |
---|
| 698 | + |
---|
| 699 | + cg_test_a_procs = cg_name(cg_test_a, "cgroup.procs"); |
---|
| 700 | + cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs"); |
---|
| 701 | + |
---|
| 702 | + if (!cg_test_a_procs || !cg_test_b_procs) |
---|
| 703 | + goto cleanup; |
---|
| 704 | + |
---|
| 705 | + if (cg_create(cg_test_a) || cg_create(cg_test_b)) |
---|
| 706 | + goto cleanup; |
---|
| 707 | + |
---|
| 708 | + if (cg_enter_current(cg_test_a)) |
---|
| 709 | + goto cleanup; |
---|
| 710 | + |
---|
| 711 | + if (chown(cg_test_a_procs, test_euid, -1) || |
---|
| 712 | + chown(cg_test_b_procs, test_euid, -1)) |
---|
| 713 | + goto cleanup; |
---|
| 714 | + |
---|
| 715 | + saved_uid = geteuid(); |
---|
| 716 | + if (seteuid(test_euid)) |
---|
| 717 | + goto cleanup; |
---|
| 718 | + |
---|
| 719 | + cg_test_b_procs_fd = open(cg_test_b_procs, O_RDWR); |
---|
| 720 | + |
---|
| 721 | + if (seteuid(saved_uid)) |
---|
| 722 | + goto cleanup; |
---|
| 723 | + |
---|
| 724 | + if (cg_test_b_procs_fd < 0) |
---|
| 725 | + goto cleanup; |
---|
| 726 | + |
---|
| 727 | + if (write(cg_test_b_procs_fd, "0", 1) >= 0 || errno != EACCES) |
---|
| 728 | + goto cleanup; |
---|
| 729 | + |
---|
| 730 | + ret = KSFT_PASS; |
---|
| 731 | + |
---|
| 732 | +cleanup: |
---|
| 733 | + cg_enter_current(root); |
---|
| 734 | + if (cg_test_b_procs_fd >= 0) |
---|
| 735 | + close(cg_test_b_procs_fd); |
---|
| 736 | + if (cg_test_b) |
---|
| 737 | + cg_destroy(cg_test_b); |
---|
| 738 | + if (cg_test_a) |
---|
| 739 | + cg_destroy(cg_test_a); |
---|
| 740 | + free(cg_test_b_procs); |
---|
| 741 | + free(cg_test_a_procs); |
---|
| 742 | + free(cg_test_b); |
---|
| 743 | + free(cg_test_a); |
---|
| 744 | + return ret; |
---|
| 745 | +} |
---|
| 746 | + |
---|
| 747 | +struct lesser_ns_open_thread_arg { |
---|
| 748 | + const char *path; |
---|
| 749 | + int fd; |
---|
| 750 | + int err; |
---|
| 751 | +}; |
---|
| 752 | + |
---|
| 753 | +static int lesser_ns_open_thread_fn(void *arg) |
---|
| 754 | +{ |
---|
| 755 | + struct lesser_ns_open_thread_arg *targ = arg; |
---|
| 756 | + |
---|
| 757 | + targ->fd = open(targ->path, O_RDWR); |
---|
| 758 | + targ->err = errno; |
---|
| 759 | + return 0; |
---|
| 760 | +} |
---|
| 761 | + |
---|
| 762 | +/* |
---|
| 763 | + * cgroup migration permission check should be performed based on the cgroup |
---|
| 764 | + * namespace at the time of open instead of write. |
---|
| 765 | + */ |
---|
| 766 | +static int test_cgcore_lesser_ns_open(const char *root) |
---|
| 767 | +{ |
---|
| 768 | + static char stack[65536]; |
---|
| 769 | + const uid_t test_euid = 65534; /* usually nobody, any !root is fine */ |
---|
| 770 | + int ret = KSFT_FAIL; |
---|
| 771 | + char *cg_test_a = NULL, *cg_test_b = NULL; |
---|
| 772 | + char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL; |
---|
| 773 | + int cg_test_b_procs_fd = -1; |
---|
| 774 | + struct lesser_ns_open_thread_arg targ = { .fd = -1 }; |
---|
| 775 | + pid_t pid; |
---|
| 776 | + int status; |
---|
| 777 | + |
---|
| 778 | + cg_test_a = cg_name(root, "cg_test_a"); |
---|
| 779 | + cg_test_b = cg_name(root, "cg_test_b"); |
---|
| 780 | + |
---|
| 781 | + if (!cg_test_a || !cg_test_b) |
---|
| 782 | + goto cleanup; |
---|
| 783 | + |
---|
| 784 | + cg_test_a_procs = cg_name(cg_test_a, "cgroup.procs"); |
---|
| 785 | + cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs"); |
---|
| 786 | + |
---|
| 787 | + if (!cg_test_a_procs || !cg_test_b_procs) |
---|
| 788 | + goto cleanup; |
---|
| 789 | + |
---|
| 790 | + if (cg_create(cg_test_a) || cg_create(cg_test_b)) |
---|
| 791 | + goto cleanup; |
---|
| 792 | + |
---|
| 793 | + if (cg_enter_current(cg_test_b)) |
---|
| 794 | + goto cleanup; |
---|
| 795 | + |
---|
| 796 | + if (chown(cg_test_a_procs, test_euid, -1) || |
---|
| 797 | + chown(cg_test_b_procs, test_euid, -1)) |
---|
| 798 | + goto cleanup; |
---|
| 799 | + |
---|
| 800 | + targ.path = cg_test_b_procs; |
---|
| 801 | + pid = clone(lesser_ns_open_thread_fn, stack + sizeof(stack), |
---|
| 802 | + CLONE_NEWCGROUP | CLONE_FILES | CLONE_VM | SIGCHLD, |
---|
| 803 | + &targ); |
---|
| 804 | + if (pid < 0) |
---|
| 805 | + goto cleanup; |
---|
| 806 | + |
---|
| 807 | + if (waitpid(pid, &status, 0) < 0) |
---|
| 808 | + goto cleanup; |
---|
| 809 | + |
---|
| 810 | + if (!WIFEXITED(status)) |
---|
| 811 | + goto cleanup; |
---|
| 812 | + |
---|
| 813 | + cg_test_b_procs_fd = targ.fd; |
---|
| 814 | + if (cg_test_b_procs_fd < 0) |
---|
| 815 | + goto cleanup; |
---|
| 816 | + |
---|
| 817 | + if (cg_enter_current(cg_test_a)) |
---|
| 818 | + goto cleanup; |
---|
| 819 | + |
---|
| 820 | + if ((status = write(cg_test_b_procs_fd, "0", 1)) >= 0 || errno != ENOENT) |
---|
| 821 | + goto cleanup; |
---|
| 822 | + |
---|
| 823 | + ret = KSFT_PASS; |
---|
| 824 | + |
---|
| 825 | +cleanup: |
---|
| 826 | + cg_enter_current(root); |
---|
| 827 | + if (cg_test_b_procs_fd >= 0) |
---|
| 828 | + close(cg_test_b_procs_fd); |
---|
| 829 | + if (cg_test_b) |
---|
| 830 | + cg_destroy(cg_test_b); |
---|
| 831 | + if (cg_test_a) |
---|
| 832 | + cg_destroy(cg_test_a); |
---|
| 833 | + free(cg_test_b_procs); |
---|
| 834 | + free(cg_test_a_procs); |
---|
| 835 | + free(cg_test_b); |
---|
| 836 | + free(cg_test_a); |
---|
354 | 837 | return ret; |
---|
355 | 838 | } |
---|
356 | 839 | |
---|
.. | .. |
---|
366 | 849 | T(test_cgcore_parent_becomes_threaded), |
---|
367 | 850 | T(test_cgcore_invalid_domain), |
---|
368 | 851 | T(test_cgcore_populated), |
---|
| 852 | + T(test_cgcore_proc_migration), |
---|
| 853 | + T(test_cgcore_thread_migration), |
---|
| 854 | + T(test_cgcore_destroy), |
---|
| 855 | + T(test_cgcore_lesser_euid_open), |
---|
| 856 | + T(test_cgcore_lesser_ns_open), |
---|
369 | 857 | }; |
---|
370 | 858 | #undef T |
---|
371 | 859 | |
---|