Commit a4b46d45 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: core: Fix access violation during port device removal

Testing with KASAN and syzkaller revealed a bug in port.c:disable_store():
usb_hub_to_struct_hub() can return NULL if the hub that the port belongs to
is concurrently removed, but the function does not check for this
possibility before dereferencing the returned value.

It turns out that the first dereference is unnecessary, since hub->intfdev
is the parent of the port device, so it can be changed easily.  Adding a
check for hub == NULL prevents further problems.

The same bug exists in the disable_show() routine, and it can be fixed the
same way.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Reported-and-tested-by: default avatarYue Sun <samsun1006219@gmail.com>
Reported-by: default avatarxingwei lee <xrivendell7@gmail.com>
Link: https://lore.kernel.org/linux-usb/CAEkJfYON+ry7xPx=AiLR9jzUNT+i_Va68ACajOC3HoacOfL1ig@mail.gmail.com/
Fixes: f061f43d ("usb: hub: port: add sysfs entry to switch port power")
CC: Michael Grzeschik <m.grzeschik@pengutronix.de>
CC: stable@vger.kernel.org
Link: https://lore.kernel.org/r/393aa580-15a5-44ca-ad3b-6462461cd313@rowland.harvard.eduSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6d735722
...@@ -51,13 +51,15 @@ static ssize_t disable_show(struct device *dev, ...@@ -51,13 +51,15 @@ static ssize_t disable_show(struct device *dev,
struct usb_port *port_dev = to_usb_port(dev); struct usb_port *port_dev = to_usb_port(dev);
struct usb_device *hdev = to_usb_device(dev->parent->parent); struct usb_device *hdev = to_usb_device(dev->parent->parent);
struct usb_hub *hub = usb_hub_to_struct_hub(hdev); struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
struct usb_interface *intf = to_usb_interface(hub->intfdev); struct usb_interface *intf = to_usb_interface(dev->parent);
int port1 = port_dev->portnum; int port1 = port_dev->portnum;
u16 portstatus, unused; u16 portstatus, unused;
bool disabled; bool disabled;
int rc; int rc;
struct kernfs_node *kn; struct kernfs_node *kn;
if (!hub)
return -ENODEV;
hub_get(hub); hub_get(hub);
rc = usb_autopm_get_interface(intf); rc = usb_autopm_get_interface(intf);
if (rc < 0) if (rc < 0)
...@@ -101,12 +103,14 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr, ...@@ -101,12 +103,14 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr,
struct usb_port *port_dev = to_usb_port(dev); struct usb_port *port_dev = to_usb_port(dev);
struct usb_device *hdev = to_usb_device(dev->parent->parent); struct usb_device *hdev = to_usb_device(dev->parent->parent);
struct usb_hub *hub = usb_hub_to_struct_hub(hdev); struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
struct usb_interface *intf = to_usb_interface(hub->intfdev); struct usb_interface *intf = to_usb_interface(dev->parent);
int port1 = port_dev->portnum; int port1 = port_dev->portnum;
bool disabled; bool disabled;
int rc; int rc;
struct kernfs_node *kn; struct kernfs_node *kn;
if (!hub)
return -ENODEV;
rc = kstrtobool(buf, &disabled); rc = kstrtobool(buf, &disabled);
if (rc) if (rc)
return rc; return rc;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment