~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/cgroup/test_cpuset.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 
  3 #include <linux/limits.h>
  4 #include <signal.h>
  5 
  6 #include "../kselftest.h"
  7 #include "cgroup_util.h"
  8 
  9 static int idle_process_fn(const char *cgroup, void *arg)
 10 {
 11         (void)pause();
 12         return 0;
 13 }
 14 
 15 static int do_migration_fn(const char *cgroup, void *arg)
 16 {
 17         int object_pid = (int)(size_t)arg;
 18 
 19         if (setuid(TEST_UID))
 20                 return EXIT_FAILURE;
 21 
 22         // XXX checking /proc/$pid/cgroup would be quicker than wait
 23         if (cg_enter(cgroup, object_pid) ||
 24             cg_wait_for_proc_count(cgroup, 1))
 25                 return EXIT_FAILURE;
 26 
 27         return EXIT_SUCCESS;
 28 }
 29 
 30 static int do_controller_fn(const char *cgroup, void *arg)
 31 {
 32         const char *child = cgroup;
 33         const char *parent = arg;
 34 
 35         if (setuid(TEST_UID))
 36                 return EXIT_FAILURE;
 37 
 38         if (!cg_read_strstr(child, "cgroup.controllers", "cpuset"))
 39                 return EXIT_FAILURE;
 40 
 41         if (cg_write(parent, "cgroup.subtree_control", "+cpuset"))
 42                 return EXIT_FAILURE;
 43 
 44         if (cg_read_strstr(child, "cgroup.controllers", "cpuset"))
 45                 return EXIT_FAILURE;
 46 
 47         if (cg_write(parent, "cgroup.subtree_control", "-cpuset"))
 48                 return EXIT_FAILURE;
 49 
 50         if (!cg_read_strstr(child, "cgroup.controllers", "cpuset"))
 51                 return EXIT_FAILURE;
 52 
 53         return EXIT_SUCCESS;
 54 }
 55 
 56 /*
 57  * Migrate a process between two sibling cgroups.
 58  * The success should only depend on the parent cgroup permissions and not the
 59  * migrated process itself (cpuset controller is in place because it uses
 60  * security_task_setscheduler() in cgroup v1).
 61  *
 62  * Deliberately don't set cpuset.cpus in children to avoid definining migration
 63  * permissions between two different cpusets.
 64  */
 65 static int test_cpuset_perms_object(const char *root, bool allow)
 66 {
 67         char *parent = NULL, *child_src = NULL, *child_dst = NULL;
 68         char *parent_procs = NULL, *child_src_procs = NULL, *child_dst_procs = NULL;
 69         const uid_t test_euid = TEST_UID;
 70         int object_pid = 0;
 71         int ret = KSFT_FAIL;
 72 
 73         parent = cg_name(root, "cpuset_test_0");
 74         if (!parent)
 75                 goto cleanup;
 76         parent_procs = cg_name(parent, "cgroup.procs");
 77         if (!parent_procs)
 78                 goto cleanup;
 79         if (cg_create(parent))
 80                 goto cleanup;
 81 
 82         child_src = cg_name(parent, "cpuset_test_1");
 83         if (!child_src)
 84                 goto cleanup;
 85         child_src_procs = cg_name(child_src, "cgroup.procs");
 86         if (!child_src_procs)
 87                 goto cleanup;
 88         if (cg_create(child_src))
 89                 goto cleanup;
 90 
 91         child_dst = cg_name(parent, "cpuset_test_2");
 92         if (!child_dst)
 93                 goto cleanup;
 94         child_dst_procs = cg_name(child_dst, "cgroup.procs");
 95         if (!child_dst_procs)
 96                 goto cleanup;
 97         if (cg_create(child_dst))
 98                 goto cleanup;
 99 
100         if (cg_write(parent, "cgroup.subtree_control", "+cpuset"))
101                 goto cleanup;
102 
103         if (cg_read_strstr(child_src, "cgroup.controllers", "cpuset") ||
104             cg_read_strstr(child_dst, "cgroup.controllers", "cpuset"))
105                 goto cleanup;
106 
107         /* Enable permissions along src->dst tree path */
108         if (chown(child_src_procs, test_euid, -1) ||
109             chown(child_dst_procs, test_euid, -1))
110                 goto cleanup;
111 
112         if (allow && chown(parent_procs, test_euid, -1))
113                 goto cleanup;
114 
115         /* Fork a privileged child as a test object */
116         object_pid = cg_run_nowait(child_src, idle_process_fn, NULL);
117         if (object_pid < 0)
118                 goto cleanup;
119 
120         /* Carry out migration in a child process that can drop all privileges
121          * (including capabilities), the main process must remain privileged for
122          * cleanup.
123          * Child process's cgroup is irrelevant but we place it into child_dst
124          * as hacky way to pass information about migration target to the child.
125          */
126         if (allow ^ (cg_run(child_dst, do_migration_fn, (void *)(size_t)object_pid) == EXIT_SUCCESS))
127                 goto cleanup;
128 
129         ret = KSFT_PASS;
130 
131 cleanup:
132         if (object_pid > 0) {
133                 (void)kill(object_pid, SIGTERM);
134                 (void)clone_reap(object_pid, WEXITED);
135         }
136 
137         cg_destroy(child_dst);
138         free(child_dst_procs);
139         free(child_dst);
140 
141         cg_destroy(child_src);
142         free(child_src_procs);
143         free(child_src);
144 
145         cg_destroy(parent);
146         free(parent_procs);
147         free(parent);
148 
149         return ret;
150 }
151 
152 static int test_cpuset_perms_object_allow(const char *root)
153 {
154         return test_cpuset_perms_object(root, true);
155 }
156 
157 static int test_cpuset_perms_object_deny(const char *root)
158 {
159         return test_cpuset_perms_object(root, false);
160 }
161 
162 /*
163  * Migrate a process between parent and child implicitely
164  * Implicit migration happens when a controller is enabled/disabled.
165  *
166  */
167 static int test_cpuset_perms_subtree(const char *root)
168 {
169         char *parent = NULL, *child = NULL;
170         char *parent_procs = NULL, *parent_subctl = NULL, *child_procs = NULL;
171         const uid_t test_euid = TEST_UID;
172         int object_pid = 0;
173         int ret = KSFT_FAIL;
174 
175         parent = cg_name(root, "cpuset_test_0");
176         if (!parent)
177                 goto cleanup;
178         parent_procs = cg_name(parent, "cgroup.procs");
179         if (!parent_procs)
180                 goto cleanup;
181         parent_subctl = cg_name(parent, "cgroup.subtree_control");
182         if (!parent_subctl)
183                 goto cleanup;
184         if (cg_create(parent))
185                 goto cleanup;
186 
187         child = cg_name(parent, "cpuset_test_1");
188         if (!child)
189                 goto cleanup;
190         child_procs = cg_name(child, "cgroup.procs");
191         if (!child_procs)
192                 goto cleanup;
193         if (cg_create(child))
194                 goto cleanup;
195 
196         /* Enable permissions as in a delegated subtree */
197         if (chown(parent_procs, test_euid, -1) ||
198             chown(parent_subctl, test_euid, -1) ||
199             chown(child_procs, test_euid, -1))
200                 goto cleanup;
201 
202         /* Put a privileged child in the subtree and modify controller state
203          * from an unprivileged process, the main process remains privileged
204          * for cleanup.
205          * The unprivileged child runs in subtree too to avoid parent and
206          * internal-node constraing violation.
207          */
208         object_pid = cg_run_nowait(child, idle_process_fn, NULL);
209         if (object_pid < 0)
210                 goto cleanup;
211 
212         if (cg_run(child, do_controller_fn, parent) != EXIT_SUCCESS)
213                 goto cleanup;
214 
215         ret = KSFT_PASS;
216 
217 cleanup:
218         if (object_pid > 0) {
219                 (void)kill(object_pid, SIGTERM);
220                 (void)clone_reap(object_pid, WEXITED);
221         }
222 
223         cg_destroy(child);
224         free(child_procs);
225         free(child);
226 
227         cg_destroy(parent);
228         free(parent_subctl);
229         free(parent_procs);
230         free(parent);
231 
232         return ret;
233 }
234 
235 
236 #define T(x) { x, #x }
237 struct cpuset_test {
238         int (*fn)(const char *root);
239         const char *name;
240 } tests[] = {
241         T(test_cpuset_perms_object_allow),
242         T(test_cpuset_perms_object_deny),
243         T(test_cpuset_perms_subtree),
244 };
245 #undef T
246 
247 int main(int argc, char *argv[])
248 {
249         char root[PATH_MAX];
250         int i, ret = EXIT_SUCCESS;
251 
252         if (cg_find_unified_root(root, sizeof(root), NULL))
253                 ksft_exit_skip("cgroup v2 isn't mounted\n");
254 
255         if (cg_read_strstr(root, "cgroup.subtree_control", "cpuset"))
256                 if (cg_write(root, "cgroup.subtree_control", "+cpuset"))
257                         ksft_exit_skip("Failed to set cpuset controller\n");
258 
259         for (i = 0; i < ARRAY_SIZE(tests); i++) {
260                 switch (tests[i].fn(root)) {
261                 case KSFT_PASS:
262                         ksft_test_result_pass("%s\n", tests[i].name);
263                         break;
264                 case KSFT_SKIP:
265                         ksft_test_result_skip("%s\n", tests[i].name);
266                         break;
267                 default:
268                         ret = EXIT_FAILURE;
269                         ksft_test_result_fail("%s\n", tests[i].name);
270                         break;
271                 }
272         }
273 
274         return ret;
275 }
276 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php