From: Alan Stern Subject: [PATCH] USB: gadget: fix udc_lock scope around usb_udc_uevent CVE-2022-49980 (2191c00855b0) takes udc_lock inside usb_udc_uevent(). On msm-4.14 that callback still runs synchronously from device_add() and kobject_uevent() while the same thread already holds udc_lock, so the mutex deadlocks and the phone hangs on the Google logo. Linux 6.1 usb_add_gadget() / gadget_bind_driver() only hold udc_lock around the list and the driver pointer. This keeps the 4.14 call shape and moves those uevents outside the lock, then applies the UAF fix. Do not apply 2191c00855b0 without this. Signed-off-by: Alan Stern --- --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1229,11 +1229,18 @@ mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); + mutex_unlock(&udc_lock); + /* + * device_add() emits a uevent that calls usb_udc_uevent(), which + * takes udc_lock. Same for device_del() on the error path. v6.1 + * usb_add_gadget() only holds the mutex around the list update. + */ ret = device_add(&udc->dev); if (ret) goto err_unlist_udc; + mutex_lock(&udc_lock); usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); udc->vbus = true; @@ -1247,9 +1254,11 @@ return 0; err_del_udc: + mutex_unlock(&udc_lock); device_del(&udc->dev); err_unlist_udc: + mutex_lock(&udc_lock); list_del(&udc->list); mutex_unlock(&udc_lock); @@ -1313,7 +1322,14 @@ dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", udc->driver->function); + /* + * Caller holds udc_lock. usb_udc_uevent() takes it too, so the + * CHANGE uevent has to run outside the lock. Driver is still bound; + * the callback re-locks to read it. + */ + mutex_unlock(&udc_lock); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + mutex_lock(&udc_lock); usb_gadget_disconnect(udc->gadget); udc->driver->disconnect(udc->gadget); @@ -1383,7 +1399,10 @@ } usb_udc_connect_control(udc); + /* Same deadlock as remove: uevent re-enters udc_lock. */ + mutex_unlock(&udc_lock); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + mutex_lock(&udc_lock); return 0; err1: if (ret != -EISNAM) @@ -1611,13 +1630,14 @@ return ret; } - if (udc->driver) { + mutex_lock(&udc_lock); + if (udc->driver) ret = add_uevent_var(env, "USB_UDC_DRIVER=%s", udc->driver->function); - if (ret) { - dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n"); - return ret; - } + mutex_unlock(&udc_lock); + if (ret) { + dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n"); + return ret; } return 0;