123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- // SPDX-License-Identifier: GPL-2.0-only
- /* Copyright (c) 2020 Facebook */
- #include <linux/init.h>
- #include <linux/namei.h>
- #include <linux/pid_namespace.h>
- #include <linux/fs.h>
- #include <linux/fdtable.h>
- #include <linux/filter.h>
- #include <linux/btf_ids.h>
- struct bpf_iter_seq_task_common {
- struct pid_namespace *ns;
- };
- struct bpf_iter_seq_task_info {
- /* The first field must be struct bpf_iter_seq_task_common.
- * this is assumed by {init, fini}_seq_pidns() callback functions.
- */
- struct bpf_iter_seq_task_common common;
- u32 tid;
- };
- static struct task_struct *task_seq_get_next(struct pid_namespace *ns,
- u32 *tid,
- bool skip_if_dup_files)
- {
- struct task_struct *task = NULL;
- struct pid *pid;
- rcu_read_lock();
- retry:
- pid = find_ge_pid(*tid, ns);
- if (pid) {
- *tid = pid_nr_ns(pid, ns);
- task = get_pid_task(pid, PIDTYPE_PID);
- if (!task) {
- ++*tid;
- goto retry;
- } else if (skip_if_dup_files && task->tgid != task->pid &&
- task->files == task->group_leader->files) {
- put_task_struct(task);
- task = NULL;
- ++*tid;
- goto retry;
- }
- }
- rcu_read_unlock();
- return task;
- }
- static void *task_seq_start(struct seq_file *seq, loff_t *pos)
- {
- struct bpf_iter_seq_task_info *info = seq->private;
- struct task_struct *task;
- task = task_seq_get_next(info->common.ns, &info->tid, false);
- if (!task)
- return NULL;
- if (*pos == 0)
- ++*pos;
- return task;
- }
- static void *task_seq_next(struct seq_file *seq, void *v, loff_t *pos)
- {
- struct bpf_iter_seq_task_info *info = seq->private;
- struct task_struct *task;
- ++*pos;
- ++info->tid;
- put_task_struct((struct task_struct *)v);
- task = task_seq_get_next(info->common.ns, &info->tid, false);
- if (!task)
- return NULL;
- return task;
- }
- struct bpf_iter__task {
- __bpf_md_ptr(struct bpf_iter_meta *, meta);
- __bpf_md_ptr(struct task_struct *, task);
- };
- DEFINE_BPF_ITER_FUNC(task, struct bpf_iter_meta *meta, struct task_struct *task)
- static int __task_seq_show(struct seq_file *seq, struct task_struct *task,
- bool in_stop)
- {
- struct bpf_iter_meta meta;
- struct bpf_iter__task ctx;
- struct bpf_prog *prog;
- meta.seq = seq;
- prog = bpf_iter_get_info(&meta, in_stop);
- if (!prog)
- return 0;
- meta.seq = seq;
- ctx.meta = &meta;
- ctx.task = task;
- return bpf_iter_run_prog(prog, &ctx);
- }
- static int task_seq_show(struct seq_file *seq, void *v)
- {
- return __task_seq_show(seq, v, false);
- }
- static void task_seq_stop(struct seq_file *seq, void *v)
- {
- if (!v)
- (void)__task_seq_show(seq, v, true);
- else
- put_task_struct((struct task_struct *)v);
- }
- static const struct seq_operations task_seq_ops = {
- .start = task_seq_start,
- .next = task_seq_next,
- .stop = task_seq_stop,
- .show = task_seq_show,
- };
- struct bpf_iter_seq_task_file_info {
- /* The first field must be struct bpf_iter_seq_task_common.
- * this is assumed by {init, fini}_seq_pidns() callback functions.
- */
- struct bpf_iter_seq_task_common common;
- struct task_struct *task;
- struct files_struct *files;
- u32 tid;
- u32 fd;
- };
- static struct file *
- task_file_seq_get_next(struct bpf_iter_seq_task_file_info *info)
- {
- struct pid_namespace *ns = info->common.ns;
- u32 curr_tid = info->tid, max_fds;
- struct files_struct *curr_files;
- struct task_struct *curr_task;
- int curr_fd = info->fd;
- /* If this function returns a non-NULL file object,
- * it held a reference to the task/files_struct/file.
- * Otherwise, it does not hold any reference.
- */
- again:
- if (info->task) {
- curr_task = info->task;
- curr_files = info->files;
- curr_fd = info->fd;
- } else {
- curr_task = task_seq_get_next(ns, &curr_tid, true);
- if (!curr_task) {
- info->task = NULL;
- info->files = NULL;
- info->tid = curr_tid;
- return NULL;
- }
- curr_files = get_files_struct(curr_task);
- if (!curr_files) {
- put_task_struct(curr_task);
- curr_tid = curr_tid + 1;
- info->fd = 0;
- goto again;
- }
- info->files = curr_files;
- info->task = curr_task;
- if (curr_tid == info->tid) {
- curr_fd = info->fd;
- } else {
- info->tid = curr_tid;
- curr_fd = 0;
- }
- }
- rcu_read_lock();
- max_fds = files_fdtable(curr_files)->max_fds;
- for (; curr_fd < max_fds; curr_fd++) {
- struct file *f;
- f = fcheck_files(curr_files, curr_fd);
- if (!f)
- continue;
- if (!get_file_rcu(f))
- continue;
- /* set info->fd */
- info->fd = curr_fd;
- rcu_read_unlock();
- return f;
- }
- /* the current task is done, go to the next task */
- rcu_read_unlock();
- put_files_struct(curr_files);
- put_task_struct(curr_task);
- info->task = NULL;
- info->files = NULL;
- info->fd = 0;
- curr_tid = ++(info->tid);
- goto again;
- }
- static void *task_file_seq_start(struct seq_file *seq, loff_t *pos)
- {
- struct bpf_iter_seq_task_file_info *info = seq->private;
- struct file *file;
- info->task = NULL;
- info->files = NULL;
- file = task_file_seq_get_next(info);
- if (file && *pos == 0)
- ++*pos;
- return file;
- }
- static void *task_file_seq_next(struct seq_file *seq, void *v, loff_t *pos)
- {
- struct bpf_iter_seq_task_file_info *info = seq->private;
- ++*pos;
- ++info->fd;
- fput((struct file *)v);
- return task_file_seq_get_next(info);
- }
- struct bpf_iter__task_file {
- __bpf_md_ptr(struct bpf_iter_meta *, meta);
- __bpf_md_ptr(struct task_struct *, task);
- u32 fd __aligned(8);
- __bpf_md_ptr(struct file *, file);
- };
- DEFINE_BPF_ITER_FUNC(task_file, struct bpf_iter_meta *meta,
- struct task_struct *task, u32 fd,
- struct file *file)
- static int __task_file_seq_show(struct seq_file *seq, struct file *file,
- bool in_stop)
- {
- struct bpf_iter_seq_task_file_info *info = seq->private;
- struct bpf_iter__task_file ctx;
- struct bpf_iter_meta meta;
- struct bpf_prog *prog;
- meta.seq = seq;
- prog = bpf_iter_get_info(&meta, in_stop);
- if (!prog)
- return 0;
- ctx.meta = &meta;
- ctx.task = info->task;
- ctx.fd = info->fd;
- ctx.file = file;
- return bpf_iter_run_prog(prog, &ctx);
- }
- static int task_file_seq_show(struct seq_file *seq, void *v)
- {
- return __task_file_seq_show(seq, v, false);
- }
- static void task_file_seq_stop(struct seq_file *seq, void *v)
- {
- struct bpf_iter_seq_task_file_info *info = seq->private;
- if (!v) {
- (void)__task_file_seq_show(seq, v, true);
- } else {
- fput((struct file *)v);
- put_files_struct(info->files);
- put_task_struct(info->task);
- info->files = NULL;
- info->task = NULL;
- }
- }
- static int init_seq_pidns(void *priv_data, struct bpf_iter_aux_info *aux)
- {
- struct bpf_iter_seq_task_common *common = priv_data;
- common->ns = get_pid_ns(task_active_pid_ns(current));
- return 0;
- }
- static void fini_seq_pidns(void *priv_data)
- {
- struct bpf_iter_seq_task_common *common = priv_data;
- put_pid_ns(common->ns);
- }
- static const struct seq_operations task_file_seq_ops = {
- .start = task_file_seq_start,
- .next = task_file_seq_next,
- .stop = task_file_seq_stop,
- .show = task_file_seq_show,
- };
- BTF_ID_LIST(btf_task_file_ids)
- BTF_ID(struct, task_struct)
- BTF_ID(struct, file)
- static const struct bpf_iter_seq_info task_seq_info = {
- .seq_ops = &task_seq_ops,
- .init_seq_private = init_seq_pidns,
- .fini_seq_private = fini_seq_pidns,
- .seq_priv_size = sizeof(struct bpf_iter_seq_task_info),
- };
- static struct bpf_iter_reg task_reg_info = {
- .target = "task",
- .ctx_arg_info_size = 1,
- .ctx_arg_info = {
- { offsetof(struct bpf_iter__task, task),
- PTR_TO_BTF_ID_OR_NULL },
- },
- .seq_info = &task_seq_info,
- };
- static const struct bpf_iter_seq_info task_file_seq_info = {
- .seq_ops = &task_file_seq_ops,
- .init_seq_private = init_seq_pidns,
- .fini_seq_private = fini_seq_pidns,
- .seq_priv_size = sizeof(struct bpf_iter_seq_task_file_info),
- };
- static struct bpf_iter_reg task_file_reg_info = {
- .target = "task_file",
- .ctx_arg_info_size = 2,
- .ctx_arg_info = {
- { offsetof(struct bpf_iter__task_file, task),
- PTR_TO_BTF_ID_OR_NULL },
- { offsetof(struct bpf_iter__task_file, file),
- PTR_TO_BTF_ID_OR_NULL },
- },
- .seq_info = &task_file_seq_info,
- };
- static int __init task_iter_init(void)
- {
- int ret;
- task_reg_info.ctx_arg_info[0].btf_id = btf_task_file_ids[0];
- ret = bpf_iter_reg_target(&task_reg_info);
- if (ret)
- return ret;
- task_file_reg_info.ctx_arg_info[0].btf_id = btf_task_file_ids[0];
- task_file_reg_info.ctx_arg_info[1].btf_id = btf_task_file_ids[1];
- return bpf_iter_reg_target(&task_file_reg_info);
- }
- late_initcall(task_iter_init);
|