]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
umh: Separate the user mode driver and the user mode helper support
authorEric W. Biederman <ebiederm@xmission.com>
Fri, 26 Jun 2020 17:23:00 +0000 (12:23 -0500)
committerEric W. Biederman <ebiederm@xmission.com>
Sat, 4 Jul 2020 14:34:32 +0000 (09:34 -0500)
This makes it clear which code is part of the core user mode
helper support and which code is needed to implement user mode
drivers.

This makes the kernel smaller for everyone who does not use a usermode
driver.

v1: https://lkml.kernel.org/r/87tuyyf0ln.fsf_-_@x220.int.ebiederm.org
v2: https://lkml.kernel.org/r/87imf963s6.fsf_-_@x220.int.ebiederm.org
Link: https://lkml.kernel.org/r/20200702164140.4468-5-ebiederm@xmission.com
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Tested-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
include/linux/bpfilter.h
include/linux/sched.h
include/linux/umh.h
include/linux/usermode_driver.h [new file with mode: 0644]
kernel/Makefile
kernel/exit.c
kernel/umh.c
kernel/usermode_driver.c [new file with mode: 0644]

index d815622cd31e51da14686c71b8d1907665c1be2f..d6d6206052a620306ad39a3d4dd38b7f96c4c5ec 100644 (file)
@@ -3,7 +3,7 @@
 #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,
index b62e6aaf28f0318455f47c047c307cb6441fdd53..59d1e92bb88ea96b97fa28ba62e901418d347fe0 100644 (file)
@@ -2020,14 +2020,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 de08af00c68a79f4aa75097e9554104e89270294..73173c4a07e5d5abee404c94ccc0c374969ccc47 100644 (file)
@@ -39,16 +39,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 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..c5f6dc9
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef __LINUX_USERMODE_DRIVER_H__
+#define __LINUX_USERMODE_DRIVER_H__
+
+#include <linux/umh.h>
+
+#ifdef CONFIG_BPFILTER
+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);
+}
+#else
+static inline void exit_umh(struct task_struct *tsk)
+{
+}
+#endif
+
+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);
+
+#endif /* __LINUX_USERMODE_DRIVER_H__ */
index f3218bc5ec69fd99ff8d615ecabc102993136dc3..43928759893a3c007fbfc2dfdeeab536f6835c43 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 727150f281033850f99eb199d66f1b2bf4c8b86d..a081deea52ca300927b26fdb972b336e019008aa 100644 (file)
@@ -63,6 +63,7 @@
 #include <linux/random.h>
 #include <linux/rcuwait.h>
 #include <linux/compat.h>
+#include <linux/usermode_driver.h>
 
 #include <linux/uaccess.h>
 #include <asm/unistd.h>
index b8fa9b99b366d51744c923af48e37d28c1aba989..3e4e453d45c8716f85efa97e08ebda602c5d9097 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)
 {
@@ -402,121 +398,6 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
 }
 EXPORT_SYMBOL(call_usermodehelper_setup);
 
-static int umd_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];
-       umh_info->pid = task_pid_nr(current);
-       current->flags |= PF_UMH;
-       return 0;
-}
-
-static void umd_cleanup(struct subprocess_info *info)
-{
-       struct umh_info *umh_info = info->data;
-
-       /* cleanup if umh_setup() was successful but exec failed */
-       if (info->retval) {
-               fput(umh_info->pipe_to_umh);
-               fput(umh_info->pipe_from_umh);
-       }
-}
-
-/**
- * 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)
-{
-       const char *cmdline = (info->cmdline) ? info->cmdline : "usermodehelper";
-       struct subprocess_info *sub_info;
-       char **argv = NULL;
-       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;
-       argv = argv_split(GFP_KERNEL, cmdline, NULL);
-       if (!argv)
-               goto out;
-
-       sub_info = call_usermodehelper_setup("none", argv, NULL, GFP_KERNEL,
-                                            umd_setup, umd_cleanup, info);
-       if (!sub_info)
-               goto out;
-
-       sub_info->file = file;
-       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:
-       if (argv)
-               argv_free(argv);
-       fput(file);
-       return err;
-}
-EXPORT_SYMBOL_GPL(fork_usermode_blob);
-
 /**
  * call_usermodehelper_exec - start a usermode application
  * @sub_info: information about the subprocessa
@@ -678,26 +559,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..5b05863
--- /dev/null
@@ -0,0 +1,146 @@
+// 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/usermode_driver.h>
+
+static LIST_HEAD(umh_list);
+static DEFINE_MUTEX(umh_list_lock);
+
+static int umd_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];
+       umh_info->pid = task_pid_nr(current);
+       current->flags |= PF_UMH;
+       return 0;
+}
+
+static void umd_cleanup(struct subprocess_info *info)
+{
+       struct umh_info *umh_info = info->data;
+
+       /* cleanup if umh_setup() was successful but exec failed */
+       if (info->retval) {
+               fput(umh_info->pipe_to_umh);
+               fput(umh_info->pipe_from_umh);
+       }
+}
+
+/**
+ * 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)
+{
+       const char *cmdline = (info->cmdline) ? info->cmdline : "usermodehelper";
+       struct subprocess_info *sub_info;
+       char **argv = NULL;
+       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;
+       argv = argv_split(GFP_KERNEL, cmdline, NULL);
+       if (!argv)
+               goto out;
+
+       sub_info = call_usermodehelper_setup("none", argv, NULL, GFP_KERNEL,
+                                            umd_setup, umd_cleanup, info);
+       if (!sub_info)
+               goto out;
+
+       sub_info->file = file;
+       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:
+       if (argv)
+               argv_free(argv);
+       fput(file);
+       return err;
+}
+EXPORT_SYMBOL_GPL(fork_usermode_blob);
+
+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);
+}
+