From dd139186ef9dfdd14797cce271f59f72de021d10 Mon Sep 17 00:00:00 2001 From: Ray Chi Date: Thu, 8 Jul 2021 17:15:26 +0800 Subject: [PATCH] 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 Change-Id: Idc4cbcaed7dc6e1e35e8a63de84c1415fb83ef5e --- drivers/usb/gadget/configfs.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 5fb6b14b73f5..9fa88eb25af2 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1534,18 +1534,28 @@ static void configfs_composite_unbind(struct usb_gadget *gadget) static int android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) { - struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_composite_dev *cdev; unsigned long flags; - struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + struct gadget_info *gi; int value = -EOPNOTSUPP; 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) { gi->connected = 1; schedule_work(&gi->work); } - spin_unlock_irqrestore(&cdev->lock, flags); + list_for_each_entry(fi, &gi->available_func, cfs_list) { if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) { value = fi->f->setup(fi->f, c); @@ -1562,12 +1572,11 @@ static int android_setup(struct usb_gadget *gadget, if (value < 0) value = composite_setup(gadget, c); - spin_lock_irqsave(&cdev->lock, flags); if (c->bRequest == USB_REQ_SET_CONFIGURATION && cdev->config) { schedule_work(&gi->work); } - spin_unlock_irqrestore(&cdev->lock, flags); + spin_unlock_irqrestore(&gi->spinlock, flags); return value; }