1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
3 
4 #include <sys/syscall.h>
5 #include <sys/mman.h>
6 #include <sys/wait.h>
7 #include <unistd.h>
8 #include <malloc.h>
9 #include <stdlib.h>
10 #include <test_progs.h>
11 #include "cgroup_helpers.h"
12 
13 #include "iters.skel.h"
14 #include "iters_state_safety.skel.h"
15 #include "iters_looping.skel.h"
16 #include "iters_num.skel.h"
17 #include "iters_testmod_seq.skel.h"
18 #include "iters_task_vma.skel.h"
19 #include "iters_task.skel.h"
20 #include "iters_css_task.skel.h"
21 #include "iters_css.skel.h"
22 #include "iters_task_failure.skel.h"
23 
24 static void subtest_num_iters(void)
25 {
26 	struct iters_num *skel;
27 	int err;
28 
29 	skel = iters_num__open_and_load();
30 	if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
31 		return;
32 
33 	err = iters_num__attach(skel);
34 	if (!ASSERT_OK(err, "skel_attach"))
35 		goto cleanup;
36 
37 	usleep(1);
38 	iters_num__detach(skel);
39 
40 #define VALIDATE_CASE(case_name)					\
41 	ASSERT_EQ(skel->bss->res_##case_name,				\
42 		  skel->rodata->exp_##case_name,			\
43 		  #case_name)
44 
45 	VALIDATE_CASE(empty_zero);
46 	VALIDATE_CASE(empty_int_min);
47 	VALIDATE_CASE(empty_int_max);
48 	VALIDATE_CASE(empty_minus_one);
49 
50 	VALIDATE_CASE(simple_sum);
51 	VALIDATE_CASE(neg_sum);
52 	VALIDATE_CASE(very_neg_sum);
53 	VALIDATE_CASE(neg_pos_sum);
54 
55 	VALIDATE_CASE(invalid_range);
56 	VALIDATE_CASE(max_range);
57 	VALIDATE_CASE(e2big_range);
58 
59 	VALIDATE_CASE(succ_elem_cnt);
60 	VALIDATE_CASE(overfetched_elem_cnt);
61 	VALIDATE_CASE(fail_elem_cnt);
62 
63 #undef VALIDATE_CASE
64 
65 cleanup:
66 	iters_num__destroy(skel);
67 }
68 
69 static void subtest_testmod_seq_iters(void)
70 {
71 	struct iters_testmod_seq *skel;
72 	int err;
73 
74 	if (!env.has_testmod) {
75 		test__skip();
76 		return;
77 	}
78 
79 	skel = iters_testmod_seq__open_and_load();
80 	if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
81 		return;
82 
83 	err = iters_testmod_seq__attach(skel);
84 	if (!ASSERT_OK(err, "skel_attach"))
85 		goto cleanup;
86 
87 	usleep(1);
88 	iters_testmod_seq__detach(skel);
89 
90 #define VALIDATE_CASE(case_name)					\
91 	ASSERT_EQ(skel->bss->res_##case_name,				\
92 		  skel->rodata->exp_##case_name,			\
93 		  #case_name)
94 
95 	VALIDATE_CASE(empty);
96 	VALIDATE_CASE(full);
97 	VALIDATE_CASE(truncated);
98 
99 #undef VALIDATE_CASE
100 
101 cleanup:
102 	iters_testmod_seq__destroy(skel);
103 }
104 
105 static void subtest_task_vma_iters(void)
106 {
107 	unsigned long start, end, bpf_iter_start, bpf_iter_end;
108 	struct iters_task_vma *skel;
109 	char rest_of_line[1000];
110 	unsigned int seen;
111 	FILE *f = NULL;
112 	int err;
113 
114 	skel = iters_task_vma__open_and_load();
115 	if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
116 		return;
117 
118 	skel->bss->target_pid = getpid();
119 
120 	err = iters_task_vma__attach(skel);
121 	if (!ASSERT_OK(err, "skel_attach"))
122 		goto cleanup;
123 
124 	getpgid(skel->bss->target_pid);
125 	iters_task_vma__detach(skel);
126 
127 	if (!ASSERT_GT(skel->bss->vmas_seen, 0, "vmas_seen_gt_zero"))
128 		goto cleanup;
129 
130 	f = fopen("/proc/self/maps", "r");
131 	if (!ASSERT_OK_PTR(f, "proc_maps_fopen"))
132 		goto cleanup;
133 
134 	seen = 0;
135 	while (fscanf(f, "%lx-%lx %[^\n]\n", &start, &end, rest_of_line) == 3) {
136 		/* [vsyscall] vma isn't _really_ part of task->mm vmas.
137 		 * /proc/PID/maps returns it when out of vmas - see get_gate_vma
138 		 * calls in fs/proc/task_mmu.c
139 		 */
140 		if (strstr(rest_of_line, "[vsyscall]"))
141 			continue;
142 
143 		bpf_iter_start = skel->bss->vm_ranges[seen].vm_start;
144 		bpf_iter_end = skel->bss->vm_ranges[seen].vm_end;
145 
146 		ASSERT_EQ(bpf_iter_start, start, "vma->vm_start match");
147 		ASSERT_EQ(bpf_iter_end, end, "vma->vm_end match");
148 		seen++;
149 	}
150 
151 	if (!ASSERT_EQ(skel->bss->vmas_seen, seen, "vmas_seen_eq"))
152 		goto cleanup;
153 
154 cleanup:
155 	if (f)
156 		fclose(f);
157 	iters_task_vma__destroy(skel);
158 }
159 
160 static pthread_mutex_t do_nothing_mutex;
161 
162 static void *do_nothing_wait(void *arg)
163 {
164 	pthread_mutex_lock(&do_nothing_mutex);
165 	pthread_mutex_unlock(&do_nothing_mutex);
166 
167 	pthread_exit(arg);
168 }
169 
170 #define thread_num 2
171 
172 static void subtest_task_iters(void)
173 {
174 	struct iters_task *skel = NULL;
175 	pthread_t thread_ids[thread_num];
176 	void *ret;
177 	int err;
178 
179 	skel = iters_task__open_and_load();
180 	if (!ASSERT_OK_PTR(skel, "open_and_load"))
181 		goto cleanup;
182 	skel->bss->target_pid = getpid();
183 	err = iters_task__attach(skel);
184 	if (!ASSERT_OK(err, "iters_task__attach"))
185 		goto cleanup;
186 	pthread_mutex_lock(&do_nothing_mutex);
187 	for (int i = 0; i < thread_num; i++)
188 		ASSERT_OK(pthread_create(&thread_ids[i], NULL, &do_nothing_wait, NULL),
189 			"pthread_create");
190 
191 	syscall(SYS_getpgid);
192 	iters_task__detach(skel);
193 	ASSERT_EQ(skel->bss->procs_cnt, 1, "procs_cnt");
194 	ASSERT_EQ(skel->bss->threads_cnt, thread_num + 1, "threads_cnt");
195 	ASSERT_EQ(skel->bss->proc_threads_cnt, thread_num + 1, "proc_threads_cnt");
196 	ASSERT_EQ(skel->bss->invalid_cnt, 0, "invalid_cnt");
197 	pthread_mutex_unlock(&do_nothing_mutex);
198 	for (int i = 0; i < thread_num; i++)
199 		ASSERT_OK(pthread_join(thread_ids[i], &ret), "pthread_join");
200 cleanup:
201 	iters_task__destroy(skel);
202 }
203 
204 extern int stack_mprotect(void);
205 
206 static void subtest_css_task_iters(void)
207 {
208 	struct iters_css_task *skel = NULL;
209 	int err, cg_fd, cg_id;
210 	const char *cgrp_path = "/cg1";
211 
212 	err = setup_cgroup_environment();
213 	if (!ASSERT_OK(err, "setup_cgroup_environment"))
214 		goto cleanup;
215 	cg_fd = create_and_get_cgroup(cgrp_path);
216 	if (!ASSERT_GE(cg_fd, 0, "create_and_get_cgroup"))
217 		goto cleanup;
218 	cg_id = get_cgroup_id(cgrp_path);
219 	err = join_cgroup(cgrp_path);
220 	if (!ASSERT_OK(err, "join_cgroup"))
221 		goto cleanup;
222 
223 	skel = iters_css_task__open_and_load();
224 	if (!ASSERT_OK_PTR(skel, "open_and_load"))
225 		goto cleanup;
226 
227 	skel->bss->target_pid = getpid();
228 	skel->bss->cg_id = cg_id;
229 	err = iters_css_task__attach(skel);
230 	if (!ASSERT_OK(err, "iters_task__attach"))
231 		goto cleanup;
232 	err = stack_mprotect();
233 	if (!ASSERT_EQ(err, -1, "stack_mprotect") ||
234 	    !ASSERT_EQ(errno, EPERM, "stack_mprotect"))
235 		goto cleanup;
236 	iters_css_task__detach(skel);
237 	ASSERT_EQ(skel->bss->css_task_cnt, 1, "css_task_cnt");
238 
239 cleanup:
240 	cleanup_cgroup_environment();
241 	iters_css_task__destroy(skel);
242 }
243 
244 static void subtest_css_iters(void)
245 {
246 	struct iters_css *skel = NULL;
247 	struct {
248 		const char *path;
249 		int fd;
250 	} cgs[] = {
251 		{ "/cg1" },
252 		{ "/cg1/cg2" },
253 		{ "/cg1/cg2/cg3" },
254 		{ "/cg1/cg2/cg3/cg4" },
255 	};
256 	int err, cg_nr = ARRAY_SIZE(cgs);
257 	int i;
258 
259 	err = setup_cgroup_environment();
260 	if (!ASSERT_OK(err, "setup_cgroup_environment"))
261 		goto cleanup;
262 	for (i = 0; i < cg_nr; i++) {
263 		cgs[i].fd = create_and_get_cgroup(cgs[i].path);
264 		if (!ASSERT_GE(cgs[i].fd, 0, "create_and_get_cgroup"))
265 			goto cleanup;
266 	}
267 
268 	skel = iters_css__open_and_load();
269 	if (!ASSERT_OK_PTR(skel, "open_and_load"))
270 		goto cleanup;
271 
272 	skel->bss->target_pid = getpid();
273 	skel->bss->root_cg_id = get_cgroup_id(cgs[0].path);
274 	skel->bss->leaf_cg_id = get_cgroup_id(cgs[cg_nr - 1].path);
275 	err = iters_css__attach(skel);
276 
277 	if (!ASSERT_OK(err, "iters_task__attach"))
278 		goto cleanup;
279 
280 	syscall(SYS_getpgid);
281 	ASSERT_EQ(skel->bss->pre_order_cnt, cg_nr, "pre_order_cnt");
282 	ASSERT_EQ(skel->bss->first_cg_id, get_cgroup_id(cgs[0].path), "first_cg_id");
283 
284 	ASSERT_EQ(skel->bss->post_order_cnt, cg_nr, "post_order_cnt");
285 	ASSERT_EQ(skel->bss->last_cg_id, get_cgroup_id(cgs[0].path), "last_cg_id");
286 	ASSERT_EQ(skel->bss->tree_high, cg_nr - 1, "tree_high");
287 	iters_css__detach(skel);
288 cleanup:
289 	cleanup_cgroup_environment();
290 	iters_css__destroy(skel);
291 }
292 
293 void test_iters(void)
294 {
295 	RUN_TESTS(iters_state_safety);
296 	RUN_TESTS(iters_looping);
297 	RUN_TESTS(iters);
298 	RUN_TESTS(iters_css_task);
299 
300 	if (env.has_testmod)
301 		RUN_TESTS(iters_testmod_seq);
302 
303 	if (test__start_subtest("num"))
304 		subtest_num_iters();
305 	if (test__start_subtest("testmod_seq"))
306 		subtest_testmod_seq_iters();
307 	if (test__start_subtest("task_vma"))
308 		subtest_task_vma_iters();
309 	if (test__start_subtest("task"))
310 		subtest_task_iters();
311 	if (test__start_subtest("css_task"))
312 		subtest_css_task_iters();
313 	if (test__start_subtest("css"))
314 		subtest_css_iters();
315 	RUN_TESTS(iters_task_failure);
316 }
317