1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2022 FUJITSU LIMITED. All rights reserved.
4 * Author: Yang Xu <[email protected]>
5 */
6
7 /*\
8 * [Description]
9 *
10 * When a task is writing to an fd opened by a different task, the perm check
11 * should use the cgroup namespace of the latter task.
12 *
13 * It is copy from kernel selftests cgroup test_core test_cgcore_lesser_ns_open
14 * subcase. Note that this case only runs on cgroup2 as cgroup1 doesn't have
15 * namespace support.
16 *
17 * It is a regression test for
18 *
19 * commit e57457641613fef0d147ede8bd6a3047df588b95
20 * Author: Tejun Heo <[email protected]>
21 * Date: Thu Jan 6 11:02:29 2022 -1000
22 *
23 * cgroup: Use open-time cgroup namespace for process migration perm checks
24 */
25
26 #define _GNU_SOURCE
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <pwd.h>
31 #include "tst_test.h"
32 #include "tst_safe_file_at.h"
33 #include "lapi/sched.h"
34
35 static struct tst_cg_group *cg_child_a, *cg_child_b;
36 static uid_t nobody_uid;
37
38 struct lesser_ns_open_thread_arg {
39 int fds[TST_CG_ROOTS_MAX];
40 int loops;
41 };
42
lesser_ns_open_thread_fn(void * arg)43 static int lesser_ns_open_thread_fn(void *arg)
44 {
45 struct lesser_ns_open_thread_arg *targ = arg;
46
47 targ->loops = SAFE_CG_OPEN(cg_child_b, "cgroup.procs", O_RDWR, targ->fds);
48 return 0;
49 }
50
test_lesser_ns_open(void)51 static void test_lesser_ns_open(void)
52 {
53 int i;
54 static char stack[65536];
55 pid_t pid;
56 int status;
57 struct lesser_ns_open_thread_arg targ = { .fds = {0}, .loops = -1};
58
59 cg_child_a = tst_cg_group_mk(tst_cg, "child_a");
60 cg_child_b = tst_cg_group_mk(tst_cg, "child_b");
61
62 if (!SAFE_FORK()) {
63 SAFE_CG_PRINT(cg_child_a, "cgroup.procs", "0");
64 SAFE_CG_FCHOWN(cg_child_a, "cgroup.procs", nobody_uid, -1);
65 SAFE_CG_FCHOWN(cg_child_b, "cgroup.procs", nobody_uid, -1);
66 pid = ltp_clone(CLONE_NEWCGROUP | CLONE_FILES | CLONE_VM | SIGCHLD,
67 lesser_ns_open_thread_fn, &targ, 65536, stack);
68 if (pid < 0) {
69 tst_res(TFAIL, "unexpected negative pid %d", pid);
70 exit(1);
71 }
72
73 SAFE_WAITPID(pid, &status, 0);
74 for (i = 0; i < targ.loops; i++) {
75 if (targ.fds[i] < 1) {
76 tst_res(TFAIL, "unexpected negative fd %d", targ.fds[i]);
77 exit(1);
78 }
79
80 TEST(write(targ.fds[i], "0", 1));
81 if (TST_RET >= 0 || TST_ERR != ENOENT)
82 tst_res(TFAIL, "%s failed", __func__);
83 else
84 tst_res(TPASS | TTERRNO, "%s passed", __func__);
85
86 SAFE_CLOSE(targ.fds[i]);
87 }
88 exit(0);
89 }
90
91 tst_reap_children();
92 cg_child_b = tst_cg_group_rm(cg_child_b);
93 cg_child_a = tst_cg_group_rm(cg_child_a);
94 }
95
setup(void)96 static void setup(void)
97 {
98 struct passwd *pw;
99
100 pw = SAFE_GETPWNAM("nobody");
101 nobody_uid = pw->pw_uid;
102 }
103
cleanup(void)104 static void cleanup(void)
105 {
106 if (cg_child_a) {
107 SAFE_CG_PRINTF(tst_cg_drain, "cgroup.procs", "%d", getpid());
108 cg_child_a = tst_cg_group_rm(cg_child_a);
109 }
110 if (cg_child_b) {
111 SAFE_CG_PRINTF(tst_cg_drain, "cgroup.procs", "%d", getpid());
112 cg_child_b = tst_cg_group_rm(cg_child_b);
113 }
114 }
115
116 static struct tst_test test = {
117 .setup = setup,
118 .cleanup = cleanup,
119 .test_all = test_lesser_ns_open,
120 .forks_child = 1,
121 .needs_root = 1,
122 .needs_cgroup_ctrls = (const char *const[]){"memory", NULL},
123 .needs_cgroup_ver = TST_CG_V2,
124 .needs_cgroup_nsdelegate = 1,
125 .tags = (const struct tst_tag[]) {
126 {"linux-git", "e57457641613"},
127 {"CVE", "2021-4197"},
128 {}
129 },
130 };
131