]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - kernel/usermode_driver.c
umh: Separate the user mode driver and the user mode helper support
[mirror_ubuntu-jammy-kernel.git] / kernel / usermode_driver.c
CommitLineData
884c5e68
EB
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * umd - User mode driver support
4 */
5#include <linux/shmem_fs.h>
6#include <linux/pipe_fs_i.h>
7#include <linux/usermode_driver.h>
8
9static LIST_HEAD(umh_list);
10static DEFINE_MUTEX(umh_list_lock);
11
12static int umd_setup(struct subprocess_info *info, struct cred *new)
13{
14 struct umh_info *umh_info = info->data;
15 struct file *from_umh[2];
16 struct file *to_umh[2];
17 int err;
18
19 /* create pipe to send data to umh */
20 err = create_pipe_files(to_umh, 0);
21 if (err)
22 return err;
23 err = replace_fd(0, to_umh[0], 0);
24 fput(to_umh[0]);
25 if (err < 0) {
26 fput(to_umh[1]);
27 return err;
28 }
29
30 /* create pipe to receive data from umh */
31 err = create_pipe_files(from_umh, 0);
32 if (err) {
33 fput(to_umh[1]);
34 replace_fd(0, NULL, 0);
35 return err;
36 }
37 err = replace_fd(1, from_umh[1], 0);
38 fput(from_umh[1]);
39 if (err < 0) {
40 fput(to_umh[1]);
41 replace_fd(0, NULL, 0);
42 fput(from_umh[0]);
43 return err;
44 }
45
46 umh_info->pipe_to_umh = to_umh[1];
47 umh_info->pipe_from_umh = from_umh[0];
48 umh_info->pid = task_pid_nr(current);
49 current->flags |= PF_UMH;
50 return 0;
51}
52
53static void umd_cleanup(struct subprocess_info *info)
54{
55 struct umh_info *umh_info = info->data;
56
57 /* cleanup if umh_setup() was successful but exec failed */
58 if (info->retval) {
59 fput(umh_info->pipe_to_umh);
60 fput(umh_info->pipe_from_umh);
61 }
62}
63
64/**
65 * fork_usermode_blob - fork a blob of bytes as a usermode process
66 * @data: a blob of bytes that can be do_execv-ed as a file
67 * @len: length of the blob
68 * @info: information about usermode process (shouldn't be NULL)
69 *
70 * If info->cmdline is set it will be used as command line for the
71 * user process, else "usermodehelper" is used.
72 *
73 * Returns either negative error or zero which indicates success
74 * in executing a blob of bytes as a usermode process. In such
75 * case 'struct umh_info *info' is populated with two pipes
76 * and a pid of the process. The caller is responsible for health
77 * check of the user process, killing it via pid, and closing the
78 * pipes when user process is no longer needed.
79 */
80int fork_usermode_blob(void *data, size_t len, struct umh_info *info)
81{
82 const char *cmdline = (info->cmdline) ? info->cmdline : "usermodehelper";
83 struct subprocess_info *sub_info;
84 char **argv = NULL;
85 struct file *file;
86 ssize_t written;
87 loff_t pos = 0;
88 int err;
89
90 file = shmem_kernel_file_setup("", len, 0);
91 if (IS_ERR(file))
92 return PTR_ERR(file);
93
94 written = kernel_write(file, data, len, &pos);
95 if (written != len) {
96 err = written;
97 if (err >= 0)
98 err = -ENOMEM;
99 goto out;
100 }
101
102 err = -ENOMEM;
103 argv = argv_split(GFP_KERNEL, cmdline, NULL);
104 if (!argv)
105 goto out;
106
107 sub_info = call_usermodehelper_setup("none", argv, NULL, GFP_KERNEL,
108 umd_setup, umd_cleanup, info);
109 if (!sub_info)
110 goto out;
111
112 sub_info->file = file;
113 err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
114 if (!err) {
115 mutex_lock(&umh_list_lock);
116 list_add(&info->list, &umh_list);
117 mutex_unlock(&umh_list_lock);
118 }
119out:
120 if (argv)
121 argv_free(argv);
122 fput(file);
123 return err;
124}
125EXPORT_SYMBOL_GPL(fork_usermode_blob);
126
127void __exit_umh(struct task_struct *tsk)
128{
129 struct umh_info *info;
130 pid_t pid = tsk->pid;
131
132 mutex_lock(&umh_list_lock);
133 list_for_each_entry(info, &umh_list, list) {
134 if (info->pid == pid) {
135 list_del(&info->list);
136 mutex_unlock(&umh_list_lock);
137 goto out;
138 }
139 }
140 mutex_unlock(&umh_list_lock);
141 return;
142out:
143 if (info->cleanup)
144 info->cleanup(info);
145}
146