1884c5e68SEric W. Biederman // SPDX-License-Identifier: GPL-2.0-only
2884c5e68SEric W. Biederman /*
3884c5e68SEric W. Biederman * umd - User mode driver support
4884c5e68SEric W. Biederman */
5884c5e68SEric W. Biederman #include <linux/shmem_fs.h>
6884c5e68SEric W. Biederman #include <linux/pipe_fs_i.h>
7e2dc9bf3SEric W. Biederman #include <linux/mount.h>
8e2dc9bf3SEric W. Biederman #include <linux/fs_struct.h>
9e2dc9bf3SEric W. Biederman #include <linux/task_work.h>
10884c5e68SEric W. Biederman #include <linux/usermode_driver.h>
11884c5e68SEric W. Biederman
blob_to_mnt(const void * data,size_t len,const char * name)12e2dc9bf3SEric W. Biederman static struct vfsmount *blob_to_mnt(const void *data, size_t len, const char *name)
13e2dc9bf3SEric W. Biederman {
14e2dc9bf3SEric W. Biederman struct file_system_type *type;
15e2dc9bf3SEric W. Biederman struct vfsmount *mnt;
16e2dc9bf3SEric W. Biederman struct file *file;
17e2dc9bf3SEric W. Biederman ssize_t written;
18e2dc9bf3SEric W. Biederman loff_t pos = 0;
19e2dc9bf3SEric W. Biederman
20e2dc9bf3SEric W. Biederman type = get_fs_type("tmpfs");
21e2dc9bf3SEric W. Biederman if (!type)
22e2dc9bf3SEric W. Biederman return ERR_PTR(-ENODEV);
23e2dc9bf3SEric W. Biederman
24e2dc9bf3SEric W. Biederman mnt = kern_mount(type);
25e2dc9bf3SEric W. Biederman put_filesystem(type);
26e2dc9bf3SEric W. Biederman if (IS_ERR(mnt))
27e2dc9bf3SEric W. Biederman return mnt;
28e2dc9bf3SEric W. Biederman
29ffb37ca3SAl Viro file = file_open_root_mnt(mnt, name, O_CREAT | O_WRONLY, 0700);
30e2dc9bf3SEric W. Biederman if (IS_ERR(file)) {
31*279b192cSAl Viro kern_unmount(mnt);
32e2dc9bf3SEric W. Biederman return ERR_CAST(file);
33e2dc9bf3SEric W. Biederman }
34e2dc9bf3SEric W. Biederman
35e2dc9bf3SEric W. Biederman written = kernel_write(file, data, len, &pos);
36e2dc9bf3SEric W. Biederman if (written != len) {
37e2dc9bf3SEric W. Biederman int err = written;
38e2dc9bf3SEric W. Biederman if (err >= 0)
39e2dc9bf3SEric W. Biederman err = -ENOMEM;
40e2dc9bf3SEric W. Biederman filp_close(file, NULL);
41*279b192cSAl Viro kern_unmount(mnt);
42e2dc9bf3SEric W. Biederman return ERR_PTR(err);
43e2dc9bf3SEric W. Biederman }
44e2dc9bf3SEric W. Biederman
45e2dc9bf3SEric W. Biederman fput(file);
46e2dc9bf3SEric W. Biederman
47e2dc9bf3SEric W. Biederman /* Flush delayed fput so exec can open the file read-only */
48e2dc9bf3SEric W. Biederman flush_delayed_fput();
49e2dc9bf3SEric W. Biederman task_work_run();
50e2dc9bf3SEric W. Biederman return mnt;
51e2dc9bf3SEric W. Biederman }
52e2dc9bf3SEric W. Biederman
53e2dc9bf3SEric W. Biederman /**
54e2dc9bf3SEric W. Biederman * umd_load_blob - Remember a blob of bytes for fork_usermode_driver
55e2dc9bf3SEric W. Biederman * @info: information about usermode driver
56e2dc9bf3SEric W. Biederman * @data: a blob of bytes that can be executed as a file
57e2dc9bf3SEric W. Biederman * @len: The lentgh of the blob
58e2dc9bf3SEric W. Biederman *
59e2dc9bf3SEric W. Biederman */
umd_load_blob(struct umd_info * info,const void * data,size_t len)60e2dc9bf3SEric W. Biederman int umd_load_blob(struct umd_info *info, const void *data, size_t len)
61e2dc9bf3SEric W. Biederman {
62e2dc9bf3SEric W. Biederman struct vfsmount *mnt;
63e2dc9bf3SEric W. Biederman
64e2dc9bf3SEric W. Biederman if (WARN_ON_ONCE(info->wd.dentry || info->wd.mnt))
65e2dc9bf3SEric W. Biederman return -EBUSY;
66e2dc9bf3SEric W. Biederman
67e2dc9bf3SEric W. Biederman mnt = blob_to_mnt(data, len, info->driver_name);
68e2dc9bf3SEric W. Biederman if (IS_ERR(mnt))
69e2dc9bf3SEric W. Biederman return PTR_ERR(mnt);
70e2dc9bf3SEric W. Biederman
71e2dc9bf3SEric W. Biederman info->wd.mnt = mnt;
72e2dc9bf3SEric W. Biederman info->wd.dentry = mnt->mnt_root;
73e2dc9bf3SEric W. Biederman return 0;
74e2dc9bf3SEric W. Biederman }
75e2dc9bf3SEric W. Biederman EXPORT_SYMBOL_GPL(umd_load_blob);
76e2dc9bf3SEric W. Biederman
77e2dc9bf3SEric W. Biederman /**
78e2dc9bf3SEric W. Biederman * umd_unload_blob - Disassociate @info from a previously loaded blob
79e2dc9bf3SEric W. Biederman * @info: information about usermode driver
80e2dc9bf3SEric W. Biederman *
81e2dc9bf3SEric W. Biederman */
umd_unload_blob(struct umd_info * info)82e2dc9bf3SEric W. Biederman int umd_unload_blob(struct umd_info *info)
83e2dc9bf3SEric W. Biederman {
84e2dc9bf3SEric W. Biederman if (WARN_ON_ONCE(!info->wd.mnt ||
85e2dc9bf3SEric W. Biederman !info->wd.dentry ||
86e2dc9bf3SEric W. Biederman info->wd.mnt->mnt_root != info->wd.dentry))
87e2dc9bf3SEric W. Biederman return -EINVAL;
88e2dc9bf3SEric W. Biederman
89e2dc9bf3SEric W. Biederman kern_unmount(info->wd.mnt);
90e2dc9bf3SEric W. Biederman info->wd.mnt = NULL;
91e2dc9bf3SEric W. Biederman info->wd.dentry = NULL;
92e2dc9bf3SEric W. Biederman return 0;
93e2dc9bf3SEric W. Biederman }
94e2dc9bf3SEric W. Biederman EXPORT_SYMBOL_GPL(umd_unload_blob);
95e2dc9bf3SEric W. Biederman
umd_setup(struct subprocess_info * info,struct cred * new)96884c5e68SEric W. Biederman static int umd_setup(struct subprocess_info *info, struct cred *new)
97884c5e68SEric W. Biederman {
9874be2d3bSEric W. Biederman struct umd_info *umd_info = info->data;
99884c5e68SEric W. Biederman struct file *from_umh[2];
100884c5e68SEric W. Biederman struct file *to_umh[2];
101884c5e68SEric W. Biederman int err;
102884c5e68SEric W. Biederman
103884c5e68SEric W. Biederman /* create pipe to send data to umh */
104884c5e68SEric W. Biederman err = create_pipe_files(to_umh, 0);
105884c5e68SEric W. Biederman if (err)
106884c5e68SEric W. Biederman return err;
107884c5e68SEric W. Biederman err = replace_fd(0, to_umh[0], 0);
108884c5e68SEric W. Biederman fput(to_umh[0]);
109884c5e68SEric W. Biederman if (err < 0) {
110884c5e68SEric W. Biederman fput(to_umh[1]);
111884c5e68SEric W. Biederman return err;
112884c5e68SEric W. Biederman }
113884c5e68SEric W. Biederman
114884c5e68SEric W. Biederman /* create pipe to receive data from umh */
115884c5e68SEric W. Biederman err = create_pipe_files(from_umh, 0);
116884c5e68SEric W. Biederman if (err) {
117884c5e68SEric W. Biederman fput(to_umh[1]);
118884c5e68SEric W. Biederman replace_fd(0, NULL, 0);
119884c5e68SEric W. Biederman return err;
120884c5e68SEric W. Biederman }
121884c5e68SEric W. Biederman err = replace_fd(1, from_umh[1], 0);
122884c5e68SEric W. Biederman fput(from_umh[1]);
123884c5e68SEric W. Biederman if (err < 0) {
124884c5e68SEric W. Biederman fput(to_umh[1]);
125884c5e68SEric W. Biederman replace_fd(0, NULL, 0);
126884c5e68SEric W. Biederman fput(from_umh[0]);
127884c5e68SEric W. Biederman return err;
128884c5e68SEric W. Biederman }
129884c5e68SEric W. Biederman
130e2dc9bf3SEric W. Biederman set_fs_pwd(current->fs, &umd_info->wd);
13174be2d3bSEric W. Biederman umd_info->pipe_to_umh = to_umh[1];
13274be2d3bSEric W. Biederman umd_info->pipe_from_umh = from_umh[0];
1331c340eadSEric W. Biederman umd_info->tgid = get_pid(task_tgid(current));
134884c5e68SEric W. Biederman return 0;
135884c5e68SEric W. Biederman }
136884c5e68SEric W. Biederman
umd_cleanup(struct subprocess_info * info)137884c5e68SEric W. Biederman static void umd_cleanup(struct subprocess_info *info)
138884c5e68SEric W. Biederman {
13974be2d3bSEric W. Biederman struct umd_info *umd_info = info->data;
140884c5e68SEric W. Biederman
141884c5e68SEric W. Biederman /* cleanup if umh_setup() was successful but exec failed */
142f60a85caSZqiang if (info->retval)
143f60a85caSZqiang umd_cleanup_helper(umd_info);
144884c5e68SEric W. Biederman }
145f60a85caSZqiang
146f60a85caSZqiang /**
147f60a85caSZqiang * umd_cleanup_helper - release the resources which were allocated in umd_setup
148f60a85caSZqiang * @info: information about usermode driver
149f60a85caSZqiang */
umd_cleanup_helper(struct umd_info * info)150f60a85caSZqiang void umd_cleanup_helper(struct umd_info *info)
151f60a85caSZqiang {
152f60a85caSZqiang fput(info->pipe_to_umh);
153f60a85caSZqiang fput(info->pipe_from_umh);
154f60a85caSZqiang put_pid(info->tgid);
155f60a85caSZqiang info->tgid = NULL;
156884c5e68SEric W. Biederman }
157f60a85caSZqiang EXPORT_SYMBOL_GPL(umd_cleanup_helper);
158884c5e68SEric W. Biederman
159884c5e68SEric W. Biederman /**
160e2dc9bf3SEric W. Biederman * fork_usermode_driver - fork a usermode driver
161e2dc9bf3SEric W. Biederman * @info: information about usermode driver (shouldn't be NULL)
162884c5e68SEric W. Biederman *
163e2dc9bf3SEric W. Biederman * Returns either negative error or zero which indicates success in
164e2dc9bf3SEric W. Biederman * executing a usermode driver. In such case 'struct umd_info *info'
1651c340eadSEric W. Biederman * is populated with two pipes and a tgid of the process. The caller is
166e2dc9bf3SEric W. Biederman * responsible for health check of the user process, killing it via
1671c340eadSEric W. Biederman * tgid, and closing the pipes when user process is no longer needed.
168884c5e68SEric W. Biederman */
fork_usermode_driver(struct umd_info * info)169e2dc9bf3SEric W. Biederman int fork_usermode_driver(struct umd_info *info)
170884c5e68SEric W. Biederman {
171884c5e68SEric W. Biederman struct subprocess_info *sub_info;
17233c32601SEric W. Biederman const char *argv[] = { info->driver_name, NULL };
173884c5e68SEric W. Biederman int err;
174884c5e68SEric W. Biederman
1751c340eadSEric W. Biederman if (WARN_ON_ONCE(info->tgid))
1761c340eadSEric W. Biederman return -EBUSY;
1771c340eadSEric W. Biederman
178884c5e68SEric W. Biederman err = -ENOMEM;
17933c32601SEric W. Biederman sub_info = call_usermodehelper_setup(info->driver_name,
18033c32601SEric W. Biederman (char **)argv, NULL, GFP_KERNEL,
181884c5e68SEric W. Biederman umd_setup, umd_cleanup, info);
182884c5e68SEric W. Biederman if (!sub_info)
183884c5e68SEric W. Biederman goto out;
184884c5e68SEric W. Biederman
185884c5e68SEric W. Biederman err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
186884c5e68SEric W. Biederman out:
187884c5e68SEric W. Biederman return err;
188884c5e68SEric W. Biederman }
189e2dc9bf3SEric W. Biederman EXPORT_SYMBOL_GPL(fork_usermode_driver);
190884c5e68SEric W. Biederman
191884c5e68SEric W. Biederman
192