Blame view

kernel/usermode_driver.c 4.34 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
  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;
ffb37ca3b   Al Viro   switch file_open_...
27
  	file = file_open_root_mnt(mnt, name, O_CREAT | O_WRONLY, 0700);
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
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
  	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...
93
94
  static int umd_setup(struct subprocess_info *info, struct cred *new)
  {
74be2d3b8   Eric W. Biederman   umd: For clarity ...
95
  	struct umd_info *umd_info = info->data;
884c5e683   Eric W. Biederman   umh: Separate the...
96
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
  	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...
126
  	set_fs_pwd(current->fs, &umd_info->wd);
74be2d3b8   Eric W. Biederman   umd: For clarity ...
127
128
  	umd_info->pipe_to_umh = to_umh[1];
  	umd_info->pipe_from_umh = from_umh[0];
1c340ead1   Eric W. Biederman   umd: Track user s...
129
  	umd_info->tgid = get_pid(task_tgid(current));
884c5e683   Eric W. Biederman   umh: Separate the...
130
131
132
133
134
  	return 0;
  }
  
  static void umd_cleanup(struct subprocess_info *info)
  {
74be2d3b8   Eric W. Biederman   umd: For clarity ...
135
  	struct umd_info *umd_info = info->data;
884c5e683   Eric W. Biederman   umh: Separate the...
136
137
  
  	/* cleanup if umh_setup() was successful but exec failed */
f60a85cad   Zqiang   bpf: Fix umd memo...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  	if (info->retval)
  		umd_cleanup_helper(umd_info);
  }
  
  /**
   * umd_cleanup_helper - release the resources which were allocated in umd_setup
   * @info: information about usermode driver
   */
  void umd_cleanup_helper(struct umd_info *info)
  {
  	fput(info->pipe_to_umh);
  	fput(info->pipe_from_umh);
  	put_pid(info->tgid);
  	info->tgid = NULL;
884c5e683   Eric W. Biederman   umh: Separate the...
152
  }
f60a85cad   Zqiang   bpf: Fix umd memo...
153
  EXPORT_SYMBOL_GPL(umd_cleanup_helper);
884c5e683   Eric W. Biederman   umh: Separate the...
154
155
  
  /**
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
156
157
   * fork_usermode_driver - fork a usermode driver
   * @info: information about usermode driver (shouldn't be NULL)
884c5e683   Eric W. Biederman   umh: Separate the...
158
   *
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
159
160
   * 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...
161
   * is populated with two pipes and a tgid of the process. The caller is
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
162
   * responsible for health check of the user process, killing it via
1c340ead1   Eric W. Biederman   umd: Track user s...
163
   * tgid, and closing the pipes when user process is no longer needed.
884c5e683   Eric W. Biederman   umh: Separate the...
164
   */
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
165
  int fork_usermode_driver(struct umd_info *info)
884c5e683   Eric W. Biederman   umh: Separate the...
166
  {
884c5e683   Eric W. Biederman   umh: Separate the...
167
  	struct subprocess_info *sub_info;
33c326014   Eric W. Biederman   umd: Stop using s...
168
  	const char *argv[] = { info->driver_name, NULL };
884c5e683   Eric W. Biederman   umh: Separate the...
169
  	int err;
1c340ead1   Eric W. Biederman   umd: Track user s...
170
171
  	if (WARN_ON_ONCE(info->tgid))
  		return -EBUSY;
884c5e683   Eric W. Biederman   umh: Separate the...
172
  	err = -ENOMEM;
33c326014   Eric W. Biederman   umd: Stop using s...
173
174
  	sub_info = call_usermodehelper_setup(info->driver_name,
  					     (char **)argv, NULL, GFP_KERNEL,
884c5e683   Eric W. Biederman   umh: Separate the...
175
176
177
  					     umd_setup, umd_cleanup, info);
  	if (!sub_info)
  		goto out;
884c5e683   Eric W. Biederman   umh: Separate the...
178
  	err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
884c5e683   Eric W. Biederman   umh: Separate the...
179
  out:
884c5e683   Eric W. Biederman   umh: Separate the...
180
181
  	return err;
  }
e2dc9bf3f   Eric W. Biederman   umd: Transform fo...
182
  EXPORT_SYMBOL_GPL(fork_usermode_driver);
884c5e683   Eric W. Biederman   umh: Separate the...
183

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