Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Alexei Starovoitov says: ==================== pull-request: bpf-next 2020-07-14 The following pull-request contains BPF updates for your *net-next* tree. We've added 21 non-merge commits during the last 1 day(s) which contain a total of 20 files changed, 308 insertions(+), 279 deletions(-). The main changes are: 1) Fix selftests/bpf build, from Alexei. 2) Fix resolve_btfids build issues, from Jiri. 3) Pull usermode-driver-cleanup set, from Eric. 4) Two minor fixes to bpfilter, from Alexei and Masahiro. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
df8201cc8b
38
fs/exec.c
38
fs/exec.c
|
@ -1818,13 +1818,14 @@ static int exec_binprm(struct linux_binprm *bprm)
|
|||
/*
|
||||
* sys_execve() executes a new program.
|
||||
*/
|
||||
static int __do_execve_file(int fd, struct filename *filename,
|
||||
struct user_arg_ptr argv,
|
||||
struct user_arg_ptr envp,
|
||||
int flags, struct file *file)
|
||||
static int do_execveat_common(int fd, struct filename *filename,
|
||||
struct user_arg_ptr argv,
|
||||
struct user_arg_ptr envp,
|
||||
int flags)
|
||||
{
|
||||
char *pathbuf = NULL;
|
||||
struct linux_binprm *bprm;
|
||||
struct file *file;
|
||||
struct files_struct *displaced;
|
||||
int retval;
|
||||
|
||||
|
@ -1863,8 +1864,7 @@ static int __do_execve_file(int fd, struct filename *filename,
|
|||
check_unsafe_exec(bprm);
|
||||
current->in_execve = 1;
|
||||
|
||||
if (!file)
|
||||
file = do_open_execat(fd, filename, flags);
|
||||
file = do_open_execat(fd, filename, flags);
|
||||
retval = PTR_ERR(file);
|
||||
if (IS_ERR(file))
|
||||
goto out_unmark;
|
||||
|
@ -1872,9 +1872,7 @@ static int __do_execve_file(int fd, struct filename *filename,
|
|||
sched_exec();
|
||||
|
||||
bprm->file = file;
|
||||
if (!filename) {
|
||||
bprm->filename = "none";
|
||||
} else if (fd == AT_FDCWD || filename->name[0] == '/') {
|
||||
if (fd == AT_FDCWD || filename->name[0] == '/') {
|
||||
bprm->filename = filename->name;
|
||||
} else {
|
||||
if (filename->name[0] == '\0')
|
||||
|
@ -1935,8 +1933,7 @@ static int __do_execve_file(int fd, struct filename *filename,
|
|||
task_numa_free(current, false);
|
||||
free_bprm(bprm);
|
||||
kfree(pathbuf);
|
||||
if (filename)
|
||||
putname(filename);
|
||||
putname(filename);
|
||||
if (displaced)
|
||||
put_files_struct(displaced);
|
||||
return retval;
|
||||
|
@ -1967,27 +1964,10 @@ out_files:
|
|||
if (displaced)
|
||||
reset_files_struct(displaced);
|
||||
out_ret:
|
||||
if (filename)
|
||||
putname(filename);
|
||||
putname(filename);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int do_execveat_common(int fd, struct filename *filename,
|
||||
struct user_arg_ptr argv,
|
||||
struct user_arg_ptr envp,
|
||||
int flags)
|
||||
{
|
||||
return __do_execve_file(fd, filename, argv, envp, flags, NULL);
|
||||
}
|
||||
|
||||
int do_execve_file(struct file *file, void *__argv, void *__envp)
|
||||
{
|
||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||
struct user_arg_ptr envp = { .ptr.native = __envp };
|
||||
|
||||
return __do_execve_file(AT_FDCWD, NULL, argv, envp, 0, file);
|
||||
}
|
||||
|
||||
int do_execve(struct filename *filename,
|
||||
const char __user *const __user *__argv,
|
||||
const char __user *const __user *__envp)
|
||||
|
|
|
@ -141,6 +141,5 @@ extern int do_execveat(int, struct filename *,
|
|||
const char __user * const __user *,
|
||||
const char __user * const __user *,
|
||||
int);
|
||||
int do_execve_file(struct file *file, void *__argv, void *__envp);
|
||||
|
||||
#endif /* _LINUX_BINFMTS_H */
|
||||
|
|
|
@ -3,22 +3,23 @@
|
|||
#define _LINUX_BPFILTER_H
|
||||
|
||||
#include <uapi/linux/bpfilter.h>
|
||||
#include <linux/umh.h>
|
||||
#include <linux/usermode_driver.h>
|
||||
|
||||
struct sock;
|
||||
int bpfilter_ip_set_sockopt(struct sock *sk, int optname, char __user *optval,
|
||||
unsigned int optlen);
|
||||
int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char __user *optval,
|
||||
int __user *optlen);
|
||||
void bpfilter_umh_cleanup(struct umd_info *info);
|
||||
|
||||
struct bpfilter_umh_ops {
|
||||
struct umh_info info;
|
||||
struct umd_info info;
|
||||
/* since ip_getsockopt() can run in parallel, serialize access to umh */
|
||||
struct mutex lock;
|
||||
int (*sockopt)(struct sock *sk, int optname,
|
||||
char __user *optval,
|
||||
unsigned int optlen, bool is_set);
|
||||
int (*start)(void);
|
||||
bool stop;
|
||||
};
|
||||
extern struct bpfilter_umh_ops bpfilter_ops;
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#ifndef _LINUX_BTF_IDS_H
|
||||
#define _LINUX_BTF_IDS_H
|
||||
|
||||
#ifdef CONFIG_DEBUG_INFO_BTF
|
||||
|
||||
#include <linux/compiler.h> /* for __PASTE */
|
||||
|
||||
/*
|
||||
|
@ -21,7 +23,7 @@
|
|||
asm( \
|
||||
".pushsection " BTF_IDS_SECTION ",\"a\"; \n" \
|
||||
".local " #symbol " ; \n" \
|
||||
".type " #symbol ", @object; \n" \
|
||||
".type " #symbol ", STT_OBJECT; \n" \
|
||||
".size " #symbol ", 4; \n" \
|
||||
#symbol ": \n" \
|
||||
".zero 4 \n" \
|
||||
|
@ -83,5 +85,12 @@ asm( \
|
|||
".zero 4 \n" \
|
||||
".popsection; \n");
|
||||
|
||||
#else
|
||||
|
||||
#define BTF_ID_LIST(name) static u32 name[5];
|
||||
#define BTF_ID(prefix, name)
|
||||
#define BTF_ID_UNUSED
|
||||
|
||||
#endif /* CONFIG_DEBUG_INFO_BTF */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1510,7 +1510,6 @@ extern struct pid *cad_pid;
|
|||
#define PF_KTHREAD 0x00200000 /* I am a kernel thread */
|
||||
#define PF_RANDOMIZE 0x00400000 /* Randomize virtual address space */
|
||||
#define PF_SWAPWRITE 0x00800000 /* Allowed to write to swap */
|
||||
#define PF_UMH 0x02000000 /* I'm an Usermodehelper process */
|
||||
#define PF_NO_SETAFFINITY 0x04000000 /* Userland is not allowed to meddle with cpus_mask */
|
||||
#define PF_MCE_EARLY 0x08000000 /* Early kill for mce process policy */
|
||||
#define PF_MEMALLOC_NOCMA 0x10000000 /* All allocation request will have _GFP_MOVABLE cleared */
|
||||
|
@ -2019,14 +2018,6 @@ static inline void rseq_execve(struct task_struct *t)
|
|||
|
||||
#endif
|
||||
|
||||
void __exit_umh(struct task_struct *tsk);
|
||||
|
||||
static inline void exit_umh(struct task_struct *tsk)
|
||||
{
|
||||
if (unlikely(tsk->flags & PF_UMH))
|
||||
__exit_umh(tsk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_RSEQ
|
||||
|
||||
void rseq_syscall(struct pt_regs *regs);
|
||||
|
|
|
@ -674,6 +674,8 @@ static inline int thread_group_empty(struct task_struct *p)
|
|||
#define delay_group_leader(p) \
|
||||
(thread_group_leader(p) && !thread_group_empty(p))
|
||||
|
||||
extern bool thread_group_exited(struct pid *pid);
|
||||
|
||||
extern struct sighand_struct *__lock_task_sighand(struct task_struct *task,
|
||||
unsigned long *flags);
|
||||
|
||||
|
|
|
@ -22,10 +22,8 @@ struct subprocess_info {
|
|||
const char *path;
|
||||
char **argv;
|
||||
char **envp;
|
||||
struct file *file;
|
||||
int wait;
|
||||
int retval;
|
||||
pid_t pid;
|
||||
int (*init)(struct subprocess_info *info, struct cred *new);
|
||||
void (*cleanup)(struct subprocess_info *info);
|
||||
void *data;
|
||||
|
@ -40,19 +38,6 @@ call_usermodehelper_setup(const char *path, char **argv, char **envp,
|
|||
int (*init)(struct subprocess_info *info, struct cred *new),
|
||||
void (*cleanup)(struct subprocess_info *), void *data);
|
||||
|
||||
struct subprocess_info *call_usermodehelper_setup_file(struct file *file,
|
||||
int (*init)(struct subprocess_info *info, struct cred *new),
|
||||
void (*cleanup)(struct subprocess_info *), void *data);
|
||||
struct umh_info {
|
||||
const char *cmdline;
|
||||
struct file *pipe_to_umh;
|
||||
struct file *pipe_from_umh;
|
||||
struct list_head list;
|
||||
void (*cleanup)(struct umh_info *info);
|
||||
pid_t pid;
|
||||
};
|
||||
int fork_usermode_blob(void *data, size_t len, struct umh_info *info);
|
||||
|
||||
extern int
|
||||
call_usermodehelper_exec(struct subprocess_info *info, int wait);
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef __LINUX_USERMODE_DRIVER_H__
|
||||
#define __LINUX_USERMODE_DRIVER_H__
|
||||
|
||||
#include <linux/umh.h>
|
||||
#include <linux/path.h>
|
||||
|
||||
struct umd_info {
|
||||
const char *driver_name;
|
||||
struct file *pipe_to_umh;
|
||||
struct file *pipe_from_umh;
|
||||
struct path wd;
|
||||
struct pid *tgid;
|
||||
};
|
||||
int umd_load_blob(struct umd_info *info, const void *data, size_t len);
|
||||
int umd_unload_blob(struct umd_info *info);
|
||||
int fork_usermode_driver(struct umd_info *info);
|
||||
|
||||
#endif /* __LINUX_USERMODE_DRIVER_H__ */
|
|
@ -12,6 +12,7 @@ obj-y = fork.o exec_domain.o panic.o \
|
|||
notifier.o ksysfs.o cred.o reboot.o \
|
||||
async.o range.o smpboot.o ucount.o
|
||||
|
||||
obj-$(CONFIG_BPFILTER) += usermode_driver.o
|
||||
obj-$(CONFIG_MODULES) += kmod.o
|
||||
obj-$(CONFIG_MULTIUSER) += groups.o
|
||||
|
||||
|
|
|
@ -804,7 +804,6 @@ void __noreturn do_exit(long code)
|
|||
exit_task_namespaces(tsk);
|
||||
exit_task_work(tsk);
|
||||
exit_thread(tsk);
|
||||
exit_umh(tsk);
|
||||
|
||||
/*
|
||||
* Flush inherited counters to the parent - before the parent
|
||||
|
@ -1711,6 +1710,30 @@ Efault:
|
|||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* thread_group_exited - check that a thread group has exited
|
||||
* @pid: tgid of thread group to be checked.
|
||||
*
|
||||
* Test if the thread group represented by tgid has exited (all
|
||||
* threads are zombies, dead or completely gone).
|
||||
*
|
||||
* Return: true if the thread group has exited. false otherwise.
|
||||
*/
|
||||
bool thread_group_exited(struct pid *pid)
|
||||
{
|
||||
struct task_struct *task;
|
||||
bool exited;
|
||||
|
||||
rcu_read_lock();
|
||||
task = pid_task(pid, PIDTYPE_PID);
|
||||
exited = !task ||
|
||||
(READ_ONCE(task->exit_state) && thread_group_empty(task));
|
||||
rcu_read_unlock();
|
||||
|
||||
return exited;
|
||||
}
|
||||
EXPORT_SYMBOL(thread_group_exited);
|
||||
|
||||
__weak void abort(void)
|
||||
{
|
||||
BUG();
|
||||
|
|
|
@ -1787,22 +1787,18 @@ static void pidfd_show_fdinfo(struct seq_file *m, struct file *f)
|
|||
*/
|
||||
static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts)
|
||||
{
|
||||
struct task_struct *task;
|
||||
struct pid *pid = file->private_data;
|
||||
__poll_t poll_flags = 0;
|
||||
|
||||
poll_wait(file, &pid->wait_pidfd, pts);
|
||||
|
||||
rcu_read_lock();
|
||||
task = pid_task(pid, PIDTYPE_PID);
|
||||
/*
|
||||
* Inform pollers only when the whole thread group exits.
|
||||
* If the thread group leader exits before all other threads in the
|
||||
* group, then poll(2) should block, similar to the wait(2) family.
|
||||
*/
|
||||
if (!task || (task->exit_state && thread_group_empty(task)))
|
||||
if (thread_group_exited(pid))
|
||||
poll_flags = EPOLLIN | EPOLLRDNORM;
|
||||
rcu_read_unlock();
|
||||
|
||||
return poll_flags;
|
||||
}
|
||||
|
|
171
kernel/umh.c
171
kernel/umh.c
|
@ -26,8 +26,6 @@
|
|||
#include <linux/ptrace.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/pipe_fs_i.h>
|
||||
|
||||
#include <trace/events/module.h>
|
||||
|
||||
|
@ -38,8 +36,6 @@ static kernel_cap_t usermodehelper_bset = CAP_FULL_SET;
|
|||
static kernel_cap_t usermodehelper_inheritable = CAP_FULL_SET;
|
||||
static DEFINE_SPINLOCK(umh_sysctl_lock);
|
||||
static DECLARE_RWSEM(umhelper_sem);
|
||||
static LIST_HEAD(umh_list);
|
||||
static DEFINE_MUTEX(umh_list_lock);
|
||||
|
||||
static void call_usermodehelper_freeinfo(struct subprocess_info *info)
|
||||
{
|
||||
|
@ -102,16 +98,9 @@ static int call_usermodehelper_exec_async(void *data)
|
|||
|
||||
commit_creds(new);
|
||||
|
||||
sub_info->pid = task_pid_nr(current);
|
||||
if (sub_info->file) {
|
||||
retval = do_execve_file(sub_info->file,
|
||||
sub_info->argv, sub_info->envp);
|
||||
if (!retval)
|
||||
current->flags |= PF_UMH;
|
||||
} else
|
||||
retval = do_execve(getname_kernel(sub_info->path),
|
||||
(const char __user *const __user *)sub_info->argv,
|
||||
(const char __user *const __user *)sub_info->envp);
|
||||
retval = do_execve(getname_kernel(sub_info->path),
|
||||
(const char __user *const __user *)sub_info->argv,
|
||||
(const char __user *const __user *)sub_info->envp);
|
||||
out:
|
||||
sub_info->retval = retval;
|
||||
/*
|
||||
|
@ -405,140 +394,6 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
|
|||
}
|
||||
EXPORT_SYMBOL(call_usermodehelper_setup);
|
||||
|
||||
struct subprocess_info *call_usermodehelper_setup_file(struct file *file,
|
||||
int (*init)(struct subprocess_info *info, struct cred *new),
|
||||
void (*cleanup)(struct subprocess_info *info), void *data)
|
||||
{
|
||||
struct subprocess_info *sub_info;
|
||||
struct umh_info *info = data;
|
||||
const char *cmdline = (info->cmdline) ? info->cmdline : "usermodehelper";
|
||||
|
||||
sub_info = kzalloc(sizeof(struct subprocess_info), GFP_KERNEL);
|
||||
if (!sub_info)
|
||||
return NULL;
|
||||
|
||||
sub_info->argv = argv_split(GFP_KERNEL, cmdline, NULL);
|
||||
if (!sub_info->argv) {
|
||||
kfree(sub_info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);
|
||||
sub_info->path = "none";
|
||||
sub_info->file = file;
|
||||
sub_info->init = init;
|
||||
sub_info->cleanup = cleanup;
|
||||
sub_info->data = data;
|
||||
return sub_info;
|
||||
}
|
||||
|
||||
static int umh_pipe_setup(struct subprocess_info *info, struct cred *new)
|
||||
{
|
||||
struct umh_info *umh_info = info->data;
|
||||
struct file *from_umh[2];
|
||||
struct file *to_umh[2];
|
||||
int err;
|
||||
|
||||
/* create pipe to send data to umh */
|
||||
err = create_pipe_files(to_umh, 0);
|
||||
if (err)
|
||||
return err;
|
||||
err = replace_fd(0, to_umh[0], 0);
|
||||
fput(to_umh[0]);
|
||||
if (err < 0) {
|
||||
fput(to_umh[1]);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* create pipe to receive data from umh */
|
||||
err = create_pipe_files(from_umh, 0);
|
||||
if (err) {
|
||||
fput(to_umh[1]);
|
||||
replace_fd(0, NULL, 0);
|
||||
return err;
|
||||
}
|
||||
err = replace_fd(1, from_umh[1], 0);
|
||||
fput(from_umh[1]);
|
||||
if (err < 0) {
|
||||
fput(to_umh[1]);
|
||||
replace_fd(0, NULL, 0);
|
||||
fput(from_umh[0]);
|
||||
return err;
|
||||
}
|
||||
|
||||
umh_info->pipe_to_umh = to_umh[1];
|
||||
umh_info->pipe_from_umh = from_umh[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void umh_clean_and_save_pid(struct subprocess_info *info)
|
||||
{
|
||||
struct umh_info *umh_info = info->data;
|
||||
|
||||
/* cleanup if umh_pipe_setup() was successful but exec failed */
|
||||
if (info->pid && info->retval) {
|
||||
fput(umh_info->pipe_to_umh);
|
||||
fput(umh_info->pipe_from_umh);
|
||||
}
|
||||
|
||||
argv_free(info->argv);
|
||||
umh_info->pid = info->pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* fork_usermode_blob - fork a blob of bytes as a usermode process
|
||||
* @data: a blob of bytes that can be do_execv-ed as a file
|
||||
* @len: length of the blob
|
||||
* @info: information about usermode process (shouldn't be NULL)
|
||||
*
|
||||
* If info->cmdline is set it will be used as command line for the
|
||||
* user process, else "usermodehelper" is used.
|
||||
*
|
||||
* Returns either negative error or zero which indicates success
|
||||
* in executing a blob of bytes as a usermode process. In such
|
||||
* case 'struct umh_info *info' is populated with two pipes
|
||||
* and a pid of the process. The caller is responsible for health
|
||||
* check of the user process, killing it via pid, and closing the
|
||||
* pipes when user process is no longer needed.
|
||||
*/
|
||||
int fork_usermode_blob(void *data, size_t len, struct umh_info *info)
|
||||
{
|
||||
struct subprocess_info *sub_info;
|
||||
struct file *file;
|
||||
ssize_t written;
|
||||
loff_t pos = 0;
|
||||
int err;
|
||||
|
||||
file = shmem_kernel_file_setup("", len, 0);
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
written = kernel_write(file, data, len, &pos);
|
||||
if (written != len) {
|
||||
err = written;
|
||||
if (err >= 0)
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
sub_info = call_usermodehelper_setup_file(file, umh_pipe_setup,
|
||||
umh_clean_and_save_pid, info);
|
||||
if (!sub_info)
|
||||
goto out;
|
||||
|
||||
err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
|
||||
if (!err) {
|
||||
mutex_lock(&umh_list_lock);
|
||||
list_add(&info->list, &umh_list);
|
||||
mutex_unlock(&umh_list_lock);
|
||||
}
|
||||
out:
|
||||
fput(file);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fork_usermode_blob);
|
||||
|
||||
/**
|
||||
* call_usermodehelper_exec - start a usermode application
|
||||
* @sub_info: information about the subprocessa
|
||||
|
@ -700,26 +555,6 @@ static int proc_cap_handler(struct ctl_table *table, int write,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void __exit_umh(struct task_struct *tsk)
|
||||
{
|
||||
struct umh_info *info;
|
||||
pid_t pid = tsk->pid;
|
||||
|
||||
mutex_lock(&umh_list_lock);
|
||||
list_for_each_entry(info, &umh_list, list) {
|
||||
if (info->pid == pid) {
|
||||
list_del(&info->list);
|
||||
mutex_unlock(&umh_list_lock);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&umh_list_lock);
|
||||
return;
|
||||
out:
|
||||
if (info->cleanup)
|
||||
info->cleanup(info);
|
||||
}
|
||||
|
||||
struct ctl_table usermodehelper_table[] = {
|
||||
{
|
||||
.procname = "bset",
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* umd - User mode driver support
|
||||
*/
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/pipe_fs_i.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/task_work.h>
|
||||
#include <linux/usermode_driver.h>
|
||||
|
||||
static struct vfsmount *blob_to_mnt(const void *data, size_t len, const char *name)
|
||||
{
|
||||
struct file_system_type *type;
|
||||
struct vfsmount *mnt;
|
||||
struct file *file;
|
||||
ssize_t written;
|
||||
loff_t pos = 0;
|
||||
|
||||
type = get_fs_type("tmpfs");
|
||||
if (!type)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
mnt = kern_mount(type);
|
||||
put_filesystem(type);
|
||||
if (IS_ERR(mnt))
|
||||
return mnt;
|
||||
|
||||
file = file_open_root(mnt->mnt_root, mnt, name, O_CREAT | O_WRONLY, 0700);
|
||||
if (IS_ERR(file)) {
|
||||
mntput(mnt);
|
||||
return ERR_CAST(file);
|
||||
}
|
||||
|
||||
written = kernel_write(file, data, len, &pos);
|
||||
if (written != len) {
|
||||
int err = written;
|
||||
if (err >= 0)
|
||||
err = -ENOMEM;
|
||||
filp_close(file, NULL);
|
||||
mntput(mnt);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
fput(file);
|
||||
|
||||
/* Flush delayed fput so exec can open the file read-only */
|
||||
flush_delayed_fput();
|
||||
task_work_run();
|
||||
return mnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* umd_load_blob - Remember a blob of bytes for fork_usermode_driver
|
||||
* @info: information about usermode driver
|
||||
* @data: a blob of bytes that can be executed as a file
|
||||
* @len: The lentgh of the blob
|
||||
*
|
||||
*/
|
||||
int umd_load_blob(struct umd_info *info, const void *data, size_t len)
|
||||
{
|
||||
struct vfsmount *mnt;
|
||||
|
||||
if (WARN_ON_ONCE(info->wd.dentry || info->wd.mnt))
|
||||
return -EBUSY;
|
||||
|
||||
mnt = blob_to_mnt(data, len, info->driver_name);
|
||||
if (IS_ERR(mnt))
|
||||
return PTR_ERR(mnt);
|
||||
|
||||
info->wd.mnt = mnt;
|
||||
info->wd.dentry = mnt->mnt_root;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(umd_load_blob);
|
||||
|
||||
/**
|
||||
* umd_unload_blob - Disassociate @info from a previously loaded blob
|
||||
* @info: information about usermode driver
|
||||
*
|
||||
*/
|
||||
int umd_unload_blob(struct umd_info *info)
|
||||
{
|
||||
if (WARN_ON_ONCE(!info->wd.mnt ||
|
||||
!info->wd.dentry ||
|
||||
info->wd.mnt->mnt_root != info->wd.dentry))
|
||||
return -EINVAL;
|
||||
|
||||
kern_unmount(info->wd.mnt);
|
||||
info->wd.mnt = NULL;
|
||||
info->wd.dentry = NULL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(umd_unload_blob);
|
||||
|
||||
static int umd_setup(struct subprocess_info *info, struct cred *new)
|
||||
{
|
||||
struct umd_info *umd_info = info->data;
|
||||
struct file *from_umh[2];
|
||||
struct file *to_umh[2];
|
||||
int err;
|
||||
|
||||
/* create pipe to send data to umh */
|
||||
err = create_pipe_files(to_umh, 0);
|
||||
if (err)
|
||||
return err;
|
||||
err = replace_fd(0, to_umh[0], 0);
|
||||
fput(to_umh[0]);
|
||||
if (err < 0) {
|
||||
fput(to_umh[1]);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* create pipe to receive data from umh */
|
||||
err = create_pipe_files(from_umh, 0);
|
||||
if (err) {
|
||||
fput(to_umh[1]);
|
||||
replace_fd(0, NULL, 0);
|
||||
return err;
|
||||
}
|
||||
err = replace_fd(1, from_umh[1], 0);
|
||||
fput(from_umh[1]);
|
||||
if (err < 0) {
|
||||
fput(to_umh[1]);
|
||||
replace_fd(0, NULL, 0);
|
||||
fput(from_umh[0]);
|
||||
return err;
|
||||
}
|
||||
|
||||
set_fs_pwd(current->fs, &umd_info->wd);
|
||||
umd_info->pipe_to_umh = to_umh[1];
|
||||
umd_info->pipe_from_umh = from_umh[0];
|
||||
umd_info->tgid = get_pid(task_tgid(current));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void umd_cleanup(struct subprocess_info *info)
|
||||
{
|
||||
struct umd_info *umd_info = info->data;
|
||||
|
||||
/* cleanup if umh_setup() was successful but exec failed */
|
||||
if (info->retval) {
|
||||
fput(umd_info->pipe_to_umh);
|
||||
fput(umd_info->pipe_from_umh);
|
||||
put_pid(umd_info->tgid);
|
||||
umd_info->tgid = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fork_usermode_driver - fork a usermode driver
|
||||
* @info: information about usermode driver (shouldn't be NULL)
|
||||
*
|
||||
* Returns either negative error or zero which indicates success in
|
||||
* executing a usermode driver. In such case 'struct umd_info *info'
|
||||
* is populated with two pipes and a tgid of the process. The caller is
|
||||
* responsible for health check of the user process, killing it via
|
||||
* tgid, and closing the pipes when user process is no longer needed.
|
||||
*/
|
||||
int fork_usermode_driver(struct umd_info *info)
|
||||
{
|
||||
struct subprocess_info *sub_info;
|
||||
const char *argv[] = { info->driver_name, NULL };
|
||||
int err;
|
||||
|
||||
if (WARN_ON_ONCE(info->tgid))
|
||||
return -EBUSY;
|
||||
|
||||
err = -ENOMEM;
|
||||
sub_info = call_usermodehelper_setup(info->driver_name,
|
||||
(char **)argv, NULL, GFP_KERNEL,
|
||||
umd_setup, umd_cleanup, info);
|
||||
if (!sub_info)
|
||||
goto out;
|
||||
|
||||
err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fork_usermode_driver);
|
||||
|
||||
|
|
@ -9,12 +9,14 @@ menuconfig BPFILTER
|
|||
if BPFILTER
|
||||
config BPFILTER_UMH
|
||||
tristate "bpfilter kernel module with user mode helper"
|
||||
depends on CC_CAN_LINK_STATIC
|
||||
depends on CC_CAN_LINK
|
||||
depends on m || CC_CAN_LINK_STATIC
|
||||
default m
|
||||
help
|
||||
This builds bpfilter kernel module with embedded user mode helper
|
||||
|
||||
Note: your toolchain must support building static binaries, since
|
||||
rootfs isn't mounted at the time when __init functions are called
|
||||
and do_execv won't be able to find the elf interpreter.
|
||||
Note: To compile this as built-in, your toolchain must support
|
||||
building static binaries, since rootfs isn't mounted at the time
|
||||
when __init functions are called and do_execv won't be able to find
|
||||
the elf interpreter.
|
||||
endif
|
||||
|
|
|
@ -7,10 +7,12 @@ userprogs := bpfilter_umh
|
|||
bpfilter_umh-objs := main.o
|
||||
userccflags += -I $(srctree)/tools/include/ -I $(srctree)/tools/include/uapi
|
||||
|
||||
ifeq ($(CONFIG_BPFILTER_UMH), y)
|
||||
# builtin bpfilter_umh should be linked with -static
|
||||
# since rootfs isn't mounted at the time of __init
|
||||
# function is called and do_execv won't find elf interpreter
|
||||
userldflags += -static
|
||||
endif
|
||||
|
||||
$(obj)/bpfilter_umh_blob.o: $(obj)/bpfilter_umh
|
||||
|
||||
|
|
|
@ -15,15 +15,13 @@ extern char bpfilter_umh_end;
|
|||
|
||||
static void shutdown_umh(void)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
struct umd_info *info = &bpfilter_ops.info;
|
||||
struct pid *tgid = info->tgid;
|
||||
|
||||
if (bpfilter_ops.stop)
|
||||
return;
|
||||
|
||||
tsk = get_pid_task(find_vpid(bpfilter_ops.info.pid), PIDTYPE_PID);
|
||||
if (tsk) {
|
||||
send_sig(SIGKILL, tsk, 1);
|
||||
put_task_struct(tsk);
|
||||
if (tgid) {
|
||||
kill_pid(tgid, SIGKILL, 1);
|
||||
wait_event(tgid->wait_pidfd, thread_group_exited(tgid));
|
||||
bpfilter_umh_cleanup(info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,8 +46,9 @@ static int __bpfilter_process_sockopt(struct sock *sk, int optname,
|
|||
req.cmd = optname;
|
||||
req.addr = (long __force __user)optval;
|
||||
req.len = optlen;
|
||||
if (!bpfilter_ops.info.pid)
|
||||
if (!bpfilter_ops.info.tgid)
|
||||
goto out;
|
||||
pos = 0;
|
||||
n = kernel_write(bpfilter_ops.info.pipe_to_umh, &req, sizeof(req),
|
||||
&pos);
|
||||
if (n != sizeof(req)) {
|
||||
|
@ -77,13 +76,10 @@ static int start_umh(void)
|
|||
int err;
|
||||
|
||||
/* fork usermode process */
|
||||
err = fork_usermode_blob(&bpfilter_umh_start,
|
||||
&bpfilter_umh_end - &bpfilter_umh_start,
|
||||
&bpfilter_ops.info);
|
||||
err = fork_usermode_driver(&bpfilter_ops.info);
|
||||
if (err)
|
||||
return err;
|
||||
bpfilter_ops.stop = false;
|
||||
pr_info("Loaded bpfilter_umh pid %d\n", bpfilter_ops.info.pid);
|
||||
pr_info("Loaded bpfilter_umh pid %d\n", pid_nr(bpfilter_ops.info.tgid));
|
||||
|
||||
/* health check that usermode process started correctly */
|
||||
if (__bpfilter_process_sockopt(NULL, 0, NULL, 0, 0) != 0) {
|
||||
|
@ -98,18 +94,21 @@ static int __init load_umh(void)
|
|||
{
|
||||
int err;
|
||||
|
||||
err = umd_load_blob(&bpfilter_ops.info,
|
||||
&bpfilter_umh_start,
|
||||
&bpfilter_umh_end - &bpfilter_umh_start);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&bpfilter_ops.lock);
|
||||
if (!bpfilter_ops.stop) {
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
err = start_umh();
|
||||
if (!err && IS_ENABLED(CONFIG_INET)) {
|
||||
bpfilter_ops.sockopt = &__bpfilter_process_sockopt;
|
||||
bpfilter_ops.start = &start_umh;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&bpfilter_ops.lock);
|
||||
if (err)
|
||||
umd_unload_blob(&bpfilter_ops.info);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -122,6 +121,8 @@ static void __exit fini_umh(void)
|
|||
bpfilter_ops.sockopt = NULL;
|
||||
}
|
||||
mutex_unlock(&bpfilter_ops.lock);
|
||||
|
||||
umd_unload_blob(&bpfilter_ops.info);
|
||||
}
|
||||
module_init(load_umh);
|
||||
module_exit(fini_umh);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
.section .rodata, "a"
|
||||
.section .init.rodata, "a"
|
||||
.global bpfilter_umh_start
|
||||
bpfilter_umh_start:
|
||||
.incbin "net/bpfilter/bpfilter_umh"
|
||||
|
|
|
@ -12,15 +12,14 @@
|
|||
struct bpfilter_umh_ops bpfilter_ops;
|
||||
EXPORT_SYMBOL_GPL(bpfilter_ops);
|
||||
|
||||
static void bpfilter_umh_cleanup(struct umh_info *info)
|
||||
void bpfilter_umh_cleanup(struct umd_info *info)
|
||||
{
|
||||
mutex_lock(&bpfilter_ops.lock);
|
||||
bpfilter_ops.stop = true;
|
||||
fput(info->pipe_to_umh);
|
||||
fput(info->pipe_from_umh);
|
||||
info->pid = 0;
|
||||
mutex_unlock(&bpfilter_ops.lock);
|
||||
put_pid(info->tgid);
|
||||
info->tgid = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bpfilter_umh_cleanup);
|
||||
|
||||
static int bpfilter_mbox_request(struct sock *sk, int optname,
|
||||
char __user *optval,
|
||||
|
@ -38,7 +37,11 @@ static int bpfilter_mbox_request(struct sock *sk, int optname,
|
|||
goto out;
|
||||
}
|
||||
}
|
||||
if (bpfilter_ops.stop) {
|
||||
if (bpfilter_ops.info.tgid &&
|
||||
thread_group_exited(bpfilter_ops.info.tgid))
|
||||
bpfilter_umh_cleanup(&bpfilter_ops.info);
|
||||
|
||||
if (!bpfilter_ops.info.tgid) {
|
||||
err = bpfilter_ops.start();
|
||||
if (err)
|
||||
goto out;
|
||||
|
@ -69,9 +72,8 @@ int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char __user *optval,
|
|||
static int __init bpfilter_sockopt_init(void)
|
||||
{
|
||||
mutex_init(&bpfilter_ops.lock);
|
||||
bpfilter_ops.stop = true;
|
||||
bpfilter_ops.info.cmdline = "bpfilter_umh";
|
||||
bpfilter_ops.info.cleanup = &bpfilter_umh_cleanup;
|
||||
bpfilter_ops.info.tgid = NULL;
|
||||
bpfilter_ops.info.driver_name = "bpfilter_umh";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,20 @@ else
|
|||
MAKEFLAGS=--no-print-directory
|
||||
endif
|
||||
|
||||
# always use the host compiler
|
||||
ifneq ($(LLVM),)
|
||||
HOSTAR ?= llvm-ar
|
||||
HOSTCC ?= clang
|
||||
HOSTLD ?= ld.lld
|
||||
else
|
||||
HOSTAR ?= ar
|
||||
HOSTCC ?= gcc
|
||||
HOSTLD ?= ld
|
||||
endif
|
||||
AR = $(HOSTAR)
|
||||
CC = $(HOSTCC)
|
||||
LD = $(HOSTLD)
|
||||
|
||||
OUTPUT ?= $(srctree)/tools/bpf/resolve_btfids/
|
||||
|
||||
LIBBPF_SRC := $(srctree)/tools/lib/bpf/
|
||||
|
|
|
@ -7,20 +7,6 @@
|
|||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
#define sk_rmem_alloc sk_backlog.rmem_alloc
|
||||
#define sk_refcnt __sk_common.skc_refcnt
|
||||
|
||||
struct bpf_iter_meta {
|
||||
struct seq_file *seq;
|
||||
__u64 session_id;
|
||||
__u64 seq_num;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct bpf_iter__netlink {
|
||||
struct bpf_iter_meta *meta;
|
||||
struct netlink_sock *sk;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
static __attribute__((noinline)) struct inode *SOCK_INODE(struct socket *socket)
|
||||
{
|
||||
return &container_of(socket, struct socket_alloc, socket)->vfs_inode;
|
||||
|
|
Loading…
Reference in New Issue