1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright (C) 2003-2008 Takahiro Hirofuchi |
4 | * Copyright (C) 2015-2016 Nobuo Iwata |
5 | */ |
6 | |
7 | #include <linux/kthread.h> |
8 | #include <linux/file.h> |
9 | #include <linux/net.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/slab.h> |
12 | |
13 | /* Hardening for Spectre-v1 */ |
14 | #include <linux/nospec.h> |
15 | |
16 | #include "usbip_common.h" |
17 | #include "vhci.h" |
18 | |
19 | /* TODO: refine locking ?*/ |
20 | |
21 | /* |
22 | * output example: |
23 | * hub port sta spd dev sockfd local_busid |
24 | * hs 0000 004 000 00000000 000003 1-2.3 |
25 | * ................................................ |
26 | * ss 0008 004 000 00000000 000004 2-3.4 |
27 | * ................................................ |
28 | * |
29 | * Output includes socket fd instead of socket pointer address to avoid |
30 | * leaking kernel memory address in: |
31 | * /sys/devices/platform/vhci_hcd.0/status and in debug output. |
32 | * The socket pointer address is not used at the moment and it was made |
33 | * visible as a convenient way to find IP address from socket pointer |
34 | * address by looking up /proc/net/{tcp,tcp6}. As this opens a security |
35 | * hole, the change is made to use sockfd instead. |
36 | * |
37 | */ |
38 | static void port_show_vhci(char **out, int hub, int port, struct vhci_device *vdev) |
39 | { |
40 | if (hub == HUB_SPEED_HIGH) |
41 | *out += sprintf(buf: *out, fmt: "hs %04u %03u " , |
42 | port, vdev->ud.status); |
43 | else /* hub == HUB_SPEED_SUPER */ |
44 | *out += sprintf(buf: *out, fmt: "ss %04u %03u " , |
45 | port, vdev->ud.status); |
46 | |
47 | if (vdev->ud.status == VDEV_ST_USED) { |
48 | *out += sprintf(buf: *out, fmt: "%03u %08x " , |
49 | vdev->speed, vdev->devid); |
50 | *out += sprintf(buf: *out, fmt: "%06u %s" , |
51 | vdev->ud.sockfd, |
52 | dev_name(dev: &vdev->udev->dev)); |
53 | |
54 | } else { |
55 | *out += sprintf(buf: *out, fmt: "000 00000000 " ); |
56 | *out += sprintf(buf: *out, fmt: "000000 0-0" ); |
57 | } |
58 | |
59 | *out += sprintf(buf: *out, fmt: "\n" ); |
60 | } |
61 | |
62 | /* Sysfs entry to show port status */ |
63 | static ssize_t status_show_vhci(int pdev_nr, char *out) |
64 | { |
65 | struct platform_device *pdev = vhcis[pdev_nr].pdev; |
66 | struct vhci *vhci; |
67 | struct usb_hcd *hcd; |
68 | struct vhci_hcd *vhci_hcd; |
69 | char *s = out; |
70 | int i; |
71 | unsigned long flags; |
72 | |
73 | if (!pdev || !out) { |
74 | usbip_dbg_vhci_sysfs("show status error\n" ); |
75 | return 0; |
76 | } |
77 | |
78 | hcd = platform_get_drvdata(pdev); |
79 | vhci_hcd = hcd_to_vhci_hcd(hcd); |
80 | vhci = vhci_hcd->vhci; |
81 | |
82 | spin_lock_irqsave(&vhci->lock, flags); |
83 | |
84 | for (i = 0; i < VHCI_HC_PORTS; i++) { |
85 | struct vhci_device *vdev = &vhci->vhci_hcd_hs->vdev[i]; |
86 | |
87 | spin_lock(lock: &vdev->ud.lock); |
88 | port_show_vhci(out: &out, hub: HUB_SPEED_HIGH, |
89 | port: pdev_nr * VHCI_PORTS + i, vdev); |
90 | spin_unlock(lock: &vdev->ud.lock); |
91 | } |
92 | |
93 | for (i = 0; i < VHCI_HC_PORTS; i++) { |
94 | struct vhci_device *vdev = &vhci->vhci_hcd_ss->vdev[i]; |
95 | |
96 | spin_lock(lock: &vdev->ud.lock); |
97 | port_show_vhci(out: &out, hub: HUB_SPEED_SUPER, |
98 | port: pdev_nr * VHCI_PORTS + VHCI_HC_PORTS + i, vdev); |
99 | spin_unlock(lock: &vdev->ud.lock); |
100 | } |
101 | |
102 | spin_unlock_irqrestore(lock: &vhci->lock, flags); |
103 | |
104 | return out - s; |
105 | } |
106 | |
107 | static ssize_t status_show_not_ready(int pdev_nr, char *out) |
108 | { |
109 | char *s = out; |
110 | int i = 0; |
111 | |
112 | for (i = 0; i < VHCI_HC_PORTS; i++) { |
113 | out += sprintf(buf: out, fmt: "hs %04u %03u " , |
114 | (pdev_nr * VHCI_PORTS) + i, |
115 | VDEV_ST_NOTASSIGNED); |
116 | out += sprintf(buf: out, fmt: "000 00000000 0000000000000000 0-0" ); |
117 | out += sprintf(buf: out, fmt: "\n" ); |
118 | } |
119 | |
120 | for (i = 0; i < VHCI_HC_PORTS; i++) { |
121 | out += sprintf(buf: out, fmt: "ss %04u %03u " , |
122 | (pdev_nr * VHCI_PORTS) + VHCI_HC_PORTS + i, |
123 | VDEV_ST_NOTASSIGNED); |
124 | out += sprintf(buf: out, fmt: "000 00000000 0000000000000000 0-0" ); |
125 | out += sprintf(buf: out, fmt: "\n" ); |
126 | } |
127 | return out - s; |
128 | } |
129 | |
130 | static int status_name_to_id(const char *name) |
131 | { |
132 | char *c; |
133 | long val; |
134 | int ret; |
135 | |
136 | c = strchr(name, '.'); |
137 | if (c == NULL) |
138 | return 0; |
139 | |
140 | ret = kstrtol(s: c+1, base: 10, res: &val); |
141 | if (ret < 0) |
142 | return ret; |
143 | |
144 | return val; |
145 | } |
146 | |
147 | static ssize_t status_show(struct device *dev, |
148 | struct device_attribute *attr, char *out) |
149 | { |
150 | char *s = out; |
151 | int pdev_nr; |
152 | |
153 | out += sprintf(buf: out, |
154 | fmt: "hub port sta spd dev sockfd local_busid\n" ); |
155 | |
156 | pdev_nr = status_name_to_id(name: attr->attr.name); |
157 | if (pdev_nr < 0) |
158 | out += status_show_not_ready(pdev_nr, out); |
159 | else |
160 | out += status_show_vhci(pdev_nr, out); |
161 | |
162 | return out - s; |
163 | } |
164 | |
165 | static ssize_t nports_show(struct device *dev, struct device_attribute *attr, |
166 | char *out) |
167 | { |
168 | char *s = out; |
169 | |
170 | /* |
171 | * Half the ports are for SPEED_HIGH and half for SPEED_SUPER, |
172 | * thus the * 2. |
173 | */ |
174 | out += sprintf(buf: out, fmt: "%d\n" , VHCI_PORTS * vhci_num_controllers); |
175 | return out - s; |
176 | } |
177 | static DEVICE_ATTR_RO(nports); |
178 | |
179 | /* Sysfs entry to shutdown a virtual connection */ |
180 | static int vhci_port_disconnect(struct vhci_hcd *vhci_hcd, __u32 rhport) |
181 | { |
182 | struct vhci_device *vdev = &vhci_hcd->vdev[rhport]; |
183 | struct vhci *vhci = vhci_hcd->vhci; |
184 | unsigned long flags; |
185 | |
186 | usbip_dbg_vhci_sysfs("enter\n" ); |
187 | |
188 | mutex_lock(&vdev->ud.sysfs_lock); |
189 | |
190 | /* lock */ |
191 | spin_lock_irqsave(&vhci->lock, flags); |
192 | spin_lock(lock: &vdev->ud.lock); |
193 | |
194 | if (vdev->ud.status == VDEV_ST_NULL) { |
195 | pr_err("not connected %d\n" , vdev->ud.status); |
196 | |
197 | /* unlock */ |
198 | spin_unlock(lock: &vdev->ud.lock); |
199 | spin_unlock_irqrestore(lock: &vhci->lock, flags); |
200 | mutex_unlock(lock: &vdev->ud.sysfs_lock); |
201 | |
202 | return -EINVAL; |
203 | } |
204 | |
205 | /* unlock */ |
206 | spin_unlock(lock: &vdev->ud.lock); |
207 | spin_unlock_irqrestore(lock: &vhci->lock, flags); |
208 | |
209 | usbip_event_add(ud: &vdev->ud, VDEV_EVENT_DOWN); |
210 | |
211 | mutex_unlock(lock: &vdev->ud.sysfs_lock); |
212 | |
213 | return 0; |
214 | } |
215 | |
216 | static int valid_port(__u32 *pdev_nr, __u32 *rhport) |
217 | { |
218 | if (*pdev_nr >= vhci_num_controllers) { |
219 | pr_err("pdev %u\n" , *pdev_nr); |
220 | return 0; |
221 | } |
222 | *pdev_nr = array_index_nospec(*pdev_nr, vhci_num_controllers); |
223 | |
224 | if (*rhport >= VHCI_HC_PORTS) { |
225 | pr_err("rhport %u\n" , *rhport); |
226 | return 0; |
227 | } |
228 | *rhport = array_index_nospec(*rhport, VHCI_HC_PORTS); |
229 | |
230 | return 1; |
231 | } |
232 | |
233 | static ssize_t detach_store(struct device *dev, struct device_attribute *attr, |
234 | const char *buf, size_t count) |
235 | { |
236 | __u32 port = 0, pdev_nr = 0, rhport = 0; |
237 | struct usb_hcd *hcd; |
238 | struct vhci_hcd *vhci_hcd; |
239 | int ret; |
240 | |
241 | if (kstrtoint(s: buf, base: 10, res: &port) < 0) |
242 | return -EINVAL; |
243 | |
244 | pdev_nr = port_to_pdev_nr(port); |
245 | rhport = port_to_rhport(port); |
246 | |
247 | if (!valid_port(pdev_nr: &pdev_nr, rhport: &rhport)) |
248 | return -EINVAL; |
249 | |
250 | hcd = platform_get_drvdata(pdev: vhcis[pdev_nr].pdev); |
251 | if (hcd == NULL) { |
252 | dev_err(dev, "port is not ready %u\n" , port); |
253 | return -EAGAIN; |
254 | } |
255 | |
256 | usbip_dbg_vhci_sysfs("rhport %d\n" , rhport); |
257 | |
258 | if ((port / VHCI_HC_PORTS) % 2) |
259 | vhci_hcd = hcd_to_vhci_hcd(hcd)->vhci->vhci_hcd_ss; |
260 | else |
261 | vhci_hcd = hcd_to_vhci_hcd(hcd)->vhci->vhci_hcd_hs; |
262 | |
263 | ret = vhci_port_disconnect(vhci_hcd, rhport); |
264 | if (ret < 0) |
265 | return -EINVAL; |
266 | |
267 | usbip_dbg_vhci_sysfs("Leave\n" ); |
268 | |
269 | return count; |
270 | } |
271 | static DEVICE_ATTR_WO(detach); |
272 | |
273 | static int valid_args(__u32 *pdev_nr, __u32 *rhport, |
274 | enum usb_device_speed speed) |
275 | { |
276 | if (!valid_port(pdev_nr, rhport)) { |
277 | return 0; |
278 | } |
279 | |
280 | switch (speed) { |
281 | case USB_SPEED_LOW: |
282 | case USB_SPEED_FULL: |
283 | case USB_SPEED_HIGH: |
284 | case USB_SPEED_WIRELESS: |
285 | case USB_SPEED_SUPER: |
286 | break; |
287 | default: |
288 | pr_err("Failed attach request for unsupported USB speed: %s\n" , |
289 | usb_speed_string(speed)); |
290 | return 0; |
291 | } |
292 | |
293 | return 1; |
294 | } |
295 | |
296 | /* Sysfs entry to establish a virtual connection */ |
297 | /* |
298 | * To start a new USB/IP attachment, a userland program needs to setup a TCP |
299 | * connection and then write its socket descriptor with remote device |
300 | * information into this sysfs file. |
301 | * |
302 | * A remote device is virtually attached to the root-hub port of @rhport with |
303 | * @speed. @devid is embedded into a request to specify the remote device in a |
304 | * server host. |
305 | * |
306 | * write() returns 0 on success, else negative errno. |
307 | */ |
308 | static ssize_t attach_store(struct device *dev, struct device_attribute *attr, |
309 | const char *buf, size_t count) |
310 | { |
311 | struct socket *socket; |
312 | int sockfd = 0; |
313 | __u32 port = 0, pdev_nr = 0, rhport = 0, devid = 0, speed = 0; |
314 | struct usb_hcd *hcd; |
315 | struct vhci_hcd *vhci_hcd; |
316 | struct vhci_device *vdev; |
317 | struct vhci *vhci; |
318 | int err; |
319 | unsigned long flags; |
320 | struct task_struct *tcp_rx = NULL; |
321 | struct task_struct *tcp_tx = NULL; |
322 | |
323 | /* |
324 | * @rhport: port number of vhci_hcd |
325 | * @sockfd: socket descriptor of an established TCP connection |
326 | * @devid: unique device identifier in a remote host |
327 | * @speed: usb device speed in a remote host |
328 | */ |
329 | if (sscanf(buf, "%u %u %u %u" , &port, &sockfd, &devid, &speed) != 4) |
330 | return -EINVAL; |
331 | pdev_nr = port_to_pdev_nr(port); |
332 | rhport = port_to_rhport(port); |
333 | |
334 | usbip_dbg_vhci_sysfs("port(%u) pdev(%d) rhport(%u)\n" , |
335 | port, pdev_nr, rhport); |
336 | usbip_dbg_vhci_sysfs("sockfd(%u) devid(%u) speed(%u)\n" , |
337 | sockfd, devid, speed); |
338 | |
339 | /* check received parameters */ |
340 | if (!valid_args(pdev_nr: &pdev_nr, rhport: &rhport, speed)) |
341 | return -EINVAL; |
342 | |
343 | hcd = platform_get_drvdata(pdev: vhcis[pdev_nr].pdev); |
344 | if (hcd == NULL) { |
345 | dev_err(dev, "port %d is not ready\n" , port); |
346 | return -EAGAIN; |
347 | } |
348 | |
349 | vhci_hcd = hcd_to_vhci_hcd(hcd); |
350 | vhci = vhci_hcd->vhci; |
351 | |
352 | if (speed == USB_SPEED_SUPER) |
353 | vdev = &vhci->vhci_hcd_ss->vdev[rhport]; |
354 | else |
355 | vdev = &vhci->vhci_hcd_hs->vdev[rhport]; |
356 | |
357 | mutex_lock(&vdev->ud.sysfs_lock); |
358 | |
359 | /* Extract socket from fd. */ |
360 | socket = sockfd_lookup(fd: sockfd, err: &err); |
361 | if (!socket) { |
362 | dev_err(dev, "failed to lookup sock" ); |
363 | err = -EINVAL; |
364 | goto unlock_mutex; |
365 | } |
366 | if (socket->type != SOCK_STREAM) { |
367 | dev_err(dev, "Expecting SOCK_STREAM - found %d" , |
368 | socket->type); |
369 | sockfd_put(socket); |
370 | err = -EINVAL; |
371 | goto unlock_mutex; |
372 | } |
373 | |
374 | /* create threads before locking */ |
375 | tcp_rx = kthread_create(vhci_rx_loop, &vdev->ud, "vhci_rx" ); |
376 | if (IS_ERR(ptr: tcp_rx)) { |
377 | sockfd_put(socket); |
378 | err = -EINVAL; |
379 | goto unlock_mutex; |
380 | } |
381 | tcp_tx = kthread_create(vhci_tx_loop, &vdev->ud, "vhci_tx" ); |
382 | if (IS_ERR(ptr: tcp_tx)) { |
383 | kthread_stop(k: tcp_rx); |
384 | sockfd_put(socket); |
385 | err = -EINVAL; |
386 | goto unlock_mutex; |
387 | } |
388 | |
389 | /* get task structs now */ |
390 | get_task_struct(t: tcp_rx); |
391 | get_task_struct(t: tcp_tx); |
392 | |
393 | /* now begin lock until setting vdev status set */ |
394 | spin_lock_irqsave(&vhci->lock, flags); |
395 | spin_lock(lock: &vdev->ud.lock); |
396 | |
397 | if (vdev->ud.status != VDEV_ST_NULL) { |
398 | /* end of the lock */ |
399 | spin_unlock(lock: &vdev->ud.lock); |
400 | spin_unlock_irqrestore(lock: &vhci->lock, flags); |
401 | |
402 | sockfd_put(socket); |
403 | kthread_stop_put(k: tcp_rx); |
404 | kthread_stop_put(k: tcp_tx); |
405 | |
406 | dev_err(dev, "port %d already used\n" , rhport); |
407 | /* |
408 | * Will be retried from userspace |
409 | * if there's another free port. |
410 | */ |
411 | err = -EBUSY; |
412 | goto unlock_mutex; |
413 | } |
414 | |
415 | dev_info(dev, "pdev(%u) rhport(%u) sockfd(%d)\n" , |
416 | pdev_nr, rhport, sockfd); |
417 | dev_info(dev, "devid(%u) speed(%u) speed_str(%s)\n" , |
418 | devid, speed, usb_speed_string(speed)); |
419 | |
420 | vdev->devid = devid; |
421 | vdev->speed = speed; |
422 | vdev->ud.sockfd = sockfd; |
423 | vdev->ud.tcp_socket = socket; |
424 | vdev->ud.tcp_rx = tcp_rx; |
425 | vdev->ud.tcp_tx = tcp_tx; |
426 | vdev->ud.status = VDEV_ST_NOTASSIGNED; |
427 | usbip_kcov_handle_init(ud: &vdev->ud); |
428 | |
429 | spin_unlock(lock: &vdev->ud.lock); |
430 | spin_unlock_irqrestore(lock: &vhci->lock, flags); |
431 | /* end the lock */ |
432 | |
433 | wake_up_process(tsk: vdev->ud.tcp_rx); |
434 | wake_up_process(tsk: vdev->ud.tcp_tx); |
435 | |
436 | rh_port_connect(vdev, speed); |
437 | |
438 | dev_info(dev, "Device attached\n" ); |
439 | |
440 | mutex_unlock(lock: &vdev->ud.sysfs_lock); |
441 | |
442 | return count; |
443 | |
444 | unlock_mutex: |
445 | mutex_unlock(lock: &vdev->ud.sysfs_lock); |
446 | return err; |
447 | } |
448 | static DEVICE_ATTR_WO(attach); |
449 | |
450 | #define MAX_STATUS_NAME 16 |
451 | |
452 | struct status_attr { |
453 | struct device_attribute attr; |
454 | char name[MAX_STATUS_NAME+1]; |
455 | }; |
456 | |
457 | static struct status_attr *status_attrs; |
458 | |
459 | static void set_status_attr(int id) |
460 | { |
461 | struct status_attr *status; |
462 | |
463 | status = status_attrs + id; |
464 | if (id == 0) |
465 | strcpy(p: status->name, q: "status" ); |
466 | else |
467 | snprintf(buf: status->name, MAX_STATUS_NAME+1, fmt: "status.%d" , id); |
468 | status->attr.attr.name = status->name; |
469 | status->attr.attr.mode = S_IRUGO; |
470 | status->attr.show = status_show; |
471 | sysfs_attr_init(&status->attr.attr); |
472 | } |
473 | |
474 | static int init_status_attrs(void) |
475 | { |
476 | int id; |
477 | |
478 | status_attrs = kcalloc(n: vhci_num_controllers, size: sizeof(struct status_attr), |
479 | GFP_KERNEL); |
480 | if (status_attrs == NULL) |
481 | return -ENOMEM; |
482 | |
483 | for (id = 0; id < vhci_num_controllers; id++) |
484 | set_status_attr(id); |
485 | |
486 | return 0; |
487 | } |
488 | |
489 | static void finish_status_attrs(void) |
490 | { |
491 | kfree(objp: status_attrs); |
492 | } |
493 | |
494 | struct attribute_group vhci_attr_group = { |
495 | .attrs = NULL, |
496 | }; |
497 | |
498 | int vhci_init_attr_group(void) |
499 | { |
500 | struct attribute **attrs; |
501 | int ret, i; |
502 | |
503 | attrs = kcalloc(n: (vhci_num_controllers + 5), size: sizeof(struct attribute *), |
504 | GFP_KERNEL); |
505 | if (attrs == NULL) |
506 | return -ENOMEM; |
507 | |
508 | ret = init_status_attrs(); |
509 | if (ret) { |
510 | kfree(objp: attrs); |
511 | return ret; |
512 | } |
513 | *attrs = &dev_attr_nports.attr; |
514 | *(attrs + 1) = &dev_attr_detach.attr; |
515 | *(attrs + 2) = &dev_attr_attach.attr; |
516 | *(attrs + 3) = &dev_attr_usbip_debug.attr; |
517 | for (i = 0; i < vhci_num_controllers; i++) |
518 | *(attrs + i + 4) = &((status_attrs + i)->attr.attr); |
519 | vhci_attr_group.attrs = attrs; |
520 | return 0; |
521 | } |
522 | |
523 | void vhci_finish_attr_group(void) |
524 | { |
525 | finish_status_attrs(); |
526 | kfree(objp: vhci_attr_group.attrs); |
527 | } |
528 | |