xref: /openbmc/linux/kernel/bpf/task_iter.c (revision 9aab6601)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2020 Facebook */
3 
4 #include <linux/init.h>
5 #include <linux/namei.h>
6 #include <linux/pid_namespace.h>
7 #include <linux/fs.h>
8 #include <linux/fdtable.h>
9 #include <linux/filter.h>
10 #include <linux/btf_ids.h>
11 
12 struct bpf_iter_seq_task_common {
13 	struct pid_namespace *ns;
14 };
15 
16 struct bpf_iter_seq_task_info {
17 	/* The first field must be struct bpf_iter_seq_task_common.
18 	 * this is assumed by {init, fini}_seq_pidns() callback functions.
19 	 */
20 	struct bpf_iter_seq_task_common common;
21 	u32 tid;
22 };
23 
24 static struct task_struct *task_seq_get_next(struct pid_namespace *ns,
25 					     u32 *tid)
26 {
27 	struct task_struct *task = NULL;
28 	struct pid *pid;
29 
30 	rcu_read_lock();
31 retry:
32 	pid = find_ge_pid(*tid, ns);
33 	if (pid) {
34 		*tid = pid_nr_ns(pid, ns);
35 		task = get_pid_task(pid, PIDTYPE_PID);
36 		if (!task) {
37 			++*tid;
38 			goto retry;
39 		}
40 	}
41 	rcu_read_unlock();
42 
43 	return task;
44 }
45 
46 static void *task_seq_start(struct seq_file *seq, loff_t *pos)
47 {
48 	struct bpf_iter_seq_task_info *info = seq->private;
49 	struct task_struct *task;
50 
51 	task = task_seq_get_next(info->common.ns, &info->tid);
52 	if (!task)
53 		return NULL;
54 
55 	if (*pos == 0)
56 		++*pos;
57 	return task;
58 }
59 
60 static void *task_seq_next(struct seq_file *seq, void *v, loff_t *pos)
61 {
62 	struct bpf_iter_seq_task_info *info = seq->private;
63 	struct task_struct *task;
64 
65 	++*pos;
66 	++info->tid;
67 	put_task_struct((struct task_struct *)v);
68 	task = task_seq_get_next(info->common.ns, &info->tid);
69 	if (!task)
70 		return NULL;
71 
72 	return task;
73 }
74 
75 struct bpf_iter__task {
76 	__bpf_md_ptr(struct bpf_iter_meta *, meta);
77 	__bpf_md_ptr(struct task_struct *, task);
78 };
79 
80 DEFINE_BPF_ITER_FUNC(task, struct bpf_iter_meta *meta, struct task_struct *task)
81 
82 static int __task_seq_show(struct seq_file *seq, struct task_struct *task,
83 			   bool in_stop)
84 {
85 	struct bpf_iter_meta meta;
86 	struct bpf_iter__task ctx;
87 	struct bpf_prog *prog;
88 
89 	meta.seq = seq;
90 	prog = bpf_iter_get_info(&meta, in_stop);
91 	if (!prog)
92 		return 0;
93 
94 	meta.seq = seq;
95 	ctx.meta = &meta;
96 	ctx.task = task;
97 	return bpf_iter_run_prog(prog, &ctx);
98 }
99 
100 static int task_seq_show(struct seq_file *seq, void *v)
101 {
102 	return __task_seq_show(seq, v, false);
103 }
104 
105 static void task_seq_stop(struct seq_file *seq, void *v)
106 {
107 	if (!v)
108 		(void)__task_seq_show(seq, v, true);
109 	else
110 		put_task_struct((struct task_struct *)v);
111 }
112 
113 static const struct seq_operations task_seq_ops = {
114 	.start	= task_seq_start,
115 	.next	= task_seq_next,
116 	.stop	= task_seq_stop,
117 	.show	= task_seq_show,
118 };
119 
120 struct bpf_iter_seq_task_file_info {
121 	/* The first field must be struct bpf_iter_seq_task_common.
122 	 * this is assumed by {init, fini}_seq_pidns() callback functions.
123 	 */
124 	struct bpf_iter_seq_task_common common;
125 	struct task_struct *task;
126 	struct files_struct *files;
127 	u32 tid;
128 	u32 fd;
129 };
130 
131 static struct file *
132 task_file_seq_get_next(struct bpf_iter_seq_task_file_info *info,
133 		       struct task_struct **task, struct files_struct **fstruct)
134 {
135 	struct pid_namespace *ns = info->common.ns;
136 	u32 curr_tid = info->tid, max_fds;
137 	struct files_struct *curr_files;
138 	struct task_struct *curr_task;
139 	int curr_fd = info->fd;
140 
141 	/* If this function returns a non-NULL file object,
142 	 * it held a reference to the task/files_struct/file.
143 	 * Otherwise, it does not hold any reference.
144 	 */
145 again:
146 	if (*task) {
147 		curr_task = *task;
148 		curr_files = *fstruct;
149 		curr_fd = info->fd;
150 	} else {
151 		curr_task = task_seq_get_next(ns, &curr_tid);
152 		if (!curr_task)
153 			return NULL;
154 
155 		curr_files = get_files_struct(curr_task);
156 		if (!curr_files) {
157 			put_task_struct(curr_task);
158 			curr_tid = ++(info->tid);
159 			info->fd = 0;
160 			goto again;
161 		}
162 
163 		/* set *fstruct, *task and info->tid */
164 		*fstruct = curr_files;
165 		*task = curr_task;
166 		if (curr_tid == info->tid) {
167 			curr_fd = info->fd;
168 		} else {
169 			info->tid = curr_tid;
170 			curr_fd = 0;
171 		}
172 	}
173 
174 	rcu_read_lock();
175 	max_fds = files_fdtable(curr_files)->max_fds;
176 	for (; curr_fd < max_fds; curr_fd++) {
177 		struct file *f;
178 
179 		f = fcheck_files(curr_files, curr_fd);
180 		if (!f)
181 			continue;
182 		if (!get_file_rcu(f))
183 			continue;
184 
185 		/* set info->fd */
186 		info->fd = curr_fd;
187 		rcu_read_unlock();
188 		return f;
189 	}
190 
191 	/* the current task is done, go to the next task */
192 	rcu_read_unlock();
193 	put_files_struct(curr_files);
194 	put_task_struct(curr_task);
195 	*task = NULL;
196 	*fstruct = NULL;
197 	info->fd = 0;
198 	curr_tid = ++(info->tid);
199 	goto again;
200 }
201 
202 static void *task_file_seq_start(struct seq_file *seq, loff_t *pos)
203 {
204 	struct bpf_iter_seq_task_file_info *info = seq->private;
205 	struct files_struct *files = NULL;
206 	struct task_struct *task = NULL;
207 	struct file *file;
208 
209 	file = task_file_seq_get_next(info, &task, &files);
210 	if (!file) {
211 		info->files = NULL;
212 		info->task = NULL;
213 		return NULL;
214 	}
215 
216 	if (*pos == 0)
217 		++*pos;
218 	info->task = task;
219 	info->files = files;
220 
221 	return file;
222 }
223 
224 static void *task_file_seq_next(struct seq_file *seq, void *v, loff_t *pos)
225 {
226 	struct bpf_iter_seq_task_file_info *info = seq->private;
227 	struct files_struct *files = info->files;
228 	struct task_struct *task = info->task;
229 	struct file *file;
230 
231 	++*pos;
232 	++info->fd;
233 	fput((struct file *)v);
234 	file = task_file_seq_get_next(info, &task, &files);
235 	if (!file) {
236 		info->files = NULL;
237 		info->task = NULL;
238 		return NULL;
239 	}
240 
241 	info->task = task;
242 	info->files = files;
243 
244 	return file;
245 }
246 
247 struct bpf_iter__task_file {
248 	__bpf_md_ptr(struct bpf_iter_meta *, meta);
249 	__bpf_md_ptr(struct task_struct *, task);
250 	u32 fd __aligned(8);
251 	__bpf_md_ptr(struct file *, file);
252 };
253 
254 DEFINE_BPF_ITER_FUNC(task_file, struct bpf_iter_meta *meta,
255 		     struct task_struct *task, u32 fd,
256 		     struct file *file)
257 
258 static int __task_file_seq_show(struct seq_file *seq, struct file *file,
259 				bool in_stop)
260 {
261 	struct bpf_iter_seq_task_file_info *info = seq->private;
262 	struct bpf_iter__task_file ctx;
263 	struct bpf_iter_meta meta;
264 	struct bpf_prog *prog;
265 
266 	meta.seq = seq;
267 	prog = bpf_iter_get_info(&meta, in_stop);
268 	if (!prog)
269 		return 0;
270 
271 	ctx.meta = &meta;
272 	ctx.task = info->task;
273 	ctx.fd = info->fd;
274 	ctx.file = file;
275 	return bpf_iter_run_prog(prog, &ctx);
276 }
277 
278 static int task_file_seq_show(struct seq_file *seq, void *v)
279 {
280 	return __task_file_seq_show(seq, v, false);
281 }
282 
283 static void task_file_seq_stop(struct seq_file *seq, void *v)
284 {
285 	struct bpf_iter_seq_task_file_info *info = seq->private;
286 
287 	if (!v) {
288 		(void)__task_file_seq_show(seq, v, true);
289 	} else {
290 		fput((struct file *)v);
291 		put_files_struct(info->files);
292 		put_task_struct(info->task);
293 		info->files = NULL;
294 		info->task = NULL;
295 	}
296 }
297 
298 static int init_seq_pidns(void *priv_data, struct bpf_iter_aux_info *aux)
299 {
300 	struct bpf_iter_seq_task_common *common = priv_data;
301 
302 	common->ns = get_pid_ns(task_active_pid_ns(current));
303 	return 0;
304 }
305 
306 static void fini_seq_pidns(void *priv_data)
307 {
308 	struct bpf_iter_seq_task_common *common = priv_data;
309 
310 	put_pid_ns(common->ns);
311 }
312 
313 static const struct seq_operations task_file_seq_ops = {
314 	.start	= task_file_seq_start,
315 	.next	= task_file_seq_next,
316 	.stop	= task_file_seq_stop,
317 	.show	= task_file_seq_show,
318 };
319 
320 BTF_ID_LIST(btf_task_file_ids)
321 BTF_ID(struct, task_struct)
322 BTF_ID(struct, file)
323 
324 static const struct bpf_iter_seq_info task_seq_info = {
325 	.seq_ops		= &task_seq_ops,
326 	.init_seq_private	= init_seq_pidns,
327 	.fini_seq_private	= fini_seq_pidns,
328 	.seq_priv_size		= sizeof(struct bpf_iter_seq_task_info),
329 };
330 
331 static struct bpf_iter_reg task_reg_info = {
332 	.target			= "task",
333 	.ctx_arg_info_size	= 1,
334 	.ctx_arg_info		= {
335 		{ offsetof(struct bpf_iter__task, task),
336 		  PTR_TO_BTF_ID_OR_NULL },
337 	},
338 	.seq_info		= &task_seq_info,
339 };
340 
341 static const struct bpf_iter_seq_info task_file_seq_info = {
342 	.seq_ops		= &task_file_seq_ops,
343 	.init_seq_private	= init_seq_pidns,
344 	.fini_seq_private	= fini_seq_pidns,
345 	.seq_priv_size		= sizeof(struct bpf_iter_seq_task_file_info),
346 };
347 
348 static struct bpf_iter_reg task_file_reg_info = {
349 	.target			= "task_file",
350 	.ctx_arg_info_size	= 2,
351 	.ctx_arg_info		= {
352 		{ offsetof(struct bpf_iter__task_file, task),
353 		  PTR_TO_BTF_ID_OR_NULL },
354 		{ offsetof(struct bpf_iter__task_file, file),
355 		  PTR_TO_BTF_ID_OR_NULL },
356 	},
357 	.seq_info		= &task_file_seq_info,
358 };
359 
360 static int __init task_iter_init(void)
361 {
362 	int ret;
363 
364 	task_reg_info.ctx_arg_info[0].btf_id = btf_task_file_ids[0];
365 	ret = bpf_iter_reg_target(&task_reg_info);
366 	if (ret)
367 		return ret;
368 
369 	task_file_reg_info.ctx_arg_info[0].btf_id = btf_task_file_ids[0];
370 	task_file_reg_info.ctx_arg_info[1].btf_id = btf_task_file_ids[1];
371 	return bpf_iter_reg_target(&task_file_reg_info);
372 }
373 late_initcall(task_iter_init);
374