diff options
Diffstat (limited to 'drivers/usb/core/devio.c')
| -rw-r--r-- | drivers/usb/core/devio.c | 169 |
1 files changed, 140 insertions, 29 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index e0f107948eba..ebb8a9de8b5f 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c | |||
| @@ -47,6 +47,7 @@ | |||
| 47 | #include <linux/notifier.h> | 47 | #include <linux/notifier.h> |
| 48 | #include <linux/security.h> | 48 | #include <linux/security.h> |
| 49 | #include <linux/user_namespace.h> | 49 | #include <linux/user_namespace.h> |
| 50 | #include <linux/scatterlist.h> | ||
| 50 | #include <asm/uaccess.h> | 51 | #include <asm/uaccess.h> |
| 51 | #include <asm/byteorder.h> | 52 | #include <asm/byteorder.h> |
| 52 | #include <linux/moduleparam.h> | 53 | #include <linux/moduleparam.h> |
| @@ -55,6 +56,7 @@ | |||
| 55 | 56 | ||
| 56 | #define USB_MAXBUS 64 | 57 | #define USB_MAXBUS 64 |
| 57 | #define USB_DEVICE_MAX USB_MAXBUS * 128 | 58 | #define USB_DEVICE_MAX USB_MAXBUS * 128 |
| 59 | #define USB_SG_SIZE 16384 /* split-size for large txs */ | ||
| 58 | 60 | ||
| 59 | /* Mutual exclusion for removal, open, and release */ | 61 | /* Mutual exclusion for removal, open, and release */ |
| 60 | DEFINE_MUTEX(usbfs_mutex); | 62 | DEFINE_MUTEX(usbfs_mutex); |
| @@ -285,9 +287,16 @@ static struct async *alloc_async(unsigned int numisoframes) | |||
| 285 | 287 | ||
| 286 | static void free_async(struct async *as) | 288 | static void free_async(struct async *as) |
| 287 | { | 289 | { |
| 290 | int i; | ||
| 291 | |||
| 288 | put_pid(as->pid); | 292 | put_pid(as->pid); |
| 289 | if (as->cred) | 293 | if (as->cred) |
| 290 | put_cred(as->cred); | 294 | put_cred(as->cred); |
| 295 | for (i = 0; i < as->urb->num_sgs; i++) { | ||
| 296 | if (sg_page(&as->urb->sg[i])) | ||
| 297 | kfree(sg_virt(&as->urb->sg[i])); | ||
| 298 | } | ||
| 299 | kfree(as->urb->sg); | ||
| 291 | kfree(as->urb->transfer_buffer); | 300 | kfree(as->urb->transfer_buffer); |
| 292 | kfree(as->urb->setup_packet); | 301 | kfree(as->urb->setup_packet); |
| 293 | usb_free_urb(as->urb); | 302 | usb_free_urb(as->urb); |
| @@ -388,6 +397,53 @@ static void snoop_urb(struct usb_device *udev, | |||
| 388 | } | 397 | } |
| 389 | } | 398 | } |
| 390 | 399 | ||
| 400 | static void snoop_urb_data(struct urb *urb, unsigned len) | ||
| 401 | { | ||
| 402 | int i, size; | ||
| 403 | |||
| 404 | if (!usbfs_snoop) | ||
| 405 | return; | ||
| 406 | |||
| 407 | if (urb->num_sgs == 0) { | ||
| 408 | print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, | ||
| 409 | urb->transfer_buffer, len, 1); | ||
| 410 | return; | ||
| 411 | } | ||
| 412 | |||
| 413 | for (i = 0; i < urb->num_sgs && len; i++) { | ||
| 414 | size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len; | ||
| 415 | print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, | ||
| 416 | sg_virt(&urb->sg[i]), size, 1); | ||
| 417 | len -= size; | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb) | ||
| 422 | { | ||
| 423 | unsigned i, len, size; | ||
| 424 | |||
| 425 | if (urb->number_of_packets > 0) /* Isochronous */ | ||
| 426 | len = urb->transfer_buffer_length; | ||
| 427 | else /* Non-Isoc */ | ||
| 428 | len = urb->actual_length; | ||
| 429 | |||
| 430 | if (urb->num_sgs == 0) { | ||
| 431 | if (copy_to_user(userbuffer, urb->transfer_buffer, len)) | ||
| 432 | return -EFAULT; | ||
| 433 | return 0; | ||
| 434 | } | ||
| 435 | |||
| 436 | for (i = 0; i < urb->num_sgs && len; i++) { | ||
| 437 | size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len; | ||
| 438 | if (copy_to_user(userbuffer, sg_virt(&urb->sg[i]), size)) | ||
| 439 | return -EFAULT; | ||
| 440 | userbuffer += size; | ||
| 441 | len -= size; | ||
| 442 | } | ||
| 443 | |||
| 444 | return 0; | ||
| 445 | } | ||
| 446 | |||
| 391 | #define AS_CONTINUATION 1 | 447 | #define AS_CONTINUATION 1 |
| 392 | #define AS_UNLINK 2 | 448 | #define AS_UNLINK 2 |
| 393 | 449 | ||
| @@ -454,9 +510,10 @@ static void async_completed(struct urb *urb) | |||
| 454 | } | 510 | } |
| 455 | snoop(&urb->dev->dev, "urb complete\n"); | 511 | snoop(&urb->dev->dev, "urb complete\n"); |
| 456 | snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, | 512 | snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, |
| 457 | as->status, COMPLETE, | 513 | as->status, COMPLETE, NULL, 0); |
| 458 | ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_OUT) ? | 514 | if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN) |
| 459 | NULL : urb->transfer_buffer, urb->actual_length); | 515 | snoop_urb_data(urb, urb->actual_length); |
| 516 | |||
| 460 | if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && | 517 | if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && |
| 461 | as->status != -ENOENT) | 518 | as->status != -ENOENT) |
| 462 | cancel_bulk_urbs(ps, as->bulk_addr); | 519 | cancel_bulk_urbs(ps, as->bulk_addr); |
| @@ -1114,8 +1171,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
| 1114 | struct async *as = NULL; | 1171 | struct async *as = NULL; |
| 1115 | struct usb_ctrlrequest *dr = NULL; | 1172 | struct usb_ctrlrequest *dr = NULL; |
| 1116 | unsigned int u, totlen, isofrmlen; | 1173 | unsigned int u, totlen, isofrmlen; |
| 1117 | int ret, ifnum = -1; | 1174 | int i, ret, is_in, num_sgs = 0, ifnum = -1; |
| 1118 | int is_in; | 1175 | void *buf; |
| 1119 | 1176 | ||
| 1120 | if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | | 1177 | if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | |
| 1121 | USBDEVFS_URB_SHORT_NOT_OK | | 1178 | USBDEVFS_URB_SHORT_NOT_OK | |
| @@ -1199,6 +1256,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
| 1199 | goto interrupt_urb; | 1256 | goto interrupt_urb; |
| 1200 | } | 1257 | } |
| 1201 | uurb->number_of_packets = 0; | 1258 | uurb->number_of_packets = 0; |
| 1259 | num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); | ||
| 1260 | if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize) | ||
| 1261 | num_sgs = 0; | ||
| 1202 | break; | 1262 | break; |
| 1203 | 1263 | ||
| 1204 | case USBDEVFS_URB_TYPE_INTERRUPT: | 1264 | case USBDEVFS_URB_TYPE_INTERRUPT: |
| @@ -1255,26 +1315,67 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
| 1255 | ret = -ENOMEM; | 1315 | ret = -ENOMEM; |
| 1256 | goto error; | 1316 | goto error; |
| 1257 | } | 1317 | } |
| 1258 | u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length; | 1318 | |
| 1319 | u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length + | ||
| 1320 | num_sgs * sizeof(struct scatterlist); | ||
| 1259 | ret = usbfs_increase_memory_usage(u); | 1321 | ret = usbfs_increase_memory_usage(u); |
| 1260 | if (ret) | 1322 | if (ret) |
| 1261 | goto error; | 1323 | goto error; |
| 1262 | as->mem_usage = u; | 1324 | as->mem_usage = u; |
| 1263 | 1325 | ||
| 1264 | if (uurb->buffer_length > 0) { | 1326 | if (num_sgs) { |
| 1327 | as->urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist), | ||
| 1328 | GFP_KERNEL); | ||
| 1329 | if (!as->urb->sg) { | ||
| 1330 | ret = -ENOMEM; | ||
| 1331 | goto error; | ||
| 1332 | } | ||
| 1333 | as->urb->num_sgs = num_sgs; | ||
| 1334 | sg_init_table(as->urb->sg, as->urb->num_sgs); | ||
| 1335 | |||
| 1336 | totlen = uurb->buffer_length; | ||
| 1337 | for (i = 0; i < as->urb->num_sgs; i++) { | ||
| 1338 | u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen; | ||
| 1339 | buf = kmalloc(u, GFP_KERNEL); | ||
| 1340 | if (!buf) { | ||
| 1341 | ret = -ENOMEM; | ||
| 1342 | goto error; | ||
| 1343 | } | ||
| 1344 | sg_set_buf(&as->urb->sg[i], buf, u); | ||
| 1345 | |||
| 1346 | if (!is_in) { | ||
| 1347 | if (copy_from_user(buf, uurb->buffer, u)) { | ||
| 1348 | ret = -EFAULT; | ||
| 1349 | goto error; | ||
| 1350 | } | ||
| 1351 | } | ||
| 1352 | totlen -= u; | ||
| 1353 | } | ||
| 1354 | } else if (uurb->buffer_length > 0) { | ||
| 1265 | as->urb->transfer_buffer = kmalloc(uurb->buffer_length, | 1355 | as->urb->transfer_buffer = kmalloc(uurb->buffer_length, |
| 1266 | GFP_KERNEL); | 1356 | GFP_KERNEL); |
| 1267 | if (!as->urb->transfer_buffer) { | 1357 | if (!as->urb->transfer_buffer) { |
| 1268 | ret = -ENOMEM; | 1358 | ret = -ENOMEM; |
| 1269 | goto error; | 1359 | goto error; |
| 1270 | } | 1360 | } |
| 1271 | /* Isochronous input data may end up being discontiguous | 1361 | |
| 1272 | * if some of the packets are short. Clear the buffer so | 1362 | if (!is_in) { |
| 1273 | * that the gaps don't leak kernel data to userspace. | 1363 | if (copy_from_user(as->urb->transfer_buffer, |
| 1274 | */ | 1364 | uurb->buffer, |
| 1275 | if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO) | 1365 | uurb->buffer_length)) { |
| 1366 | ret = -EFAULT; | ||
| 1367 | goto error; | ||
| 1368 | } | ||
| 1369 | } else if (uurb->type == USBDEVFS_URB_TYPE_ISO) { | ||
| 1370 | /* | ||
| 1371 | * Isochronous input data may end up being | ||
| 1372 | * discontiguous if some of the packets are short. | ||
| 1373 | * Clear the buffer so that the gaps don't leak | ||
| 1374 | * kernel data to userspace. | ||
| 1375 | */ | ||
| 1276 | memset(as->urb->transfer_buffer, 0, | 1376 | memset(as->urb->transfer_buffer, 0, |
| 1277 | uurb->buffer_length); | 1377 | uurb->buffer_length); |
| 1378 | } | ||
| 1278 | } | 1379 | } |
| 1279 | as->urb->dev = ps->dev; | 1380 | as->urb->dev = ps->dev; |
| 1280 | as->urb->pipe = (uurb->type << 30) | | 1381 | as->urb->pipe = (uurb->type << 30) | |
| @@ -1328,17 +1429,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
| 1328 | as->pid = get_pid(task_pid(current)); | 1429 | as->pid = get_pid(task_pid(current)); |
| 1329 | as->cred = get_current_cred(); | 1430 | as->cred = get_current_cred(); |
| 1330 | security_task_getsecid(current, &as->secid); | 1431 | security_task_getsecid(current, &as->secid); |
| 1331 | if (!is_in && uurb->buffer_length > 0) { | ||
| 1332 | if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, | ||
| 1333 | uurb->buffer_length)) { | ||
| 1334 | ret = -EFAULT; | ||
| 1335 | goto error; | ||
| 1336 | } | ||
| 1337 | } | ||
| 1338 | snoop_urb(ps->dev, as->userurb, as->urb->pipe, | 1432 | snoop_urb(ps->dev, as->userurb, as->urb->pipe, |
| 1339 | as->urb->transfer_buffer_length, 0, SUBMIT, | 1433 | as->urb->transfer_buffer_length, 0, SUBMIT, |
| 1340 | is_in ? NULL : as->urb->transfer_buffer, | 1434 | NULL, 0); |
| 1341 | uurb->buffer_length); | 1435 | if (!is_in) |
| 1436 | snoop_urb_data(as->urb, as->urb->transfer_buffer_length); | ||
| 1437 | |||
| 1342 | async_newpending(as); | 1438 | async_newpending(as); |
| 1343 | 1439 | ||
| 1344 | if (usb_endpoint_xfer_bulk(&ep->desc)) { | 1440 | if (usb_endpoint_xfer_bulk(&ep->desc)) { |
| @@ -1433,11 +1529,7 @@ static int processcompl(struct async *as, void __user * __user *arg) | |||
| 1433 | unsigned int i; | 1529 | unsigned int i; |
| 1434 | 1530 | ||
| 1435 | if (as->userbuffer && urb->actual_length) { | 1531 | if (as->userbuffer && urb->actual_length) { |
| 1436 | if (urb->number_of_packets > 0) /* Isochronous */ | 1532 | if (copy_urb_data_to_user(as->userbuffer, urb)) |
| 1437 | i = urb->transfer_buffer_length; | ||
| 1438 | else /* Non-Isoc */ | ||
| 1439 | i = urb->actual_length; | ||
| 1440 | if (copy_to_user(as->userbuffer, urb->transfer_buffer, i)) | ||
| 1441 | goto err_out; | 1533 | goto err_out; |
| 1442 | } | 1534 | } |
| 1443 | if (put_user(as->status, &userurb->status)) | 1535 | if (put_user(as->status, &userurb->status)) |
| @@ -1604,10 +1696,10 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) | |||
| 1604 | void __user *addr = as->userurb; | 1696 | void __user *addr = as->userurb; |
| 1605 | unsigned int i; | 1697 | unsigned int i; |
| 1606 | 1698 | ||
| 1607 | if (as->userbuffer && urb->actual_length) | 1699 | if (as->userbuffer && urb->actual_length) { |
| 1608 | if (copy_to_user(as->userbuffer, urb->transfer_buffer, | 1700 | if (copy_urb_data_to_user(as->userbuffer, urb)) |
| 1609 | urb->actual_length)) | ||
| 1610 | return -EFAULT; | 1701 | return -EFAULT; |
| 1702 | } | ||
| 1611 | if (put_user(as->status, &userurb->status)) | 1703 | if (put_user(as->status, &userurb->status)) |
| 1612 | return -EFAULT; | 1704 | return -EFAULT; |
| 1613 | if (put_user(urb->actual_length, &userurb->actual_length)) | 1705 | if (put_user(urb->actual_length, &userurb->actual_length)) |
| @@ -1820,6 +1912,22 @@ static int proc_release_port(struct dev_state *ps, void __user *arg) | |||
| 1820 | return usb_hub_release_port(ps->dev, portnum, ps); | 1912 | return usb_hub_release_port(ps->dev, portnum, ps); |
| 1821 | } | 1913 | } |
| 1822 | 1914 | ||
| 1915 | static int proc_get_capabilities(struct dev_state *ps, void __user *arg) | ||
| 1916 | { | ||
| 1917 | __u32 caps; | ||
| 1918 | |||
| 1919 | caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; | ||
| 1920 | if (!ps->dev->bus->no_stop_on_short) | ||
| 1921 | caps |= USBDEVFS_CAP_BULK_CONTINUATION; | ||
| 1922 | if (ps->dev->bus->sg_tablesize) | ||
| 1923 | caps |= USBDEVFS_CAP_BULK_SCATTER_GATHER; | ||
| 1924 | |||
| 1925 | if (put_user(caps, (__u32 __user *)arg)) | ||
| 1926 | return -EFAULT; | ||
| 1927 | |||
| 1928 | return 0; | ||
| 1929 | } | ||
| 1930 | |||
| 1823 | /* | 1931 | /* |
| 1824 | * NOTE: All requests here that have interface numbers as parameters | 1932 | * NOTE: All requests here that have interface numbers as parameters |
| 1825 | * are assuming that somehow the configuration has been prevented from | 1933 | * are assuming that somehow the configuration has been prevented from |
| @@ -1990,6 +2098,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, | |||
| 1990 | snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__); | 2098 | snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__); |
| 1991 | ret = proc_release_port(ps, p); | 2099 | ret = proc_release_port(ps, p); |
| 1992 | break; | 2100 | break; |
| 2101 | case USBDEVFS_GET_CAPABILITIES: | ||
| 2102 | ret = proc_get_capabilities(ps, p); | ||
| 2103 | break; | ||
| 1993 | } | 2104 | } |
| 1994 | usb_unlock_device(dev); | 2105 | usb_unlock_device(dev); |
| 1995 | if (ret >= 0) | 2106 | if (ret >= 0) |
