Blame view

kernel/usermode_driver.c 4.12 KB
884c5e683   Eric W. Biederman   umh: Separate the...
1
2
3
4
5
6
  // SPDX-License-Identifier: GPL-2.0-only
  /*
   * umd - User mode driver support
   */
  #include <linux/shmem_fs.h>
  #include <linux/pipe_fs_i.h>
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
7
8
9
  #include <linux/mount.h>
  #include <linux/fs_struct.h>
  #include <linux/task_work.h>
884c5e683   Eric W. Biederman   umh: Separate the...
10
  #include <linux/usermode_driver.h>
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  static struct vfsmount *blob_to_mnt(const void *data, size_t len, const char *name)
  {
  	struct file_system_type *type;
  	struct vfsmount *mnt;
  	struct file *file;
  	ssize_t written;
  	loff_t pos = 0;
  
  	type = get_fs_type("tmpfs");
  	if (!type)
  		return ERR_PTR(-ENODEV);
  
  	mnt = kern_mount(type);
  	put_filesystem(type);
  	if (IS_ERR(mnt))
  		return mnt;
  
  	file = file_open_root(mnt->mnt_root, mnt, name, O_CREAT | O_WRONLY, 0700);
  	if (IS_ERR(file)) {
  		mntput(mnt);
  		return ERR_CAST(file);
  	}
  
  	written = kernel_write(file, data, len, &pos);
  	if (written != len) {
  		int err = written;
  		if (err >= 0)
  			err = -ENOMEM;
  		filp_close(file, NULL);
  		mntput(mnt);
  		return ERR_PTR(err);
  	}
  
  	fput(file);
  
  	/* Flush delayed fput so exec can open the file read-only */
  	flush_delayed_fput();
  	task_work_run();
  	return mnt;
  }
  
  /**
   * umd_load_blob - Remember a blob of bytes for fork_usermode_driver
   * @info: information about usermode driver
   * @data: a blob of bytes that can be executed as a file
   * @len:  The lentgh of the blob
   *
   */
  int umd_load_blob(struct umd_info *info, const void *data, size_t len)
  {
  	struct vfsmount *mnt;
  
  	if (WARN_ON_ONCE(info->wd.dentry || info->wd.mnt))
  		return -EBUSY;
  
  	mnt = blob_to_mnt(data, len, info->driver_name);
  	if (IS_ERR(mnt))
  		return PTR_ERR(mnt);
  
  	info->wd.mnt = mnt;
  	info->wd.dentry = mnt->mnt_root;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(umd_load_blob);
  
  /**
   * umd_unload_blob - Disassociate @info from a previously loaded blob
   * @info: information about usermode driver
   *
   */
  int umd_unload_blob(struct umd_info *info)
  {
  	if (WARN_ON_ONCE(!info->wd.mnt ||
  			 !info->wd.dentry ||
  			 info->wd.mnt->mnt_root != info->wd.dentry))
  		return -EINVAL;
  
  	kern_unmount(info->wd.mnt);
  	info->wd.mnt = NULL;
  	info->wd.dentry = NULL;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(umd_unload_blob);
884c5e683   Eric W. Biederman   umh: Separate the...
94
95
  static int umd_setup(struct subprocess_info *info, struct cred *new)
  {
74be2d3b8   Eric W. Biederman   umd: For clarity ...
96
  	struct umd_info *umd_info = info->data;
884c5e683   Eric W. Biederman   umh: Separate the...
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  	struct file *from_umh[2];
  	struct file *to_umh[2];
  	int err;
  
  	/* create pipe to send data to umh */
  	err = create_pipe_files(to_umh, 0);
  	if (err)
  		return err;
  	err = replace_fd(0, to_umh[0], 0);
  	fput(to_umh[0]);
  	if (err < 0) {
  		fput(to_umh[1]);
  		return err;
  	}
  
  	/* create pipe to receive data from umh */
  	err = create_pipe_files(from_umh, 0);
  	if (err) {
  		fput(to_umh[1]);
  		replace_fd(0, NULL, 0);
  		return err;
  	}
  	err = replace_fd(1, from_umh[1], 0);
  	fput(from_umh[1]);
  	if (err < 0) {
  		fput(to_umh[1]);
  		replace_fd(0, NULL, 0);
  		fput(from_umh[0]);
  		return err;
  	}
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
127
  	set_fs_pwd(current->fs, &umd_info->wd);
74be2d3b8   Eric W. Biederman   umd: For clarity ...
128
129
  	umd_info->pipe_to_umh = to_umh[1];
  	umd_info->pipe_from_umh = from_umh[0];
1c340ead1   Eric W. Biederman   umd: Track user s...
130
  	umd_info->tgid = get_pid(task_tgid(current));
884c5e683   Eric W. Biederman   umh: Separate the...
131
132
133
134
135
  	return 0;
  }
  
  static void umd_cleanup(struct subprocess_info *info)
  {
74be2d3b8   Eric W. Biederman   umd: For clarity ...
136
  	struct umd_info *umd_info = info->data;
884c5e683   Eric W. Biederman   umh: Separate the...
137
138
139
  
  	/* cleanup if umh_setup() was successful but exec failed */
  	if (info->retval) {
74be2d3b8   Eric W. Biederman   umd: For clarity ...
140
141
  		fput(umd_info->pipe_to_umh);
  		fput(umd_info->pipe_from_umh);
1c340ead1   Eric W. Biederman   umd: Track user s...
142
143
  		put_pid(umd_info->tgid);
  		umd_info->tgid = NULL;
884c5e683   Eric W. Biederman   umh: Separate the...
144
145
146
147
  	}
  }
  
  /**
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
148
149
   * fork_usermode_driver - fork a usermode driver
   * @info: information about usermode driver (shouldn't be NULL)
884c5e683   Eric W. Biederman   umh: Separate the...
150
   *
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
151
152
   * Returns either negative error or zero which indicates success in
   * executing a usermode driver. In such case 'struct umd_info *info'
1c340ead1   Eric W. Biederman   umd: Track user s...
153
   * is populated with two pipes and a tgid of the process. The caller is
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
154
   * responsible for health check of the user process, killing it via
1c340ead1   Eric W. Biederman   umd: Track user s...
155
   * tgid, and closing the pipes when user process is no longer needed.
884c5e683   Eric W. Biederman   umh: Separate the...
156
   */
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
157
  int fork_usermode_driver(struct umd_info *info)
884c5e683   Eric W. Biederman   umh: Separate the...
158
  {
884c5e683   Eric W. Biederman   umh: Separate the...
159
  	struct subprocess_info *sub_info;
33c326014   Eric W. Biederman   umd: Stop using s...
160
  	const char *argv[] = { info->driver_name, NULL };
884c5e683   Eric W. Biederman   umh: Separate the...
161
  	int err;
1c340ead1   Eric W. Biederman   umd: Track user s...
162
163
  	if (WARN_ON_ONCE(info->tgid))
  		return -EBUSY;
884c5e683   Eric W. Biederman   umh: Separate the...
164
  	err = -ENOMEM;
33c326014   Eric W. Biederman   umd: Stop using s...
165
166
  	sub_info = call_usermodehelper_setup(info->driver_name,
  					     (char **)argv, NULL, GFP_KERNEL,
884c5e683   Eric W. Biederman   umh: Separate the...
167
168
169
  					     umd_setup, umd_cleanup, info);
  	if (!sub_info)
  		goto out;
884c5e683   Eric W. Biederman   umh: Separate the...
170
  	err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
884c5e683   Eric W. Biederman   umh: Separate the...
171
  out:
884c5e683   Eric W. Biederman   umh: Separate the...
172
173
  	return err;
  }
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
174
  EXPORT_SYMBOL_GPL(fork_usermode_driver);
884c5e683   Eric W. Biederman   umh: Separate the...
175

884c5e683   Eric W. Biederman   umh: Separate the...
176