diff options
author | Andrey Konovalov <andreyknvl@google.com> | 2020-04-07 16:47:54 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-04-16 14:52:14 +0200 |
commit | fdd10499de0dbcd2cdfe8108ed162bb00fe6f276 (patch) | |
tree | 0776ad4bac82d93fdf3aa428f13b289d84cea04b | |
parent | usb: raw-gadget: Fix copy_to/from_user() checks (diff) | |
download | linux-fdd10499de0dbcd2cdfe8108ed162bb00fe6f276.tar.xz linux-fdd10499de0dbcd2cdfe8108ed162bb00fe6f276.zip |
usb: raw-gadget: fix raw_event_queue_fetch locking
If queue->size check in raw_event_queue_fetch() fails (which normally
shouldn't happen, that check is a fail-safe), the function returns
without reenabling interrupts. This patch fixes that issue, along with
propagating the cause of failure to the function caller.
Fixes: f2c2e717642c ("usb: gadget: add raw-gadget interface")
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Andrey Konovalov <andreyknvl@google.com>
Link: https://lore.kernel.org/r/9f7ce7a1472cfb9447f6c5a494186fa1f2670f6f.1586270396.git.andreyknvl@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/gadget/legacy/raw_gadget.c | 25 |
1 files changed, 20 insertions, 5 deletions
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c index e490ffa1f58b..ca7d95bf7397 100644 --- a/drivers/usb/gadget/legacy/raw_gadget.c +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -81,6 +81,7 @@ static int raw_event_queue_add(struct raw_event_queue *queue, static struct usb_raw_event *raw_event_queue_fetch( struct raw_event_queue *queue) { + int ret; unsigned long flags; struct usb_raw_event *event; @@ -89,11 +90,18 @@ static struct usb_raw_event *raw_event_queue_fetch( * there's at least one event queued by decrementing the semaphore, * and then take the lock to protect queue struct fields. */ - if (down_interruptible(&queue->sema)) - return NULL; + ret = down_interruptible(&queue->sema); + if (ret) + return ERR_PTR(ret); spin_lock_irqsave(&queue->lock, flags); - if (WARN_ON(!queue->size)) - return NULL; + /* + * queue->size must have the same value as queue->sema counter (before + * the down_interruptible() call above), so this check is a fail-safe. + */ + if (WARN_ON(!queue->size)) { + spin_unlock_irqrestore(&queue->lock, flags); + return ERR_PTR(-ENODEV); + } event = queue->events[0]; queue->size--; memmove(&queue->events[0], &queue->events[1], @@ -522,10 +530,17 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value) spin_unlock_irqrestore(&dev->lock, flags); event = raw_event_queue_fetch(&dev->queue); - if (!event) { + if (PTR_ERR(event) == -EINTR) { dev_dbg(&dev->gadget->dev, "event fetching interrupted\n"); return -EINTR; } + if (IS_ERR(event)) { + dev_err(&dev->gadget->dev, "failed to fetch event\n"); + spin_lock_irqsave(&dev->lock, flags); + dev->state = STATE_DEV_FAILED; + spin_unlock_irqrestore(&dev->lock, flags); + return -ENODEV; + } length = min(arg.length, event->length); if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) return -EFAULT; |