]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
usb: gadget: printer: fix infinite loop in printer_read()
authorMelbin K Mathew <mlbnkm1@gmail.com>
Thu, 9 Jul 2026 20:56:22 +0000 (21:56 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 13 Jul 2026 05:10:11 +0000 (07:10 +0200)
printer_read() uses the same variable for the requested copy size and
the number of bytes actually copied to user space. copy_to_user()
returns the number of bytes not copied, so when it fails to copy
anything, the computed copied length becomes zero.

In that case len, buf, current_rx_bytes and current_rx_buf are left
unchanged. If RX data is available and the user buffer remains
unwritable, the read loop can repeat indefinitely.

Track the copied length separately and return -EFAULT, or the number of
bytes already copied, if an iteration makes no progress.

Fixes: b185f01a9ab7 ("usb: gadget: printer: factor out f_printer")
Cc: stable <stable@kernel.org>
Reviewed-by: Peter Chen <peter.chen@kernel.org>
Signed-off-by: Melbin K Mathew <mlbnkm1@gmail.com>
Link: https://patch.msgid.link/20260709205622.55700-1-mlbnkm1@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/function/f_printer.c

index 837f753d0cae594b66fbcd51b3ffcc291221c952..1857d786110b4f16777a6ee926ae336e2f200116 100644 (file)
@@ -431,7 +431,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
 {
        struct printer_dev              *dev = fd->private_data;
        unsigned long                   flags;
-       size_t                          size;
+       size_t                          size, not_copied, copied;
        size_t                          bytes_copied;
        struct usb_request              *req;
        /* This is a pointer to the current USB rx request. */
@@ -524,10 +524,12 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
                else
                        size = len;
 
-               size -= copy_to_user(buf, current_rx_buf, size);
-               bytes_copied += size;
-               len -= size;
-               buf += size;
+               not_copied = copy_to_user(buf, current_rx_buf, size);
+               copied = size - not_copied;
+
+               bytes_copied += copied;
+               len -= copied;
+               buf += copied;
 
                spin_lock_irqsave(&dev->lock, flags);
 
@@ -542,6 +544,17 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
                if (dev->interface < 0)
                        goto out_disabled;
 
+               if (!copied) {
+                       dev->current_rx_req = current_rx_req;
+                       dev->current_rx_bytes = current_rx_bytes;
+                       dev->current_rx_buf = current_rx_buf;
+                       spin_unlock_irqrestore(&dev->lock, flags);
+                       mutex_unlock(&dev->lock_printer_io);
+                       return bytes_copied ? bytes_copied : -EFAULT;
+               }
+
+               size = copied;
+
                /* 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