xref: /linux/tools/testing/selftests/cgroup/test_core.c (revision 40d8c81577db09b71ee5402ba336b642d32d6a82)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 
3 #define _GNU_SOURCE
4 #include <linux/limits.h>
5 #include <linux/sched.h>
6 #include <sys/types.h>
7 #include <sys/mman.h>
8 #include <sys/mount.h>
9 #include <sys/stat.h>
10 #include <sys/wait.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <sched.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <signal.h>
17 #include <string.h>
18 #include <pthread.h>
19 
20 #include "kselftest.h"
21 #include "cgroup_util.h"
22 
23 static bool nsdelegate;
24 #ifndef CLONE_NEWCGROUP
25 #define CLONE_NEWCGROUP 0
26 #endif
27 
touch_anon(char * buf,size_t size)28 static int touch_anon(char *buf, size_t size)
29 {
30 	int fd;
31 	char *pos = buf;
32 
33 	fd = open("/dev/urandom", O_RDONLY);
34 	if (fd < 0)
35 		return -1;
36 
37 	while (size > 0) {
38 		ssize_t ret = read(fd, pos, size);
39 
40 		if (ret < 0) {
41 			if (errno != EINTR) {
42 				close(fd);
43 				return -1;
44 			}
45 		} else {
46 			pos += ret;
47 			size -= ret;
48 		}
49 	}
50 	close(fd);
51 
52 	return 0;
53 }
54 
alloc_and_touch_anon_noexit(const char * cgroup,void * arg)55 static int alloc_and_touch_anon_noexit(const char *cgroup, void *arg)
56 {
57 	int ppid = getppid();
58 	size_t size = (size_t)arg;
59 	void *buf;
60 
61 	buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
62 		   0, 0);
63 	if (buf == MAP_FAILED)
64 		return -1;
65 
66 	if (touch_anon((char *)buf, size)) {
67 		munmap(buf, size);
68 		return -1;
69 	}
70 
71 	while (getppid() == ppid)
72 		sleep(1);
73 
74 	munmap(buf, size);
75 	return 0;
76 }
77 
78 /*
79  * Create a child process that allocates and touches 100MB, then waits to be
80  * killed. Wait until the child is attached to the cgroup, kill all processes
81  * in that cgroup and wait until "cgroup.procs" is empty. At this point try to
82  * destroy the empty cgroup. The test helps detect race conditions between
83  * dying processes leaving the cgroup and cgroup destruction path.
84  */
test_cgcore_destroy(const char * root)85 static int test_cgcore_destroy(const char *root)
86 {
87 	int ret = KSFT_FAIL;
88 	char *cg_test = NULL;
89 	int child_pid;
90 	char buf[BUF_SIZE];
91 
92 	cg_test = cg_name(root, "cg_test");
93 
94 	if (!cg_test)
95 		goto cleanup;
96 
97 	for (int i = 0; i < 10; i++) {
98 		if (cg_create(cg_test))
99 			goto cleanup;
100 
101 		child_pid = cg_run_nowait(cg_test, alloc_and_touch_anon_noexit,
102 					  (void *) MB(100));
103 
104 		if (child_pid < 0)
105 			goto cleanup;
106 
107 		/* wait for the child to enter cgroup */
108 		if (cg_wait_for_proc_count(cg_test, 1))
109 			goto cleanup;
110 
111 		if (cg_killall(cg_test))
112 			goto cleanup;
113 
114 		/* wait for cgroup to be empty */
115 		while (1) {
116 			if (cg_read(cg_test, "cgroup.procs", buf, sizeof(buf)))
117 				goto cleanup;
118 			if (buf[0] == '\0')
119 				break;
120 			usleep(1000);
121 		}
122 
123 		if (rmdir(cg_test))
124 			goto cleanup;
125 
126 		if (waitpid(child_pid, NULL, 0) < 0)
127 			goto cleanup;
128 	}
129 	ret = KSFT_PASS;
130 cleanup:
131 	if (cg_test)
132 		cg_destroy(cg_test);
133 	free(cg_test);
134 	return ret;
135 }
136 
137 /*
138  * A(0) - B(0) - C(1)
139  *        \ D(0)
140  *
141  * A, B and C's "populated" fields would be 1 while D's 0.
142  * test that after the one process in C is moved to root,
143  * A,B and C's "populated" fields would flip to "0" and file
144  * modified events will be generated on the
145  * "cgroup.events" files of both cgroups.
146  */
test_cgcore_populated(const char * root)147 static int test_cgcore_populated(const char *root)
148 {
149 	int ret = KSFT_FAIL;
150 	int err;
151 	char *cg_test_a = NULL, *cg_test_b = NULL;
152 	char *cg_test_c = NULL, *cg_test_d = NULL;
153 	int cgroup_fd = -EBADF;
154 	pid_t pid;
155 
156 	if (cg_test_v1_named)
157 		return KSFT_SKIP;
158 
159 	cg_test_a = cg_name(root, "cg_test_a");
160 	cg_test_b = cg_name(root, "cg_test_a/cg_test_b");
161 	cg_test_c = cg_name(root, "cg_test_a/cg_test_b/cg_test_c");
162 	cg_test_d = cg_name(root, "cg_test_a/cg_test_b/cg_test_d");
163 
164 	if (!cg_test_a || !cg_test_b || !cg_test_c || !cg_test_d)
165 		goto cleanup;
166 
167 	if (cg_create(cg_test_a))
168 		goto cleanup;
169 
170 	if (cg_create(cg_test_b))
171 		goto cleanup;
172 
173 	if (cg_create(cg_test_c))
174 		goto cleanup;
175 
176 	if (cg_create(cg_test_d))
177 		goto cleanup;
178 
179 	if (cg_enter_current(cg_test_c))
180 		goto cleanup;
181 
182 	if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 1\n"))
183 		goto cleanup;
184 
185 	if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 1\n"))
186 		goto cleanup;
187 
188 	if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 1\n"))
189 		goto cleanup;
190 
191 	if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
192 		goto cleanup;
193 
194 	if (cg_enter_current(root))
195 		goto cleanup;
196 
197 	if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 0\n"))
198 		goto cleanup;
199 
200 	if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 0\n"))
201 		goto cleanup;
202 
203 	if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 0\n"))
204 		goto cleanup;
205 
206 	if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
207 		goto cleanup;
208 
209 	/* Test that we can directly clone into a new cgroup. */
210 	cgroup_fd = dirfd_open_opath(cg_test_d);
211 	if (cgroup_fd < 0)
212 		goto cleanup;
213 
214 	pid = clone_into_cgroup(cgroup_fd);
215 	if (pid < 0) {
216 		if (errno == ENOSYS)
217 			goto cleanup_pass;
218 		goto cleanup;
219 	}
220 
221 	if (pid == 0) {
222 		if (raise(SIGSTOP))
223 			exit(EXIT_FAILURE);
224 		exit(EXIT_SUCCESS);
225 	}
226 
227 	err = cg_read_strcmp(cg_test_d, "cgroup.events", "populated 1\n");
228 
229 	(void)clone_reap(pid, WSTOPPED);
230 	(void)kill(pid, SIGCONT);
231 	(void)clone_reap(pid, WEXITED);
232 
233 	if (err)
234 		goto cleanup;
235 
236 	if (cg_read_strcmp_wait(cg_test_d, "cgroup.events",
237 				   "populated 0\n"))
238 		goto cleanup;
239 
240 	/* Remove cgroup. */
241 	if (cg_test_d) {
242 		cg_destroy(cg_test_d);
243 		free(cg_test_d);
244 		cg_test_d = NULL;
245 	}
246 
247 	pid = clone_into_cgroup(cgroup_fd);
248 	if (pid < 0)
249 		goto cleanup_pass;
250 	if (pid == 0)
251 		exit(EXIT_SUCCESS);
252 	(void)clone_reap(pid, WEXITED);
253 	goto cleanup;
254 
255 cleanup_pass:
256 	ret = KSFT_PASS;
257 
258 cleanup:
259 	if (cg_test_d)
260 		cg_destroy(cg_test_d);
261 	if (cg_test_c)
262 		cg_destroy(cg_test_c);
263 	if (cg_test_b)
264 		cg_destroy(cg_test_b);
265 	if (cg_test_a)
266 		cg_destroy(cg_test_a);
267 	free(cg_test_d);
268 	free(cg_test_c);
269 	free(cg_test_b);
270 	free(cg_test_a);
271 	if (cgroup_fd >= 0)
272 		close(cgroup_fd);
273 	return ret;
274 }
275 
276 /*
277  * A (domain threaded) - B (threaded) - C (domain)
278  *
279  * test that C can't be used until it is turned into a
280  * threaded cgroup.  "cgroup.type" file will report "domain (invalid)" in
281  * these cases. Operations which fail due to invalid topology use
282  * EOPNOTSUPP as the errno.
283  */
test_cgcore_invalid_domain(const char * root)284 static int test_cgcore_invalid_domain(const char *root)
285 {
286 	int ret = KSFT_FAIL;
287 	char *grandparent = NULL, *parent = NULL, *child = NULL;
288 
289 	if (cg_test_v1_named)
290 		return KSFT_SKIP;
291 
292 	grandparent = cg_name(root, "cg_test_grandparent");
293 	parent = cg_name(root, "cg_test_grandparent/cg_test_parent");
294 	child = cg_name(root, "cg_test_grandparent/cg_test_parent/cg_test_child");
295 	if (!parent || !child || !grandparent)
296 		goto cleanup;
297 
298 	if (cg_create(grandparent))
299 		goto cleanup;
300 
301 	if (cg_create(parent))
302 		goto cleanup;
303 
304 	if (cg_create(child))
305 		goto cleanup;
306 
307 	if (cg_write(parent, "cgroup.type", "threaded"))
308 		goto cleanup;
309 
310 	if (cg_read_strcmp(child, "cgroup.type", "domain invalid\n"))
311 		goto cleanup;
312 
313 	if (!cg_enter_current(child))
314 		goto cleanup;
315 
316 	if (errno != EOPNOTSUPP)
317 		goto cleanup;
318 
319 	if (!clone_into_cgroup_run_wait(child))
320 		goto cleanup;
321 
322 	if (errno == ENOSYS)
323 		goto cleanup_pass;
324 
325 	if (errno != EOPNOTSUPP)
326 		goto cleanup;
327 
328 cleanup_pass:
329 	ret = KSFT_PASS;
330 
331 cleanup:
332 	cg_enter_current(root);
333 	if (child)
334 		cg_destroy(child);
335 	if (parent)
336 		cg_destroy(parent);
337 	if (grandparent)
338 		cg_destroy(grandparent);
339 	free(child);
340 	free(parent);
341 	free(grandparent);
342 	return ret;
343 }
344 
345 /*
346  * Test that when a child becomes threaded
347  * the parent type becomes domain threaded.
348  */
test_cgcore_parent_becomes_threaded(const char * root)349 static int test_cgcore_parent_becomes_threaded(const char *root)
350 {
351 	int ret = KSFT_FAIL;
352 	char *parent = NULL, *child = NULL;
353 
354 	if (cg_test_v1_named)
355 		return KSFT_SKIP;
356 
357 	parent = cg_name(root, "cg_test_parent");
358 	child = cg_name(root, "cg_test_parent/cg_test_child");
359 	if (!parent || !child)
360 		goto cleanup;
361 
362 	if (cg_create(parent))
363 		goto cleanup;
364 
365 	if (cg_create(child))
366 		goto cleanup;
367 
368 	if (cg_write(child, "cgroup.type", "threaded"))
369 		goto cleanup;
370 
371 	if (cg_read_strcmp(parent, "cgroup.type", "domain threaded\n"))
372 		goto cleanup;
373 
374 	ret = KSFT_PASS;
375 
376 cleanup:
377 	if (child)
378 		cg_destroy(child);
379 	if (parent)
380 		cg_destroy(parent);
381 	free(child);
382 	free(parent);
383 	return ret;
384 
385 }
386 
387 /*
388  * Test that there's no internal process constrain on threaded cgroups.
389  * You can add threads/processes on a parent with a controller enabled.
390  */
test_cgcore_no_internal_process_constraint_on_threads(const char * root)391 static int test_cgcore_no_internal_process_constraint_on_threads(const char *root)
392 {
393 	int ret = KSFT_FAIL;
394 	char *parent = NULL, *child = NULL;
395 
396 	if (cg_test_v1_named ||
397 	    cg_read_strstr(root, "cgroup.controllers", "cpu") ||
398 	    cg_write(root, "cgroup.subtree_control", "+cpu")) {
399 		ret = KSFT_SKIP;
400 		goto cleanup;
401 	}
402 
403 	parent = cg_name(root, "cg_test_parent");
404 	child = cg_name(root, "cg_test_parent/cg_test_child");
405 	if (!parent || !child)
406 		goto cleanup;
407 
408 	if (cg_create(parent))
409 		goto cleanup;
410 
411 	if (cg_create(child))
412 		goto cleanup;
413 
414 	if (cg_write(parent, "cgroup.type", "threaded"))
415 		goto cleanup;
416 
417 	if (cg_write(child, "cgroup.type", "threaded"))
418 		goto cleanup;
419 
420 	if (cg_write(parent, "cgroup.subtree_control", "+cpu"))
421 		goto cleanup;
422 
423 	if (cg_enter_current(parent))
424 		goto cleanup;
425 
426 	ret = KSFT_PASS;
427 
428 cleanup:
429 	cg_enter_current(root);
430 	if (child)
431 		cg_destroy(child);
432 	if (parent)
433 		cg_destroy(parent);
434 	free(child);
435 	free(parent);
436 	return ret;
437 }
438 
439 /*
440  * Test that you can't enable a controller on a child if it's not enabled
441  * on the parent.
442  */
test_cgcore_top_down_constraint_enable(const char * root)443 static int test_cgcore_top_down_constraint_enable(const char *root)
444 {
445 	int ret = KSFT_FAIL;
446 	char *parent = NULL, *child = NULL;
447 
448 	if (cg_test_v1_named)
449 		return KSFT_SKIP;
450 
451 	parent = cg_name(root, "cg_test_parent");
452 	child = cg_name(root, "cg_test_parent/cg_test_child");
453 	if (!parent || !child)
454 		goto cleanup;
455 
456 	if (cg_create(parent))
457 		goto cleanup;
458 
459 	if (cg_create(child))
460 		goto cleanup;
461 
462 	if (!cg_write(child, "cgroup.subtree_control", "+memory"))
463 		goto cleanup;
464 
465 	ret = KSFT_PASS;
466 
467 cleanup:
468 	if (child)
469 		cg_destroy(child);
470 	if (parent)
471 		cg_destroy(parent);
472 	free(child);
473 	free(parent);
474 	return ret;
475 }
476 
477 /*
478  * Test that you can't disable a controller on a parent
479  * if it's enabled in a child.
480  */
test_cgcore_top_down_constraint_disable(const char * root)481 static int test_cgcore_top_down_constraint_disable(const char *root)
482 {
483 	int ret = KSFT_FAIL;
484 	char *parent = NULL, *child = NULL;
485 
486 	if (cg_test_v1_named)
487 		return KSFT_SKIP;
488 
489 	parent = cg_name(root, "cg_test_parent");
490 	child = cg_name(root, "cg_test_parent/cg_test_child");
491 	if (!parent || !child)
492 		goto cleanup;
493 
494 	if (cg_create(parent))
495 		goto cleanup;
496 
497 	if (cg_create(child))
498 		goto cleanup;
499 
500 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
501 		goto cleanup;
502 
503 	if (cg_write(child, "cgroup.subtree_control", "+memory"))
504 		goto cleanup;
505 
506 	if (!cg_write(parent, "cgroup.subtree_control", "-memory"))
507 		goto cleanup;
508 
509 	ret = KSFT_PASS;
510 
511 cleanup:
512 	if (child)
513 		cg_destroy(child);
514 	if (parent)
515 		cg_destroy(parent);
516 	free(child);
517 	free(parent);
518 	return ret;
519 }
520 
521 /*
522  * Test internal process constraint.
523  * You can't add a pid to a domain parent if a controller is enabled.
524  */
test_cgcore_internal_process_constraint(const char * root)525 static int test_cgcore_internal_process_constraint(const char *root)
526 {
527 	int ret = KSFT_FAIL;
528 	char *parent = NULL, *child = NULL;
529 
530 	if (cg_test_v1_named)
531 		return KSFT_SKIP;
532 
533 	parent = cg_name(root, "cg_test_parent");
534 	child = cg_name(root, "cg_test_parent/cg_test_child");
535 	if (!parent || !child)
536 		goto cleanup;
537 
538 	if (cg_create(parent))
539 		goto cleanup;
540 
541 	if (cg_create(child))
542 		goto cleanup;
543 
544 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
545 		goto cleanup;
546 
547 	if (!cg_enter_current(parent))
548 		goto cleanup;
549 
550 	if (!clone_into_cgroup_run_wait(parent))
551 		goto cleanup;
552 
553 	ret = KSFT_PASS;
554 
555 cleanup:
556 	if (child)
557 		cg_destroy(child);
558 	if (parent)
559 		cg_destroy(parent);
560 	free(child);
561 	free(parent);
562 	return ret;
563 }
564 
dummy_thread_fn(void * arg)565 static void *dummy_thread_fn(void *arg)
566 {
567 	return (void *)(size_t)pause();
568 }
569 
570 /*
571  * Test threadgroup migration.
572  * All threads of a process are migrated together.
573  */
test_cgcore_proc_migration(const char * root)574 static int test_cgcore_proc_migration(const char *root)
575 {
576 	int ret = KSFT_FAIL;
577 	int t, c_threads = 0, n_threads = 13;
578 	char *src = NULL, *dst = NULL;
579 	pthread_t threads[n_threads];
580 
581 	src = cg_name(root, "cg_src");
582 	dst = cg_name(root, "cg_dst");
583 	if (!src || !dst)
584 		goto cleanup;
585 
586 	if (cg_create(src))
587 		goto cleanup;
588 	if (cg_create(dst))
589 		goto cleanup;
590 
591 	if (cg_enter_current(src))
592 		goto cleanup;
593 
594 	for (c_threads = 0; c_threads < n_threads; ++c_threads) {
595 		if (pthread_create(&threads[c_threads], NULL, dummy_thread_fn, NULL))
596 			goto cleanup;
597 	}
598 
599 	cg_enter_current(dst);
600 	if (cg_read_lc(dst, CG_THREADS_FILE) != n_threads + 1)
601 		goto cleanup;
602 
603 	ret = KSFT_PASS;
604 
605 cleanup:
606 	for (t = 0; t < c_threads; ++t) {
607 		pthread_cancel(threads[t]);
608 	}
609 
610 	for (t = 0; t < c_threads; ++t) {
611 		pthread_join(threads[t], NULL);
612 	}
613 
614 	cg_enter_current(root);
615 
616 	if (dst)
617 		cg_destroy(dst);
618 	if (src)
619 		cg_destroy(src);
620 	free(dst);
621 	free(src);
622 	return ret;
623 }
624 
migrating_thread_fn(void * arg)625 static void *migrating_thread_fn(void *arg)
626 {
627 	int g, i, n_iterations = 1000;
628 	char **grps = arg;
629 	char lines[3][PATH_MAX];
630 
631 	for (g = 1; g < 3; ++g)
632 		snprintf(lines[g], sizeof(lines[g]), CG_PATH_FORMAT, grps[g] + strlen(grps[0]));
633 
634 	for (i = 0; i < n_iterations; ++i) {
635 		cg_enter_current_thread(grps[(i % 2) + 1]);
636 
637 		if (proc_read_strstr(0, 1, "cgroup", lines[(i % 2) + 1]))
638 			return (void *)-1;
639 	}
640 	return NULL;
641 }
642 
643 /*
644  * Test single thread migration.
645  * Threaded cgroups allow successful migration of a thread.
646  */
test_cgcore_thread_migration(const char * root)647 static int test_cgcore_thread_migration(const char *root)
648 {
649 	int ret = KSFT_FAIL;
650 	char *dom = NULL;
651 	char line[PATH_MAX];
652 	char *grps[3] = { (char *)root, NULL, NULL };
653 	pthread_t thr;
654 	void *retval;
655 
656 	dom = cg_name(root, "cg_dom");
657 	grps[1] = cg_name(root, "cg_dom/cg_src");
658 	grps[2] = cg_name(root, "cg_dom/cg_dst");
659 	if (!grps[1] || !grps[2] || !dom)
660 		goto cleanup;
661 
662 	if (cg_create(dom))
663 		goto cleanup;
664 	if (cg_create(grps[1]))
665 		goto cleanup;
666 	if (cg_create(grps[2]))
667 		goto cleanup;
668 
669 	if (!cg_test_v1_named) {
670 		if (cg_write(grps[1], "cgroup.type", "threaded"))
671 			goto cleanup;
672 		if (cg_write(grps[2], "cgroup.type", "threaded"))
673 			goto cleanup;
674 	}
675 
676 	if (cg_enter_current(grps[1]))
677 		goto cleanup;
678 
679 	if (pthread_create(&thr, NULL, migrating_thread_fn, grps))
680 		goto cleanup;
681 
682 	if (pthread_join(thr, &retval))
683 		goto cleanup;
684 
685 	if (retval)
686 		goto cleanup;
687 
688 	snprintf(line, sizeof(line), CG_PATH_FORMAT, grps[1] + strlen(grps[0]));
689 	if (proc_read_strstr(0, 1, "cgroup", line))
690 		goto cleanup;
691 
692 	ret = KSFT_PASS;
693 
694 cleanup:
695 	cg_enter_current(root);
696 	if (grps[2])
697 		cg_destroy(grps[2]);
698 	if (grps[1])
699 		cg_destroy(grps[1]);
700 	if (dom)
701 		cg_destroy(dom);
702 	free(grps[2]);
703 	free(grps[1]);
704 	free(dom);
705 	return ret;
706 }
707 
708 /*
709  * cgroup migration permission check should be performed based on the
710  * credentials at the time of open instead of write.
711  */
test_cgcore_lesser_euid_open(const char * root)712 static int test_cgcore_lesser_euid_open(const char *root)
713 {
714 	const uid_t test_euid = TEST_UID;
715 	int ret = KSFT_FAIL;
716 	char *cg_test_a = NULL, *cg_test_b = NULL;
717 	char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL;
718 	int cg_test_b_procs_fd = -1;
719 	uid_t saved_uid;
720 
721 	cg_test_a = cg_name(root, "cg_test_a");
722 	cg_test_b = cg_name(root, "cg_test_b");
723 
724 	if (!cg_test_a || !cg_test_b)
725 		goto cleanup;
726 
727 	cg_test_a_procs = cg_name(cg_test_a, "cgroup.procs");
728 	cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs");
729 
730 	if (!cg_test_a_procs || !cg_test_b_procs)
731 		goto cleanup;
732 
733 	if (cg_create(cg_test_a) || cg_create(cg_test_b))
734 		goto cleanup;
735 
736 	if (cg_enter_current(cg_test_a))
737 		goto cleanup;
738 
739 	if (chown(cg_test_a_procs, test_euid, -1) ||
740 	    chown(cg_test_b_procs, test_euid, -1))
741 		goto cleanup;
742 
743 	saved_uid = geteuid();
744 	if (seteuid(test_euid))
745 		goto cleanup;
746 
747 	cg_test_b_procs_fd = open(cg_test_b_procs, O_RDWR);
748 
749 	if (seteuid(saved_uid))
750 		goto cleanup;
751 
752 	if (cg_test_b_procs_fd < 0)
753 		goto cleanup;
754 
755 	if (write(cg_test_b_procs_fd, "0", 1) >= 0 || errno != EACCES)
756 		goto cleanup;
757 
758 	ret = KSFT_PASS;
759 
760 cleanup:
761 	cg_enter_current(root);
762 	if (cg_test_b_procs_fd >= 0)
763 		close(cg_test_b_procs_fd);
764 	if (cg_test_b)
765 		cg_destroy(cg_test_b);
766 	if (cg_test_a)
767 		cg_destroy(cg_test_a);
768 	free(cg_test_b_procs);
769 	free(cg_test_a_procs);
770 	free(cg_test_b);
771 	free(cg_test_a);
772 	return ret;
773 }
774 
775 struct lesser_ns_open_thread_arg {
776 	const char	*path;
777 	int		fd;
778 	int		err;
779 };
780 
lesser_ns_open_thread_fn(void * arg)781 static int lesser_ns_open_thread_fn(void *arg)
782 {
783 	struct lesser_ns_open_thread_arg *targ = arg;
784 
785 	targ->fd = open(targ->path, O_RDWR);
786 	targ->err = errno;
787 	return 0;
788 }
789 
790 /*
791  * cgroup migration permission check should be performed based on the cgroup
792  * namespace at the time of open instead of write.
793  */
test_cgcore_lesser_ns_open(const char * root)794 static int test_cgcore_lesser_ns_open(const char *root)
795 {
796 	static char stack[65536];
797 	int ret = KSFT_FAIL;
798 	char *cg_test_a = NULL, *cg_test_b = NULL;
799 	char *cg_test_b_procs = NULL;
800 	int cg_test_b_procs_fd = -1;
801 	struct lesser_ns_open_thread_arg targ = { .fd = -1 };
802 	pid_t pid;
803 	int status;
804 
805 	if (!nsdelegate)
806 		return KSFT_SKIP;
807 
808 	cg_test_a = cg_name(root, "cg_test_a");
809 	cg_test_b = cg_name(root, "cg_test_b");
810 
811 	if (!cg_test_a || !cg_test_b)
812 		goto cleanup;
813 
814 	cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs");
815 
816 	if (!cg_test_b_procs)
817 		goto cleanup;
818 
819 	if (cg_create(cg_test_a) || cg_create(cg_test_b))
820 		goto cleanup;
821 
822 	if (cg_enter_current(cg_test_b))
823 		goto cleanup;
824 
825 	targ.path = cg_test_b_procs;
826 	pid = clone(lesser_ns_open_thread_fn, stack + sizeof(stack),
827 		    CLONE_NEWCGROUP | CLONE_FILES | CLONE_VM | SIGCHLD,
828 		    &targ);
829 	if (pid < 0)
830 		goto cleanup;
831 
832 	if (waitpid(pid, &status, 0) < 0)
833 		goto cleanup;
834 
835 	if (!WIFEXITED(status))
836 		goto cleanup;
837 
838 	cg_test_b_procs_fd = targ.fd;
839 	if (cg_test_b_procs_fd < 0)
840 		goto cleanup;
841 
842 	if (cg_enter_current(cg_test_a))
843 		goto cleanup;
844 
845 	if ((status = write(cg_test_b_procs_fd, "0", 1)) >= 0 || errno != ENOENT)
846 		goto cleanup;
847 
848 	ret = KSFT_PASS;
849 
850 cleanup:
851 	cg_enter_current(root);
852 	if (cg_test_b_procs_fd >= 0)
853 		close(cg_test_b_procs_fd);
854 	if (cg_test_b)
855 		cg_destroy(cg_test_b);
856 	if (cg_test_a)
857 		cg_destroy(cg_test_a);
858 	free(cg_test_b_procs);
859 	free(cg_test_b);
860 	free(cg_test_a);
861 	return ret;
862 }
863 
setup_named_v1_root(char * root,size_t len,const char * name)864 static int setup_named_v1_root(char *root, size_t len, const char *name)
865 {
866 	char options[PATH_MAX];
867 	int r;
868 
869 	r = snprintf(root, len, "/mnt/cg_selftest");
870 	if (r < 0)
871 		return r;
872 
873 	r = snprintf(options, sizeof(options), "none,name=%s", name);
874 	if (r < 0)
875 		return r;
876 
877 	r = mkdir(root, 0755);
878 	if (r < 0 && errno != EEXIST)
879 		return r;
880 
881 	r = mount("none", root, "cgroup", 0, options);
882 	if (r < 0)
883 		return r;
884 
885 	return 0;
886 }
887 
cleanup_named_v1_root(char * root)888 static void cleanup_named_v1_root(char *root)
889 {
890 	if (!cg_test_v1_named)
891 		return;
892 	umount(root);
893 	rmdir(root);
894 }
895 
896 #define T(x) { x, #x }
897 struct corecg_test {
898 	int (*fn)(const char *root);
899 	const char *name;
900 } tests[] = {
901 	T(test_cgcore_internal_process_constraint),
902 	T(test_cgcore_top_down_constraint_enable),
903 	T(test_cgcore_top_down_constraint_disable),
904 	T(test_cgcore_no_internal_process_constraint_on_threads),
905 	T(test_cgcore_parent_becomes_threaded),
906 	T(test_cgcore_invalid_domain),
907 	T(test_cgcore_populated),
908 	T(test_cgcore_proc_migration),
909 	T(test_cgcore_thread_migration),
910 	T(test_cgcore_destroy),
911 	T(test_cgcore_lesser_euid_open),
912 	T(test_cgcore_lesser_ns_open),
913 };
914 #undef T
915 
main(int argc,char * argv[])916 int main(int argc, char *argv[])
917 {
918 	char root[PATH_MAX];
919 	int i;
920 
921 	ksft_print_header();
922 	ksft_set_plan(ARRAY_SIZE(tests));
923 	if (cg_find_unified_root(root, sizeof(root), &nsdelegate)) {
924 		if (setup_named_v1_root(root, sizeof(root), CG_NAMED_NAME))
925 			ksft_exit_skip("cgroup v2 isn't mounted and could not setup named v1 hierarchy\n");
926 		cg_test_v1_named = true;
927 		goto post_v2_setup;
928 	}
929 
930 	if (cg_read_strstr(root, "cgroup.subtree_control", "memory"))
931 		if (cg_write(root, "cgroup.subtree_control", "+memory"))
932 			ksft_exit_skip("Failed to set memory controller\n");
933 
934 post_v2_setup:
935 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
936 		switch (tests[i].fn(root)) {
937 		case KSFT_PASS:
938 			ksft_test_result_pass("%s\n", tests[i].name);
939 			break;
940 		case KSFT_SKIP:
941 			ksft_test_result_skip("%s\n", tests[i].name);
942 			break;
943 		default:
944 			ksft_test_result_fail("%s\n", tests[i].name);
945 			break;
946 		}
947 	}
948 
949 	cleanup_named_v1_root(root);
950 	ksft_finished();
951 }
952