forked from Minki/linux
faf145d6f3
Pull execve updates from Eric Biederman: "This set of changes ultimately fixes the interaction of posix file lock and exec. Fundamentally most of the change is just moving where unshare_files is called during exec, and tweaking the users of files_struct so that the count of files_struct is not unnecessarily played with. Along the way fcheck and related helpers were renamed to more accurately reflect what they do. There were also many other small changes that fell out, as this is the first time in a long time much of this code has been touched. Benchmarks haven't turned up any practical issues but Al Viro has observed a possibility for a lot of pounding on task_lock. So I have some changes in progress to convert put_files_struct to always rcu free files_struct. That wasn't ready for the merge window so that will have to wait until next time" * 'exec-for-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace: (27 commits) exec: Move io_uring_task_cancel after the point of no return coredump: Document coredump code exclusively used by cell spufs file: Remove get_files_struct file: Rename __close_fd_get_file close_fd_get_file file: Replace ksys_close with close_fd file: Rename __close_fd to close_fd and remove the files parameter file: Merge __alloc_fd into alloc_fd file: In f_dupfd read RLIMIT_NOFILE once. file: Merge __fd_install into fd_install proc/fd: In fdinfo seq_show don't use get_files_struct bpf/task_iter: In task_file_seq_get_next use task_lookup_next_fd_rcu proc/fd: In proc_readfd_common use task_lookup_next_fd_rcu file: Implement task_lookup_next_fd_rcu kcmp: In get_file_raw_ptr use task_lookup_fd_rcu proc/fd: In tid_fd_mode use task_lookup_fd_rcu file: Implement task_lookup_fd_rcu file: Rename fcheck lookup_fd_rcu file: Replace fcheck_files with files_lookup_fd_rcu file: Factor files_lookup_fd_locked out of fcheck_files file: Rename __fcheck_files to files_lookup_fd_raw ...
344 lines
7.6 KiB
C
344 lines
7.6 KiB
C
// 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;
|
|
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;
|
|
struct task_struct *curr_task;
|
|
unsigned int curr_fd = info->fd;
|
|
|
|
/* If this function returns a non-NULL file object,
|
|
* it held a reference to the task/file.
|
|
* Otherwise, it does not hold any reference.
|
|
*/
|
|
again:
|
|
if (info->task) {
|
|
curr_task = info->task;
|
|
curr_fd = info->fd;
|
|
} else {
|
|
curr_task = task_seq_get_next(ns, &curr_tid, true);
|
|
if (!curr_task) {
|
|
info->task = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
/* set info->task and info->tid */
|
|
info->task = curr_task;
|
|
if (curr_tid == info->tid) {
|
|
curr_fd = info->fd;
|
|
} else {
|
|
info->tid = curr_tid;
|
|
curr_fd = 0;
|
|
}
|
|
}
|
|
|
|
rcu_read_lock();
|
|
for (;; curr_fd++) {
|
|
struct file *f;
|
|
f = task_lookup_next_fd_rcu(curr_task, &curr_fd);
|
|
if (!f)
|
|
break;
|
|
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_task_struct(curr_task);
|
|
info->task = 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;
|
|
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_task_struct(info->task);
|
|
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",
|
|
.feature = BPF_ITER_RESCHED,
|
|
.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",
|
|
.feature = BPF_ITER_RESCHED,
|
|
.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);
|