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