ANDROID: usb: gadget: fix NULL pointer dereference in android_setup

This is a possibility in android_setup when using cdev leading
to a NULL pointer dereference in spin_lock_irqsave. Using the
spinlock of gadget item to prevent the condition.

Bug: 189800931
Signed-off-by: Ray Chi <raychi@google.com>
Change-Id: Idc4cbcaed7dc6e1e35e8a63de84c1415fb83ef5e
This commit is contained in:
Ray Chi
2021-07-08 17:15:26 +08:00
committed by Greg Kroah-Hartman
parent 07f65598af
commit dd139186ef

View File

@@ -1534,18 +1534,28 @@ static void configfs_composite_unbind(struct usb_gadget *gadget)
static int android_setup(struct usb_gadget *gadget, static int android_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *c) const struct usb_ctrlrequest *c)
{ {
struct usb_composite_dev *cdev = get_gadget_data(gadget); struct usb_composite_dev *cdev;
unsigned long flags; unsigned long flags;
struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); struct gadget_info *gi;
int value = -EOPNOTSUPP; int value = -EOPNOTSUPP;
struct usb_function_instance *fi; struct usb_function_instance *fi;
spin_lock_irqsave(&cdev->lock, flags); if (!android_device)
return 0;
gi = dev_get_drvdata(android_device);
spin_lock_irqsave(&gi->spinlock, flags);
cdev = get_gadget_data(gadget);
if (!cdev || gi->unbind) {
spin_unlock_irqrestore(&gi->spinlock, flags);
return 0;
}
if (!gi->connected) { if (!gi->connected) {
gi->connected = 1; gi->connected = 1;
schedule_work(&gi->work); schedule_work(&gi->work);
} }
spin_unlock_irqrestore(&cdev->lock, flags);
list_for_each_entry(fi, &gi->available_func, cfs_list) { list_for_each_entry(fi, &gi->available_func, cfs_list) {
if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) { if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) {
value = fi->f->setup(fi->f, c); value = fi->f->setup(fi->f, c);
@@ -1562,12 +1572,11 @@ static int android_setup(struct usb_gadget *gadget,
if (value < 0) if (value < 0)
value = composite_setup(gadget, c); value = composite_setup(gadget, c);
spin_lock_irqsave(&cdev->lock, flags);
if (c->bRequest == USB_REQ_SET_CONFIGURATION && if (c->bRequest == USB_REQ_SET_CONFIGURATION &&
cdev->config) { cdev->config) {
schedule_work(&gi->work); schedule_work(&gi->work);
} }
spin_unlock_irqrestore(&cdev->lock, flags); spin_unlock_irqrestore(&gi->spinlock, flags);
return value; return value;
} }