Skip to content

Commit

Permalink
usb: roles: fix NULL pointer issue when put module's reference
Browse files Browse the repository at this point in the history
commit 1c9be13 upstream.

In current design, usb role class driver will get usb_role_switch parent's
module reference after the user get usb_role_switch device and put the
reference after the user put the usb_role_switch device. However, the
parent device of usb_role_switch may be removed before the user put the
usb_role_switch. If so, then, NULL pointer issue will be met when the user
put the parent module's reference.

This will save the module pointer in structure of usb_role_switch. Then,
we don't need to find module by iterating long relations.

Fixes: 5c54fca ("usb: roles: Take care of driver module reference counting")
cc: stable@vger.kernel.org
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20240129093739.2371530-1-xu.yang_2@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Xu Yang authored and gregkh committed Mar 1, 2024
1 parent da7fc10 commit 4b45829
Showing 1 changed file with 11 additions and 6 deletions.
17 changes: 11 additions & 6 deletions drivers/usb/roles/class.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ static const struct class role_class = {
struct usb_role_switch {
struct device dev;
struct mutex lock; /* device lock*/
struct module *module; /* the module this device depends on */
enum usb_role role;

/* From descriptor */
Expand Down Expand Up @@ -135,7 +136,7 @@ struct usb_role_switch *usb_role_switch_get(struct device *dev)
usb_role_switch_match);

if (!IS_ERR_OR_NULL(sw))
WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
WARN_ON(!try_module_get(sw->module));

return sw;
}
Expand All @@ -157,7 +158,7 @@ struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode)
sw = fwnode_connection_find_match(fwnode, "usb-role-switch",
NULL, usb_role_switch_match);
if (!IS_ERR_OR_NULL(sw))
WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
WARN_ON(!try_module_get(sw->module));

return sw;
}
Expand All @@ -172,7 +173,7 @@ EXPORT_SYMBOL_GPL(fwnode_usb_role_switch_get);
void usb_role_switch_put(struct usb_role_switch *sw)
{
if (!IS_ERR_OR_NULL(sw)) {
module_put(sw->dev.parent->driver->owner);
module_put(sw->module);
put_device(&sw->dev);
}
}
Expand All @@ -189,15 +190,18 @@ struct usb_role_switch *
usb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode)
{
struct device *dev;
struct usb_role_switch *sw = NULL;

if (!fwnode)
return NULL;

dev = class_find_device_by_fwnode(&role_class, fwnode);
if (dev)
WARN_ON(!try_module_get(dev->parent->driver->owner));
if (dev) {
sw = to_role_switch(dev);
WARN_ON(!try_module_get(sw->module));
}

return dev ? to_role_switch(dev) : NULL;
return sw;
}
EXPORT_SYMBOL_GPL(usb_role_switch_find_by_fwnode);

Expand Down Expand Up @@ -338,6 +342,7 @@ usb_role_switch_register(struct device *parent,
sw->set = desc->set;
sw->get = desc->get;

sw->module = parent->driver->owner;
sw->dev.parent = parent;
sw->dev.fwnode = desc->fwnode;
sw->dev.class = &role_class;
Expand Down

0 comments on commit 4b45829

Please sign in to comment.