blob: 46d60d855e93f3303c79e780bbc1795c2febdf8d [file] [log] [blame]
Eric W. Biederman884c5e62020-06-26 12:23:00 -05001// 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{
Eric W. Biederman74be2d32020-06-26 11:16:06 -050014 struct umd_info *umd_info = info->data;
Eric W. Biederman884c5e62020-06-26 12:23:00 -050015 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
Eric W. Biederman74be2d32020-06-26 11:16:06 -050046 umd_info->pipe_to_umh = to_umh[1];
47 umd_info->pipe_from_umh = from_umh[0];
48 umd_info->pid = task_pid_nr(current);
Eric W. Biederman884c5e62020-06-26 12:23:00 -050049 current->flags |= PF_UMH;
50 return 0;
51}
52
53static void umd_cleanup(struct subprocess_info *info)
54{
Eric W. Biederman74be2d32020-06-26 11:16:06 -050055 struct umd_info *umd_info = info->data;
Eric W. Biederman884c5e62020-06-26 12:23:00 -050056
57 /* cleanup if umh_setup() was successful but exec failed */
58 if (info->retval) {
Eric W. Biederman74be2d32020-06-26 11:16:06 -050059 fput(umd_info->pipe_to_umh);
60 fput(umd_info->pipe_from_umh);
Eric W. Biederman884c5e62020-06-26 12:23:00 -050061 }
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 *
Eric W. Biederman884c5e62020-06-26 12:23:00 -050070 * Returns either negative error or zero which indicates success
71 * in executing a blob of bytes as a usermode process. In such
Eric W. Biederman74be2d32020-06-26 11:16:06 -050072 * case 'struct umd_info *info' is populated with two pipes
Eric W. Biederman884c5e62020-06-26 12:23:00 -050073 * and a pid of the process. The caller is responsible for health
74 * check of the user process, killing it via pid, and closing the
75 * pipes when user process is no longer needed.
76 */
Eric W. Biederman74be2d32020-06-26 11:16:06 -050077int fork_usermode_blob(void *data, size_t len, struct umd_info *info)
Eric W. Biederman884c5e62020-06-26 12:23:00 -050078{
Eric W. Biederman884c5e62020-06-26 12:23:00 -050079 struct subprocess_info *sub_info;
80 char **argv = NULL;
81 struct file *file;
82 ssize_t written;
83 loff_t pos = 0;
84 int err;
85
Eric W. Biederman1199c6c2020-06-25 11:38:08 -050086 file = shmem_kernel_file_setup(info->driver_name, len, 0);
Eric W. Biederman884c5e62020-06-26 12:23:00 -050087 if (IS_ERR(file))
88 return PTR_ERR(file);
89
90 written = kernel_write(file, data, len, &pos);
91 if (written != len) {
92 err = written;
93 if (err >= 0)
94 err = -ENOMEM;
95 goto out;
96 }
97
98 err = -ENOMEM;
Eric W. Biederman1199c6c2020-06-25 11:38:08 -050099 argv = argv_split(GFP_KERNEL, info->driver_name, NULL);
Eric W. Biederman884c5e62020-06-26 12:23:00 -0500100 if (!argv)
101 goto out;
102
Eric W. Biederman1199c6c2020-06-25 11:38:08 -0500103 sub_info = call_usermodehelper_setup(info->driver_name, argv, NULL,
104 GFP_KERNEL,
Eric W. Biederman884c5e62020-06-26 12:23:00 -0500105 umd_setup, umd_cleanup, info);
106 if (!sub_info)
107 goto out;
108
109 sub_info->file = file;
110 err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
111 if (!err) {
112 mutex_lock(&umh_list_lock);
113 list_add(&info->list, &umh_list);
114 mutex_unlock(&umh_list_lock);
115 }
116out:
117 if (argv)
118 argv_free(argv);
119 fput(file);
120 return err;
121}
122EXPORT_SYMBOL_GPL(fork_usermode_blob);
123
124void __exit_umh(struct task_struct *tsk)
125{
Eric W. Biederman74be2d32020-06-26 11:16:06 -0500126 struct umd_info *info;
Eric W. Biederman884c5e62020-06-26 12:23:00 -0500127 pid_t pid = tsk->pid;
128
129 mutex_lock(&umh_list_lock);
130 list_for_each_entry(info, &umh_list, list) {
131 if (info->pid == pid) {
132 list_del(&info->list);
133 mutex_unlock(&umh_list_lock);
134 goto out;
135 }
136 }
137 mutex_unlock(&umh_list_lock);
138 return;
139out:
140 if (info->cleanup)
141 info->cleanup(info);
142}
143