xref: /linux/kernel/usermode_driver.c (revision 279b192c)
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