In usb_add_phy_dev(), x->dev->type is overwritten with &usb_phy_dev_type.
However, the underlying struct device is allocated and managed by a parent
subsystem or bus driver, such as an I2C client device in phy-isp1301.
Overwriting x->dev->type clobbers the original device type (e.g.
i2c_client_type) and removes its lifecycle release callback (such as
i2c_client_dev_release).
When an I2C client device using a USB PHY is deregistered and its reference
count drops to zero, device_release() falls back to dev->type->release
since dev->release is NULL for I2C clients. Because usb_phy_dev_type does
not provide a .release callback, a warning is triggered and the memory of
the client structure is leaked:
Device '2-002c' does not have a release() function, it is broken and must
be fixed. See Documentation/core-api/kobject.rst.
WARNING: drivers/base/core.c:2643 at device_release+0x194/0x1f0
drivers/base/core.c:2642
Call Trace:
kobject_cleanup lib/kobject.c:689 [inline]
kobject_release lib/kobject.c:720 [inline]
kref_put include/linux/kref.h:65 [inline]
kobject_put+0x222/0x550 lib/kobject.c:737
i2c_deregister_clients+0x281/0x3a0 drivers/i2c/i2c-core-base.c:1783
i2c_del_adapter+0x124/0x440 drivers/i2c/i2c-core-base.c:1827
i2c_tiny_usb_disconnect+0x49/0xd0 drivers/i2c/busses/i2c-tiny-usb.c:282
usb_unbind_interface+0x295/0x9f0 drivers/usb/core/driver.c:461
device_remove drivers/base/dd.c:618 [inline]
__device_release_driver drivers/base/dd.c:1349 [inline]
device_release_driver_internal+0x4f5/0x880 drivers/base/dd.c:1372
bus_remove_device+0x444/0x560 drivers/base/bus.c:664
device_del+0x524/0x8f0 drivers/base/core.c:3965
Platform devices were unaffected because platform_device_release is
assigned directly to dev->release, which device_release() checks before
dev->type->release. However, subsystems should not overwrite dev->type on
devices they do not own.
Fix this by removing usb_phy_dev_type and its assignment in
usb_add_phy_dev(). To maintain the charger uevents, construct the
environment variables directly and pass them to kobject_uevent_env() in
usb_phy_notify_charger_work().
Fixes: a8534cb092d7 ("usb: phy: introduce usb_phy device type with its own uevent handler")
Assisted-by: Gemini:gemini-3.7-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: syzbot+3fb7629cfd12d04beeab@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=3fb7629cfd12d04beeab
Link: https://syzkaller.appspot.com/ai_job?id=313c85bd-96bf-44e8-a1a6-e25813b43afe
To: "Greg Kroah-Hartman"
To:
To: "Grzegorz Jaszczyk"
Cc: "Diogo Ivo"
Cc:
---
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index 5a9b9353f..7c85c7ca1 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -42,12 +42,6 @@ static const char *const usb_chger_type[] = {
[ACA_TYPE] = "USB_CHARGER_ACA_TYPE",
};
-static const char *const usb_chger_state[] = {
- [USB_CHARGER_DEFAULT] = "USB_CHARGER_DEFAULT",
- [USB_CHARGER_PRESENT] = "USB_CHARGER_PRESENT",
- [USB_CHARGER_ABSENT] = "USB_CHARGER_ABSENT",
-};
-
static struct usb_phy *__usb_find_phy(struct list_head *list,
enum usb_phy_type type)
{
@@ -80,18 +74,6 @@ static struct usb_phy *__of_usb_find_phy(struct device_node *node)
return ERR_PTR(-EPROBE_DEFER);
}
-static struct usb_phy *__device_to_usb_phy(const struct device *dev)
-{
- struct usb_phy *usb_phy;
-
- list_for_each_entry(usb_phy, &phy_list, head) {
- if (usb_phy->dev == dev)
- return usb_phy;
- }
-
- return NULL;
-}
-
static void usb_phy_set_default_current(struct usb_phy *usb_phy)
{
usb_phy->chg_cur.sdp_min = DEFAULT_SDP_CUR_MIN;
@@ -123,6 +105,9 @@ static void usb_phy_set_default_current(struct usb_phy *usb_phy)
static void usb_phy_notify_charger_work(struct work_struct *work)
{
struct usb_phy *usb_phy = container_of(work, struct usb_phy, chg_work);
+ char uchger_state[50] = { 0 };
+ char uchger_type[50] = { 0 };
+ char *envp[] = { uchger_state, uchger_type, NULL };
unsigned int min, max;
switch (usb_phy->chg_state) {
@@ -130,11 +115,15 @@ static void usb_phy_notify_charger_work(struct work_struct *work)
usb_phy_get_charger_current(usb_phy, &min, &max);
atomic_notifier_call_chain(&usb_phy->notifier, max, usb_phy);
+ snprintf(uchger_state, ARRAY_SIZE(uchger_state),
+ "USB_CHARGER_STATE=%s", "USB_CHARGER_PRESENT");
break;
case USB_CHARGER_ABSENT:
usb_phy_set_default_current(usb_phy);
atomic_notifier_call_chain(&usb_phy->notifier, 0, usb_phy);
+ snprintf(uchger_state, ARRAY_SIZE(uchger_state),
+ "USB_CHARGER_STATE=%s", "USB_CHARGER_ABSENT");
break;
default:
dev_warn(usb_phy->dev, "Unknown USB charger state: %d\n",
@@ -142,36 +131,9 @@ static void usb_phy_notify_charger_work(struct work_struct *work)
return;
}
- kobject_uevent(&usb_phy->dev->kobj, KOBJ_CHANGE);
-}
-
-static int usb_phy_uevent(const struct device *dev, struct kobj_uevent_env *env)
-{
- const struct usb_phy *usb_phy;
- char uchger_state[50] = { 0 };
- char uchger_type[50] = { 0 };
- unsigned long flags;
-
- spin_lock_irqsave(&phy_lock, flags);
- usb_phy = __device_to_usb_phy(dev);
- spin_unlock_irqrestore(&phy_lock, flags);
-
- if (!usb_phy)
- return -ENODEV;
-
- snprintf(uchger_state, ARRAY_SIZE(uchger_state),
- "USB_CHARGER_STATE=%s", usb_chger_state[usb_phy->chg_state]);
-
snprintf(uchger_type, ARRAY_SIZE(uchger_type),
"USB_CHARGER_TYPE=%s", usb_chger_type[usb_phy->chg_type]);
-
- if (add_uevent_var(env, uchger_state))
- return -ENOMEM;
-
- if (add_uevent_var(env, uchger_type))
- return -ENOMEM;
-
- return 0;
+ kobject_uevent_env(&usb_phy->dev->kobj, KOBJ_CHANGE, envp);
}
static void __usb_phy_get_charger_type(struct usb_phy *usb_phy)
@@ -675,11 +637,6 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
}
EXPORT_SYMBOL_GPL(usb_add_phy);
-static const struct device_type usb_phy_dev_type = {
- .name = "usb_phy",
- .uevent = usb_phy_uevent,
-};
-
/**
* usb_add_phy_dev - declare the USB PHY
* @x: the USB phy to be used; or NULL
@@ -705,8 +662,6 @@ int usb_add_phy_dev(struct usb_phy *x)
if (ret)
return ret;
- x->dev->type = &usb_phy_dev_type;
-
ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier);
spin_lock_irqsave(&phy_lock, flags);
base-commit: df2908090cda368b01ff43709f51890076c56157
--
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at syzkaller@googlegroups.com.