Skip to content

Commit

Permalink
usb: gadget: printer: fix races against disable
Browse files Browse the repository at this point in the history
commit e587a76 upstream.

printer_read() and printer_write() guard against the race
against disable() by checking the dev->interface flag,
which in turn is guarded by a spinlock.
These functions, however, drop the lock on multiple occasions.
This means that the test has to be redone after reacquiring
the lock and before doing IO.

Add the tests.

This also addresses CVE-2024-25741

Fixes: 7f2ca14 ("usb: gadget: function: printer: Interface is disabled and returns error")
Cc: stable <[email protected]>
Signed-off-by: Oliver Neukum <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
oneukum authored and gregkh committed Jul 5, 2024
1 parent 9dec26e commit a96915f
Showing 1 changed file with 29 additions and 10 deletions.
39 changes: 29 additions & 10 deletions drivers/usb/gadget/function/f_printer.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,18 +450,18 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);

if (dev->interface < 0) {
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -ENODEV;
}
if (dev->interface < 0)
goto out_disabled;

/* We will use this flag later to check if a printer reset happened
* after we turn interrupts back on.
*/
dev->reset_printer = 0;

setup_rx_reqs(dev);
/* this dropped the lock - need to retest */
if (dev->interface < 0)
goto out_disabled;

bytes_copied = 0;
current_rx_req = dev->current_rx_req;
Expand Down Expand Up @@ -495,6 +495,8 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
wait_event_interruptible(dev->rx_wait,
(likely(!list_empty(&dev->rx_buffers))));
spin_lock_irqsave(&dev->lock, flags);
if (dev->interface < 0)
goto out_disabled;
}

/* We have data to return then copy it to the caller's buffer.*/
Expand Down Expand Up @@ -538,6 +540,9 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
return -EAGAIN;
}

if (dev->interface < 0)
goto out_disabled;

/* If we not returning all the data left in this RX request
* buffer then adjust the amount of data left in the buffer.
* Othewise if we are done with this RX request buffer then
Expand Down Expand Up @@ -567,6 +572,11 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
return bytes_copied;
else
return -EAGAIN;

out_disabled:
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -ENODEV;
}

static ssize_t
Expand All @@ -587,11 +597,8 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);

if (dev->interface < 0) {
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -ENODEV;
}
if (dev->interface < 0)
goto out_disabled;

/* Check if a printer reset happens while we have interrupts on */
dev->reset_printer = 0;
Expand All @@ -614,6 +621,8 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
wait_event_interruptible(dev->tx_wait,
(likely(!list_empty(&dev->tx_reqs))));
spin_lock_irqsave(&dev->lock, flags);
if (dev->interface < 0)
goto out_disabled;
}

while (likely(!list_empty(&dev->tx_reqs)) && len) {
Expand Down Expand Up @@ -663,6 +672,9 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
return -EAGAIN;
}

if (dev->interface < 0)
goto out_disabled;

list_add(&req->list, &dev->tx_reqs_active);

/* here, we unlock, and only unlock, to avoid deadlock. */
Expand All @@ -675,6 +687,8 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
if (dev->interface < 0)
goto out_disabled;
}

spin_unlock_irqrestore(&dev->lock, flags);
Expand All @@ -686,6 +700,11 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
return bytes_copied;
else
return -EAGAIN;

out_disabled:
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -ENODEV;
}

static int
Expand Down

0 comments on commit a96915f

Please sign in to comment.