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