0001
0002 #include <stdbool.h>
0003 #include <linux/limits.h>
0004 #include <sys/ptrace.h>
0005 #include <sys/types.h>
0006 #include <sys/mman.h>
0007 #include <unistd.h>
0008 #include <stdio.h>
0009 #include <errno.h>
0010 #include <stdlib.h>
0011 #include <string.h>
0012 #include <sys/wait.h>
0013
0014 #include "../kselftest.h"
0015 #include "cgroup_util.h"
0016
0017 #define DEBUG
0018 #ifdef DEBUG
0019 #define debug(args...) fprintf(stderr, args)
0020 #else
0021 #define debug(args...)
0022 #endif
0023
0024
0025
0026
0027 static int cg_check_frozen(const char *cgroup, bool frozen)
0028 {
0029 if (frozen) {
0030 if (cg_read_strstr(cgroup, "cgroup.events", "frozen 1") != 0) {
0031 debug("Cgroup %s isn't frozen\n", cgroup);
0032 return -1;
0033 }
0034 } else {
0035
0036
0037
0038 if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) {
0039 debug("Cgroup %s is frozen\n", cgroup);
0040 return -1;
0041 }
0042 }
0043
0044 return 0;
0045 }
0046
0047
0048
0049
0050 static int cg_freeze_nowait(const char *cgroup, bool freeze)
0051 {
0052 return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");
0053 }
0054
0055
0056
0057
0058
0059 static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid,
0060 bool frozen)
0061 {
0062 int fd, ret = -1;
0063 int attempts;
0064
0065 fd = cg_prepare_for_wait(cgroup);
0066 if (fd < 0)
0067 return fd;
0068
0069 ret = cg_enter(cgroup, pid);
0070 if (ret)
0071 goto out;
0072
0073 for (attempts = 0; attempts < 10; attempts++) {
0074 ret = cg_wait_for(fd);
0075 if (ret)
0076 break;
0077
0078 ret = cg_check_frozen(cgroup, frozen);
0079 if (ret)
0080 continue;
0081 }
0082
0083 out:
0084 close(fd);
0085 return ret;
0086 }
0087
0088
0089
0090
0091
0092
0093 static int cg_freeze_wait(const char *cgroup, bool freeze)
0094 {
0095 int fd, ret = -1;
0096
0097 fd = cg_prepare_for_wait(cgroup);
0098 if (fd < 0)
0099 return fd;
0100
0101 ret = cg_freeze_nowait(cgroup, freeze);
0102 if (ret) {
0103 debug("Error: cg_freeze_nowait() failed\n");
0104 goto out;
0105 }
0106
0107 ret = cg_wait_for(fd);
0108 if (ret)
0109 goto out;
0110
0111 ret = cg_check_frozen(cgroup, freeze);
0112 out:
0113 close(fd);
0114 return ret;
0115 }
0116
0117
0118
0119
0120
0121 static int child_fn(const char *cgroup, void *arg)
0122 {
0123 int ppid = getppid();
0124
0125 while (getppid() == ppid)
0126 usleep(1000);
0127
0128 return getppid() == ppid;
0129 }
0130
0131
0132
0133
0134
0135
0136 static int test_cgfreezer_simple(const char *root)
0137 {
0138 int ret = KSFT_FAIL;
0139 char *cgroup = NULL;
0140 int i;
0141
0142 cgroup = cg_name(root, "cg_test_simple");
0143 if (!cgroup)
0144 goto cleanup;
0145
0146 if (cg_create(cgroup))
0147 goto cleanup;
0148
0149 for (i = 0; i < 100; i++)
0150 cg_run_nowait(cgroup, child_fn, NULL);
0151
0152 if (cg_wait_for_proc_count(cgroup, 100))
0153 goto cleanup;
0154
0155 if (cg_check_frozen(cgroup, false))
0156 goto cleanup;
0157
0158 if (cg_freeze_wait(cgroup, true))
0159 goto cleanup;
0160
0161 if (cg_freeze_wait(cgroup, false))
0162 goto cleanup;
0163
0164 ret = KSFT_PASS;
0165
0166 cleanup:
0167 if (cgroup)
0168 cg_destroy(cgroup);
0169 free(cgroup);
0170 return ret;
0171 }
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188 static int test_cgfreezer_tree(const char *root)
0189 {
0190 char *cgroup[10] = {0};
0191 int ret = KSFT_FAIL;
0192 int i;
0193
0194 cgroup[0] = cg_name(root, "cg_test_tree_A");
0195 if (!cgroup[0])
0196 goto cleanup;
0197
0198 cgroup[1] = cg_name(cgroup[0], "B");
0199 if (!cgroup[1])
0200 goto cleanup;
0201
0202 cgroup[2] = cg_name(cgroup[1], "C");
0203 if (!cgroup[2])
0204 goto cleanup;
0205
0206 cgroup[3] = cg_name(cgroup[1], "D");
0207 if (!cgroup[3])
0208 goto cleanup;
0209
0210 cgroup[4] = cg_name(cgroup[0], "E");
0211 if (!cgroup[4])
0212 goto cleanup;
0213
0214 cgroup[5] = cg_name(cgroup[4], "F");
0215 if (!cgroup[5])
0216 goto cleanup;
0217
0218 cgroup[6] = cg_name(cgroup[5], "G");
0219 if (!cgroup[6])
0220 goto cleanup;
0221
0222 cgroup[7] = cg_name(cgroup[6], "H");
0223 if (!cgroup[7])
0224 goto cleanup;
0225
0226 cgroup[8] = cg_name(cgroup[0], "I");
0227 if (!cgroup[8])
0228 goto cleanup;
0229
0230 cgroup[9] = cg_name(cgroup[0], "K");
0231 if (!cgroup[9])
0232 goto cleanup;
0233
0234 for (i = 0; i < 10; i++)
0235 if (cg_create(cgroup[i]))
0236 goto cleanup;
0237
0238 cg_run_nowait(cgroup[2], child_fn, NULL);
0239 cg_run_nowait(cgroup[7], child_fn, NULL);
0240 cg_run_nowait(cgroup[9], child_fn, NULL);
0241 cg_run_nowait(cgroup[9], child_fn, NULL);
0242 cg_run_nowait(cgroup[9], child_fn, NULL);
0243
0244
0245
0246
0247
0248
0249 if (cg_wait_for_proc_count(cgroup[2], 1) ||
0250 cg_wait_for_proc_count(cgroup[7], 1) ||
0251 cg_wait_for_proc_count(cgroup[9], 3))
0252 goto cleanup;
0253
0254
0255
0256
0257 if (cg_freeze_wait(cgroup[1], true))
0258 goto cleanup;
0259
0260
0261
0262
0263 if (cg_freeze_wait(cgroup[5], true))
0264 goto cleanup;
0265
0266
0267
0268
0269 if (cg_freeze_wait(cgroup[6], true))
0270 goto cleanup;
0271
0272
0273
0274
0275 if (cg_check_frozen(cgroup[0], false))
0276 goto cleanup;
0277
0278 if (cg_check_frozen(cgroup[4], false))
0279 goto cleanup;
0280
0281
0282
0283
0284 if (cg_freeze_wait(cgroup[0], true))
0285 goto cleanup;
0286
0287 if (cg_check_frozen(cgroup[1], true))
0288 goto cleanup;
0289
0290 if (cg_check_frozen(cgroup[4], true))
0291 goto cleanup;
0292
0293
0294
0295
0296 if (cg_freeze_nowait(cgroup[1], false))
0297 goto cleanup;
0298
0299 if (cg_freeze_nowait(cgroup[5], false))
0300 goto cleanup;
0301
0302 if (cg_freeze_nowait(cgroup[6], false))
0303 goto cleanup;
0304
0305
0306
0307
0308 if (cg_check_frozen(cgroup[2], true))
0309 goto cleanup;
0310
0311 if (cg_check_frozen(cgroup[7], true))
0312 goto cleanup;
0313
0314
0315
0316
0317 if (cg_freeze_wait(cgroup[0], false))
0318 goto cleanup;
0319
0320 if (cg_check_frozen(cgroup[2], false))
0321 goto cleanup;
0322
0323 if (cg_check_frozen(cgroup[9], false))
0324 goto cleanup;
0325
0326 ret = KSFT_PASS;
0327
0328 cleanup:
0329 for (i = 9; i >= 0 && cgroup[i]; i--) {
0330 cg_destroy(cgroup[i]);
0331 free(cgroup[i]);
0332 }
0333
0334 return ret;
0335 }
0336
0337
0338
0339
0340 static int forkbomb_fn(const char *cgroup, void *arg)
0341 {
0342 int ppid;
0343
0344 fork();
0345 fork();
0346
0347 ppid = getppid();
0348
0349 while (getppid() == ppid)
0350 usleep(1000);
0351
0352 return getppid() == ppid;
0353 }
0354
0355
0356
0357
0358
0359
0360 static int test_cgfreezer_forkbomb(const char *root)
0361 {
0362 int ret = KSFT_FAIL;
0363 char *cgroup = NULL;
0364
0365 cgroup = cg_name(root, "cg_forkbomb_test");
0366 if (!cgroup)
0367 goto cleanup;
0368
0369 if (cg_create(cgroup))
0370 goto cleanup;
0371
0372 cg_run_nowait(cgroup, forkbomb_fn, NULL);
0373
0374 usleep(100000);
0375
0376 if (cg_freeze_wait(cgroup, true))
0377 goto cleanup;
0378
0379 if (cg_killall(cgroup))
0380 goto cleanup;
0381
0382 if (cg_wait_for_proc_count(cgroup, 0))
0383 goto cleanup;
0384
0385 ret = KSFT_PASS;
0386
0387 cleanup:
0388 if (cgroup)
0389 cg_destroy(cgroup);
0390 free(cgroup);
0391 return ret;
0392 }
0393
0394
0395
0396
0397
0398
0399 static int test_cgfreezer_mkdir(const char *root)
0400 {
0401 int ret = KSFT_FAIL;
0402 char *parent, *child = NULL;
0403 int pid;
0404
0405 parent = cg_name(root, "cg_test_mkdir_A");
0406 if (!parent)
0407 goto cleanup;
0408
0409 child = cg_name(parent, "cg_test_mkdir_B");
0410 if (!child)
0411 goto cleanup;
0412
0413 if (cg_create(parent))
0414 goto cleanup;
0415
0416 if (cg_freeze_wait(parent, true))
0417 goto cleanup;
0418
0419 if (cg_create(child))
0420 goto cleanup;
0421
0422 pid = cg_run_nowait(child, child_fn, NULL);
0423 if (pid < 0)
0424 goto cleanup;
0425
0426 if (cg_wait_for_proc_count(child, 1))
0427 goto cleanup;
0428
0429 if (cg_check_frozen(child, true))
0430 goto cleanup;
0431
0432 if (cg_check_frozen(parent, true))
0433 goto cleanup;
0434
0435 ret = KSFT_PASS;
0436
0437 cleanup:
0438 if (child)
0439 cg_destroy(child);
0440 free(child);
0441 if (parent)
0442 cg_destroy(parent);
0443 free(parent);
0444 return ret;
0445 }
0446
0447
0448
0449
0450
0451
0452
0453 static int test_cgfreezer_rmdir(const char *root)
0454 {
0455 int ret = KSFT_FAIL;
0456 char *parent, *child = NULL;
0457
0458 parent = cg_name(root, "cg_test_rmdir_A");
0459 if (!parent)
0460 goto cleanup;
0461
0462 child = cg_name(parent, "cg_test_rmdir_B");
0463 if (!child)
0464 goto cleanup;
0465
0466 if (cg_create(parent))
0467 goto cleanup;
0468
0469 if (cg_create(child))
0470 goto cleanup;
0471
0472 if (cg_freeze_wait(parent, true))
0473 goto cleanup;
0474
0475 if (cg_destroy(child))
0476 goto cleanup;
0477
0478 if (cg_check_frozen(parent, true))
0479 goto cleanup;
0480
0481 if (cg_create(child))
0482 goto cleanup;
0483
0484 if (cg_check_frozen(child, true))
0485 goto cleanup;
0486
0487 ret = KSFT_PASS;
0488
0489 cleanup:
0490 if (child)
0491 cg_destroy(child);
0492 free(child);
0493 if (parent)
0494 cg_destroy(parent);
0495 free(parent);
0496 return ret;
0497 }
0498
0499
0500
0501
0502
0503
0504
0505
0506
0507
0508 static int test_cgfreezer_migrate(const char *root)
0509 {
0510 int ret = KSFT_FAIL;
0511 char *cgroup[2] = {0};
0512 int pid;
0513
0514 cgroup[0] = cg_name(root, "cg_test_migrate_A");
0515 if (!cgroup[0])
0516 goto cleanup;
0517
0518 cgroup[1] = cg_name(root, "cg_test_migrate_B");
0519 if (!cgroup[1])
0520 goto cleanup;
0521
0522 if (cg_create(cgroup[0]))
0523 goto cleanup;
0524
0525 if (cg_create(cgroup[1]))
0526 goto cleanup;
0527
0528 pid = cg_run_nowait(cgroup[0], child_fn, NULL);
0529 if (pid < 0)
0530 goto cleanup;
0531
0532 if (cg_wait_for_proc_count(cgroup[0], 1))
0533 goto cleanup;
0534
0535
0536
0537
0538 if (cg_freeze_wait(cgroup[1], true))
0539 goto cleanup;
0540
0541 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
0542 goto cleanup;
0543
0544 if (cg_check_frozen(cgroup[0], false))
0545 goto cleanup;
0546
0547
0548
0549
0550 if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))
0551 goto cleanup;
0552
0553 if (cg_check_frozen(cgroup[1], true))
0554 goto cleanup;
0555
0556
0557
0558
0559 if (cg_freeze_wait(cgroup[0], true))
0560 goto cleanup;
0561
0562 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
0563 goto cleanup;
0564
0565 if (cg_check_frozen(cgroup[0], true))
0566 goto cleanup;
0567
0568 ret = KSFT_PASS;
0569
0570 cleanup:
0571 if (cgroup[0])
0572 cg_destroy(cgroup[0]);
0573 free(cgroup[0]);
0574 if (cgroup[1])
0575 cg_destroy(cgroup[1]);
0576 free(cgroup[1]);
0577 return ret;
0578 }
0579
0580
0581
0582
0583 static int test_cgfreezer_ptrace(const char *root)
0584 {
0585 int ret = KSFT_FAIL;
0586 char *cgroup = NULL;
0587 siginfo_t siginfo;
0588 int pid;
0589
0590 cgroup = cg_name(root, "cg_test_ptrace");
0591 if (!cgroup)
0592 goto cleanup;
0593
0594 if (cg_create(cgroup))
0595 goto cleanup;
0596
0597 pid = cg_run_nowait(cgroup, child_fn, NULL);
0598 if (pid < 0)
0599 goto cleanup;
0600
0601 if (cg_wait_for_proc_count(cgroup, 1))
0602 goto cleanup;
0603
0604 if (cg_freeze_wait(cgroup, true))
0605 goto cleanup;
0606
0607 if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
0608 goto cleanup;
0609
0610 if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
0611 goto cleanup;
0612
0613 waitpid(pid, NULL, 0);
0614
0615
0616
0617
0618
0619 if (cg_check_frozen(cgroup, true))
0620 goto cleanup;
0621
0622 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
0623 goto cleanup;
0624
0625 if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
0626 goto cleanup;
0627
0628 if (cg_check_frozen(cgroup, true))
0629 goto cleanup;
0630
0631 ret = KSFT_PASS;
0632
0633 cleanup:
0634 if (cgroup)
0635 cg_destroy(cgroup);
0636 free(cgroup);
0637 return ret;
0638 }
0639
0640
0641
0642
0643 static int proc_check_stopped(int pid)
0644 {
0645 char buf[PAGE_SIZE];
0646 int len;
0647
0648 len = proc_read_text(pid, 0, "stat", buf, sizeof(buf));
0649 if (len == -1) {
0650 debug("Can't get %d stat\n", pid);
0651 return -1;
0652 }
0653
0654 if (strstr(buf, "(test_freezer) T ") == NULL) {
0655 debug("Process %d in the unexpected state: %s\n", pid, buf);
0656 return -1;
0657 }
0658
0659 return 0;
0660 }
0661
0662
0663
0664
0665 static int test_cgfreezer_stopped(const char *root)
0666 {
0667 int pid, ret = KSFT_FAIL;
0668 char *cgroup = NULL;
0669
0670 cgroup = cg_name(root, "cg_test_stopped");
0671 if (!cgroup)
0672 goto cleanup;
0673
0674 if (cg_create(cgroup))
0675 goto cleanup;
0676
0677 pid = cg_run_nowait(cgroup, child_fn, NULL);
0678
0679 if (cg_wait_for_proc_count(cgroup, 1))
0680 goto cleanup;
0681
0682 if (kill(pid, SIGSTOP))
0683 goto cleanup;
0684
0685 if (cg_check_frozen(cgroup, false))
0686 goto cleanup;
0687
0688 if (cg_freeze_wait(cgroup, true))
0689 goto cleanup;
0690
0691 if (cg_freeze_wait(cgroup, false))
0692 goto cleanup;
0693
0694 if (proc_check_stopped(pid))
0695 goto cleanup;
0696
0697 ret = KSFT_PASS;
0698
0699 cleanup:
0700 if (cgroup)
0701 cg_destroy(cgroup);
0702 free(cgroup);
0703 return ret;
0704 }
0705
0706
0707
0708
0709 static int test_cgfreezer_ptraced(const char *root)
0710 {
0711 int pid, ret = KSFT_FAIL;
0712 char *cgroup = NULL;
0713 siginfo_t siginfo;
0714
0715 cgroup = cg_name(root, "cg_test_ptraced");
0716 if (!cgroup)
0717 goto cleanup;
0718
0719 if (cg_create(cgroup))
0720 goto cleanup;
0721
0722 pid = cg_run_nowait(cgroup, child_fn, NULL);
0723
0724 if (cg_wait_for_proc_count(cgroup, 1))
0725 goto cleanup;
0726
0727 if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
0728 goto cleanup;
0729
0730 if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
0731 goto cleanup;
0732
0733 waitpid(pid, NULL, 0);
0734
0735 if (cg_check_frozen(cgroup, false))
0736 goto cleanup;
0737
0738 if (cg_freeze_wait(cgroup, true))
0739 goto cleanup;
0740
0741
0742
0743
0744
0745 if (cg_freeze_wait(cgroup, false))
0746 goto cleanup;
0747
0748 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
0749 goto cleanup;
0750
0751 if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
0752 goto cleanup;
0753
0754 ret = KSFT_PASS;
0755
0756 cleanup:
0757 if (cgroup)
0758 cg_destroy(cgroup);
0759 free(cgroup);
0760 return ret;
0761 }
0762
0763 static int vfork_fn(const char *cgroup, void *arg)
0764 {
0765 int pid = vfork();
0766
0767 if (pid == 0)
0768 while (true)
0769 sleep(1);
0770
0771 return pid;
0772 }
0773
0774
0775
0776
0777
0778 static int test_cgfreezer_vfork(const char *root)
0779 {
0780 int ret = KSFT_FAIL;
0781 char *cgroup = NULL;
0782
0783 cgroup = cg_name(root, "cg_test_vfork");
0784 if (!cgroup)
0785 goto cleanup;
0786
0787 if (cg_create(cgroup))
0788 goto cleanup;
0789
0790 cg_run_nowait(cgroup, vfork_fn, NULL);
0791
0792 if (cg_wait_for_proc_count(cgroup, 2))
0793 goto cleanup;
0794
0795 if (cg_freeze_wait(cgroup, true))
0796 goto cleanup;
0797
0798 ret = KSFT_PASS;
0799
0800 cleanup:
0801 if (cgroup)
0802 cg_destroy(cgroup);
0803 free(cgroup);
0804 return ret;
0805 }
0806
0807 #define T(x) { x, #x }
0808 struct cgfreezer_test {
0809 int (*fn)(const char *root);
0810 const char *name;
0811 } tests[] = {
0812 T(test_cgfreezer_simple),
0813 T(test_cgfreezer_tree),
0814 T(test_cgfreezer_forkbomb),
0815 T(test_cgfreezer_mkdir),
0816 T(test_cgfreezer_rmdir),
0817 T(test_cgfreezer_migrate),
0818 T(test_cgfreezer_ptrace),
0819 T(test_cgfreezer_stopped),
0820 T(test_cgfreezer_ptraced),
0821 T(test_cgfreezer_vfork),
0822 };
0823 #undef T
0824
0825 int main(int argc, char *argv[])
0826 {
0827 char root[PATH_MAX];
0828 int i, ret = EXIT_SUCCESS;
0829
0830 if (cg_find_unified_root(root, sizeof(root)))
0831 ksft_exit_skip("cgroup v2 isn't mounted\n");
0832 for (i = 0; i < ARRAY_SIZE(tests); i++) {
0833 switch (tests[i].fn(root)) {
0834 case KSFT_PASS:
0835 ksft_test_result_pass("%s\n", tests[i].name);
0836 break;
0837 case KSFT_SKIP:
0838 ksft_test_result_skip("%s\n", tests[i].name);
0839 break;
0840 default:
0841 ret = EXIT_FAILURE;
0842 ksft_test_result_fail("%s\n", tests[i].name);
0843 break;
0844 }
0845 }
0846
0847 return ret;
0848 }