Merge branch 'usermode-driver-cleanup' of git://git.kernel.org/pub/scm/linux/kernel...
authorAlexei Starovoitov <ast@kernel.org>
Tue, 14 Jul 2020 19:18:01 +0000 (12:18 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 14 Jul 2020 19:18:01 +0000 (12:18 -0700)
15 files changed:
fs/exec.c
include/linux/binfmts.h
include/linux/bpfilter.h
include/linux/sched.h
include/linux/sched/signal.h
include/linux/umh.h
include/linux/usermode_driver.h [new file with mode: 0644]
kernel/Makefile
kernel/exit.c
kernel/fork.c
kernel/umh.c
kernel/usermode_driver.c [new file with mode: 0644]
net/bpfilter/bpfilter_kern.c
net/bpfilter/bpfilter_umh_blob.S
net/ipv4/bpfilter/sockopt.c

index e6e8a9a..23dfbb8 100644 (file)
--- a/fs/exec.c
+++ b/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)
index 4a20b75..7c27d7b 100644 (file)
@@ -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 */
index d815622..9b114c7 100644 (file)
@@ -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
index 692e327..c191e44 100644 (file)
@@ -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);
index 0ee5e69..1bad18a 100644 (file)
@@ -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);
 
index 0c08de3..244aff6 100644 (file)
@@ -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);
 
diff --git a/include/linux/usermode_driver.h b/include/linux/usermode_driver.h
new file mode 100644 (file)
index 0000000..073a9e0
--- /dev/null
@@ -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__ */
index f3218bc..4392875 100644 (file)
@@ -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
 
index 727150f..39226a0 100644 (file)
@@ -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();
index efc5493..1b94965 100644 (file)
@@ -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;
 }
index 79f139a..6ca2096 100644 (file)
@@ -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",
diff --git a/kernel/usermode_driver.c b/kernel/usermode_driver.c
new file mode 100644 (file)
index 0000000..0b35212
--- /dev/null
@@ -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);
+
+
index 1905e01..54f82c3 100644 (file)
@@ -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,7 +46,7 @@ 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;
        n = kernel_write(bpfilter_ops.info.pipe_to_umh, &req, sizeof(req),
                           &pos);
@@ -77,13 +75,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 +93,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 +120,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);
index 9ea6100..40311d1 100644 (file)
@@ -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"
index 0480918..9063c67 100644 (file)
 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;
 }