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.