diff options
| author | Johan Hovold <jhovold@gmail.com> | 2012-10-25 04:29:15 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-10-25 12:37:13 -0400 |
| commit | f79b2d0fe81eecb412dc48e87a119afc690da8e9 (patch) | |
| tree | 2f5ff17fa309a58dc3745ffb9560ce134bd88f12 | |
| parent | 5260e458f5eff269a43e4f1e9c47186c57b88ddb (diff) | |
USB: keyspan: fix NULL-pointer dereferences and memory leaks
Fix NULL-pointer dereference at release by moving port data allocation
and deallocation to port_probe and port_remove.
Fix NULL-pointer dereference at disconnect by stopping port urbs at
port_remove.
Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no
driver is bound) the port private data is no longer accessible at
disconnect or release.
Note that this patch also fixes port and interface-data memory leaks in
the error path of attach should port initialisation fail for any port.
Compile-only tested.
Cc: <stable@vger.kernel.org>
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/usb/serial/keyspan.c | 181 | ||||
| -rw-r--r-- | drivers/usb/serial/keyspan.h | 8 |
2 files changed, 96 insertions, 93 deletions
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 29c943d737d0..7179b0c5f814 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c | |||
| @@ -1374,13 +1374,9 @@ static struct callbacks { | |||
| 1374 | data in device_details */ | 1374 | data in device_details */ |
| 1375 | static void keyspan_setup_urbs(struct usb_serial *serial) | 1375 | static void keyspan_setup_urbs(struct usb_serial *serial) |
| 1376 | { | 1376 | { |
| 1377 | int i, j; | ||
| 1378 | struct keyspan_serial_private *s_priv; | 1377 | struct keyspan_serial_private *s_priv; |
| 1379 | const struct keyspan_device_details *d_details; | 1378 | const struct keyspan_device_details *d_details; |
| 1380 | struct usb_serial_port *port; | ||
| 1381 | struct keyspan_port_private *p_priv; | ||
| 1382 | struct callbacks *cback; | 1379 | struct callbacks *cback; |
| 1383 | int endp; | ||
| 1384 | 1380 | ||
| 1385 | s_priv = usb_get_serial_data(serial); | 1381 | s_priv = usb_get_serial_data(serial); |
| 1386 | d_details = s_priv->device_details; | 1382 | d_details = s_priv->device_details; |
| @@ -1404,45 +1400,6 @@ static void keyspan_setup_urbs(struct usb_serial *serial) | |||
| 1404 | (serial, d_details->glocont_endpoint, USB_DIR_OUT, | 1400 | (serial, d_details->glocont_endpoint, USB_DIR_OUT, |
| 1405 | serial, s_priv->glocont_buf, GLOCONT_BUFLEN, | 1401 | serial, s_priv->glocont_buf, GLOCONT_BUFLEN, |
| 1406 | cback->glocont_callback); | 1402 | cback->glocont_callback); |
| 1407 | |||
| 1408 | /* Setup endpoints for each port specific thing */ | ||
| 1409 | for (i = 0; i < d_details->num_ports; i++) { | ||
| 1410 | port = serial->port[i]; | ||
| 1411 | p_priv = usb_get_serial_port_data(port); | ||
| 1412 | |||
| 1413 | /* Do indat endpoints first, once for each flip */ | ||
| 1414 | endp = d_details->indat_endpoints[i]; | ||
| 1415 | for (j = 0; j <= d_details->indat_endp_flip; ++j, ++endp) { | ||
| 1416 | p_priv->in_urbs[j] = keyspan_setup_urb | ||
| 1417 | (serial, endp, USB_DIR_IN, port, | ||
| 1418 | p_priv->in_buffer[j], 64, | ||
| 1419 | cback->indat_callback); | ||
| 1420 | } | ||
| 1421 | for (; j < 2; ++j) | ||
| 1422 | p_priv->in_urbs[j] = NULL; | ||
| 1423 | |||
| 1424 | /* outdat endpoints also have flip */ | ||
| 1425 | endp = d_details->outdat_endpoints[i]; | ||
| 1426 | for (j = 0; j <= d_details->outdat_endp_flip; ++j, ++endp) { | ||
| 1427 | p_priv->out_urbs[j] = keyspan_setup_urb | ||
| 1428 | (serial, endp, USB_DIR_OUT, port, | ||
| 1429 | p_priv->out_buffer[j], 64, | ||
| 1430 | cback->outdat_callback); | ||
| 1431 | } | ||
| 1432 | for (; j < 2; ++j) | ||
| 1433 | p_priv->out_urbs[j] = NULL; | ||
| 1434 | |||
| 1435 | /* inack endpoint */ | ||
| 1436 | p_priv->inack_urb = keyspan_setup_urb | ||
| 1437 | (serial, d_details->inack_endpoints[i], USB_DIR_IN, | ||
| 1438 | port, p_priv->inack_buffer, 1, cback->inack_callback); | ||
| 1439 | |||
| 1440 | /* outcont endpoint */ | ||
| 1441 | p_priv->outcont_urb = keyspan_setup_urb | ||
| 1442 | (serial, d_details->outcont_endpoints[i], USB_DIR_OUT, | ||
| 1443 | port, p_priv->outcont_buffer, 64, | ||
| 1444 | cback->outcont_callback); | ||
| 1445 | } | ||
| 1446 | } | 1403 | } |
| 1447 | 1404 | ||
| 1448 | /* usa19 function doesn't require prescaler */ | 1405 | /* usa19 function doesn't require prescaler */ |
| @@ -2407,9 +2364,7 @@ static void keyspan_send_setup(struct usb_serial_port *port, int reset_port) | |||
| 2407 | static int keyspan_startup(struct usb_serial *serial) | 2364 | static int keyspan_startup(struct usb_serial *serial) |
| 2408 | { | 2365 | { |
| 2409 | int i, err; | 2366 | int i, err; |
| 2410 | struct usb_serial_port *port; | ||
| 2411 | struct keyspan_serial_private *s_priv; | 2367 | struct keyspan_serial_private *s_priv; |
| 2412 | struct keyspan_port_private *p_priv; | ||
| 2413 | const struct keyspan_device_details *d_details; | 2368 | const struct keyspan_device_details *d_details; |
| 2414 | 2369 | ||
| 2415 | for (i = 0; (d_details = keyspan_devices[i]) != NULL; ++i) | 2370 | for (i = 0; (d_details = keyspan_devices[i]) != NULL; ++i) |
| @@ -2432,19 +2387,6 @@ static int keyspan_startup(struct usb_serial *serial) | |||
| 2432 | s_priv->device_details = d_details; | 2387 | s_priv->device_details = d_details; |
| 2433 | usb_set_serial_data(serial, s_priv); | 2388 | usb_set_serial_data(serial, s_priv); |
| 2434 | 2389 | ||
| 2435 | /* Now setup per port private data */ | ||
| 2436 | for (i = 0; i < serial->num_ports; i++) { | ||
| 2437 | port = serial->port[i]; | ||
| 2438 | p_priv = kzalloc(sizeof(struct keyspan_port_private), | ||
| 2439 | GFP_KERNEL); | ||
| 2440 | if (!p_priv) { | ||
| 2441 | dev_dbg(&port->dev, "%s - kmalloc for keyspan_port_private (%d) failed!.\n", __func__, i); | ||
| 2442 | return 1; | ||
| 2443 | } | ||
| 2444 | p_priv->device_details = d_details; | ||
| 2445 | usb_set_serial_port_data(port, p_priv); | ||
| 2446 | } | ||
| 2447 | |||
| 2448 | keyspan_setup_urbs(serial); | 2390 | keyspan_setup_urbs(serial); |
| 2449 | 2391 | ||
| 2450 | if (s_priv->instat_urb != NULL) { | 2392 | if (s_priv->instat_urb != NULL) { |
| @@ -2463,59 +2405,112 @@ static int keyspan_startup(struct usb_serial *serial) | |||
| 2463 | 2405 | ||
| 2464 | static void keyspan_disconnect(struct usb_serial *serial) | 2406 | static void keyspan_disconnect(struct usb_serial *serial) |
| 2465 | { | 2407 | { |
| 2466 | int i, j; | 2408 | struct keyspan_serial_private *s_priv; |
| 2467 | struct usb_serial_port *port; | ||
| 2468 | struct keyspan_serial_private *s_priv; | ||
| 2469 | struct keyspan_port_private *p_priv; | ||
| 2470 | 2409 | ||
| 2471 | s_priv = usb_get_serial_data(serial); | 2410 | s_priv = usb_get_serial_data(serial); |
| 2472 | 2411 | ||
| 2473 | /* Stop reading/writing urbs */ | ||
| 2474 | stop_urb(s_priv->instat_urb); | 2412 | stop_urb(s_priv->instat_urb); |
| 2475 | stop_urb(s_priv->glocont_urb); | 2413 | stop_urb(s_priv->glocont_urb); |
| 2476 | stop_urb(s_priv->indat_urb); | 2414 | stop_urb(s_priv->indat_urb); |
| 2477 | for (i = 0; i < serial->num_ports; ++i) { | 2415 | } |
| 2478 | port = serial->port[i]; | 2416 | |
| 2479 | p_priv = usb_get_serial_port_data(port); | 2417 | static void keyspan_release(struct usb_serial *serial) |
| 2480 | stop_urb(p_priv->inack_urb); | 2418 | { |
| 2481 | stop_urb(p_priv->outcont_urb); | 2419 | struct keyspan_serial_private *s_priv; |
| 2482 | for (j = 0; j < 2; j++) { | 2420 | |
| 2483 | stop_urb(p_priv->in_urbs[j]); | 2421 | s_priv = usb_get_serial_data(serial); |
| 2484 | stop_urb(p_priv->out_urbs[j]); | ||
| 2485 | } | ||
| 2486 | } | ||
| 2487 | 2422 | ||
| 2488 | /* Now free them */ | ||
| 2489 | usb_free_urb(s_priv->instat_urb); | 2423 | usb_free_urb(s_priv->instat_urb); |
| 2490 | usb_free_urb(s_priv->indat_urb); | 2424 | usb_free_urb(s_priv->indat_urb); |
| 2491 | usb_free_urb(s_priv->glocont_urb); | 2425 | usb_free_urb(s_priv->glocont_urb); |
| 2492 | for (i = 0; i < serial->num_ports; ++i) { | 2426 | |
| 2493 | port = serial->port[i]; | 2427 | kfree(s_priv); |
| 2494 | p_priv = usb_get_serial_port_data(port); | ||
| 2495 | usb_free_urb(p_priv->inack_urb); | ||
| 2496 | usb_free_urb(p_priv->outcont_urb); | ||
| 2497 | for (j = 0; j < 2; j++) { | ||
| 2498 | usb_free_urb(p_priv->in_urbs[j]); | ||
| 2499 | usb_free_urb(p_priv->out_urbs[j]); | ||
| 2500 | } | ||
| 2501 | } | ||
| 2502 | } | 2428 | } |
| 2503 | 2429 | ||
| 2504 | static void keyspan_release(struct usb_serial *serial) | 2430 | static int keyspan_port_probe(struct usb_serial_port *port) |
| 2505 | { | 2431 | { |
| 2506 | int i; | 2432 | struct usb_serial *serial = port->serial; |
| 2507 | struct usb_serial_port *port; | 2433 | struct keyspan_port_private *s_priv; |
| 2508 | struct keyspan_serial_private *s_priv; | 2434 | struct keyspan_port_private *p_priv; |
| 2435 | const struct keyspan_device_details *d_details; | ||
| 2436 | struct callbacks *cback; | ||
| 2437 | int endp; | ||
| 2438 | int port_num; | ||
| 2439 | int i; | ||
| 2509 | 2440 | ||
| 2510 | s_priv = usb_get_serial_data(serial); | 2441 | s_priv = usb_get_serial_data(serial); |
| 2442 | d_details = s_priv->device_details; | ||
| 2511 | 2443 | ||
| 2512 | kfree(s_priv); | 2444 | p_priv = kzalloc(sizeof(*p_priv), GFP_KERNEL); |
| 2445 | if (!p_priv) | ||
| 2446 | return -ENOMEM; | ||
| 2513 | 2447 | ||
| 2514 | /* Now free per port private data */ | 2448 | s_priv = usb_get_serial_data(port->serial); |
| 2515 | for (i = 0; i < serial->num_ports; i++) { | 2449 | p_priv->device_details = d_details; |
| 2516 | port = serial->port[i]; | 2450 | |
| 2517 | kfree(usb_get_serial_port_data(port)); | 2451 | /* Setup values for the various callback routines */ |
| 2452 | cback = &keyspan_callbacks[d_details->msg_format]; | ||
| 2453 | |||
| 2454 | port_num = port->number - port->serial->minor; | ||
| 2455 | |||
| 2456 | /* Do indat endpoints first, once for each flip */ | ||
| 2457 | endp = d_details->indat_endpoints[port_num]; | ||
| 2458 | for (i = 0; i <= d_details->indat_endp_flip; ++i, ++endp) { | ||
| 2459 | p_priv->in_urbs[i] = keyspan_setup_urb(serial, endp, | ||
| 2460 | USB_DIR_IN, port, | ||
| 2461 | p_priv->in_buffer[i], 64, | ||
| 2462 | cback->indat_callback); | ||
| 2463 | } | ||
| 2464 | /* outdat endpoints also have flip */ | ||
| 2465 | endp = d_details->outdat_endpoints[port_num]; | ||
| 2466 | for (i = 0; i <= d_details->outdat_endp_flip; ++i, ++endp) { | ||
| 2467 | p_priv->out_urbs[i] = keyspan_setup_urb(serial, endp, | ||
| 2468 | USB_DIR_OUT, port, | ||
| 2469 | p_priv->out_buffer[i], 64, | ||
| 2470 | cback->outdat_callback); | ||
| 2471 | } | ||
| 2472 | /* inack endpoint */ | ||
| 2473 | p_priv->inack_urb = keyspan_setup_urb(serial, | ||
| 2474 | d_details->inack_endpoints[port_num], | ||
| 2475 | USB_DIR_IN, port, | ||
| 2476 | p_priv->inack_buffer, 1, | ||
| 2477 | cback->inack_callback); | ||
| 2478 | /* outcont endpoint */ | ||
| 2479 | p_priv->outcont_urb = keyspan_setup_urb(serial, | ||
| 2480 | d_details->outcont_endpoints[port_num], | ||
| 2481 | USB_DIR_OUT, port, | ||
| 2482 | p_priv->outcont_buffer, 64, | ||
| 2483 | cback->outcont_callback); | ||
| 2484 | |||
| 2485 | usb_set_serial_port_data(port, p_priv); | ||
| 2486 | |||
| 2487 | return 0; | ||
| 2488 | } | ||
| 2489 | |||
| 2490 | static int keyspan_port_remove(struct usb_serial_port *port) | ||
| 2491 | { | ||
| 2492 | struct keyspan_port_private *p_priv; | ||
| 2493 | int i; | ||
| 2494 | |||
| 2495 | p_priv = usb_get_serial_port_data(port); | ||
| 2496 | |||
| 2497 | stop_urb(p_priv->inack_urb); | ||
| 2498 | stop_urb(p_priv->outcont_urb); | ||
| 2499 | for (i = 0; i < 2; i++) { | ||
| 2500 | stop_urb(p_priv->in_urbs[i]); | ||
| 2501 | stop_urb(p_priv->out_urbs[i]); | ||
| 2502 | } | ||
| 2503 | |||
| 2504 | usb_free_urb(p_priv->inack_urb); | ||
| 2505 | usb_free_urb(p_priv->outcont_urb); | ||
| 2506 | for (i = 0; i < 2; i++) { | ||
| 2507 | usb_free_urb(p_priv->in_urbs[i]); | ||
| 2508 | usb_free_urb(p_priv->out_urbs[i]); | ||
| 2518 | } | 2509 | } |
| 2510 | |||
| 2511 | kfree(p_priv); | ||
| 2512 | |||
| 2513 | return 0; | ||
| 2519 | } | 2514 | } |
| 2520 | 2515 | ||
| 2521 | MODULE_AUTHOR(DRIVER_AUTHOR); | 2516 | MODULE_AUTHOR(DRIVER_AUTHOR); |
diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 0a8a40b5711e..0273dda303a4 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h | |||
| @@ -42,6 +42,8 @@ static void keyspan_dtr_rts (struct usb_serial_port *port, int on); | |||
| 42 | static int keyspan_startup (struct usb_serial *serial); | 42 | static int keyspan_startup (struct usb_serial *serial); |
| 43 | static void keyspan_disconnect (struct usb_serial *serial); | 43 | static void keyspan_disconnect (struct usb_serial *serial); |
| 44 | static void keyspan_release (struct usb_serial *serial); | 44 | static void keyspan_release (struct usb_serial *serial); |
| 45 | static int keyspan_port_probe(struct usb_serial_port *port); | ||
| 46 | static int keyspan_port_remove(struct usb_serial_port *port); | ||
| 45 | static int keyspan_write_room (struct tty_struct *tty); | 47 | static int keyspan_write_room (struct tty_struct *tty); |
| 46 | 48 | ||
| 47 | static int keyspan_write (struct tty_struct *tty, | 49 | static int keyspan_write (struct tty_struct *tty, |
| @@ -567,6 +569,8 @@ static struct usb_serial_driver keyspan_1port_device = { | |||
| 567 | .attach = keyspan_startup, | 569 | .attach = keyspan_startup, |
| 568 | .disconnect = keyspan_disconnect, | 570 | .disconnect = keyspan_disconnect, |
| 569 | .release = keyspan_release, | 571 | .release = keyspan_release, |
| 572 | .port_probe = keyspan_port_probe, | ||
| 573 | .port_remove = keyspan_port_remove, | ||
| 570 | }; | 574 | }; |
| 571 | 575 | ||
| 572 | static struct usb_serial_driver keyspan_2port_device = { | 576 | static struct usb_serial_driver keyspan_2port_device = { |
| @@ -589,6 +593,8 @@ static struct usb_serial_driver keyspan_2port_device = { | |||
| 589 | .attach = keyspan_startup, | 593 | .attach = keyspan_startup, |
| 590 | .disconnect = keyspan_disconnect, | 594 | .disconnect = keyspan_disconnect, |
| 591 | .release = keyspan_release, | 595 | .release = keyspan_release, |
| 596 | .port_probe = keyspan_port_probe, | ||
| 597 | .port_remove = keyspan_port_remove, | ||
| 592 | }; | 598 | }; |
| 593 | 599 | ||
| 594 | static struct usb_serial_driver keyspan_4port_device = { | 600 | static struct usb_serial_driver keyspan_4port_device = { |
| @@ -611,6 +617,8 @@ static struct usb_serial_driver keyspan_4port_device = { | |||
| 611 | .attach = keyspan_startup, | 617 | .attach = keyspan_startup, |
| 612 | .disconnect = keyspan_disconnect, | 618 | .disconnect = keyspan_disconnect, |
| 613 | .release = keyspan_release, | 619 | .release = keyspan_release, |
| 620 | .port_probe = keyspan_port_probe, | ||
| 621 | .port_remove = keyspan_port_remove, | ||
| 614 | }; | 622 | }; |
| 615 | 623 | ||
| 616 | static struct usb_serial_driver * const serial_drivers[] = { | 624 | static struct usb_serial_driver * const serial_drivers[] = { |
