lkml.org 
[lkml]   [2019]   [Oct]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.4 24/41] USB: ldusb: fix read info leaks
    Date
    From: Johan Hovold <johan@kernel.org>

    commit 7a6f22d7479b7a0b68eadd308a997dd64dda7dae upstream.

    Fix broken read implementation, which could be used to trigger slab info
    leaks.

    The driver failed to check if the custom ring buffer was still empty
    when waking up after having waited for more data. This would happen on
    every interrupt-in completion, even if no data had been added to the
    ring buffer (e.g. on disconnect events).

    Due to missing sanity checks and uninitialised (kmalloced) ring-buffer
    entries, this meant that huge slab info leaks could easily be triggered.

    Note that the empty-buffer check after wakeup is enough to fix the info
    leak on disconnect, but let's clear the buffer on allocation and add a
    sanity check to read() to prevent further leaks.

    Fixes: 2824bd250f0b ("[PATCH] USB: add ldusb driver")
    Cc: stable <stable@vger.kernel.org> # 2.6.13
    Reported-by: syzbot+6fe95b826644f7f12b0b@syzkaller.appspotmail.com
    Signed-off-by: Johan Hovold <johan@kernel.org>
    Link: https://lore.kernel.org/r/20191018151955.25135-2-johan@kernel.org
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    drivers/usb/misc/ldusb.c | 15 +++++++++++----
    1 file changed, 11 insertions(+), 4 deletions(-)

    --- a/drivers/usb/misc/ldusb.c
    +++ b/drivers/usb/misc/ldusb.c
    @@ -468,7 +468,7 @@ static ssize_t ld_usb_read(struct file *

    /* wait for data */
    spin_lock_irq(&dev->rbsl);
    - if (dev->ring_head == dev->ring_tail) {
    + while (dev->ring_head == dev->ring_tail) {
    dev->interrupt_in_done = 0;
    spin_unlock_irq(&dev->rbsl);
    if (file->f_flags & O_NONBLOCK) {
    @@ -478,12 +478,17 @@ static ssize_t ld_usb_read(struct file *
    retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done);
    if (retval < 0)
    goto unlock_exit;
    - } else {
    - spin_unlock_irq(&dev->rbsl);
    +
    + spin_lock_irq(&dev->rbsl);
    }
    + spin_unlock_irq(&dev->rbsl);

    /* actual_buffer contains actual_length + interrupt_in_buffer */
    actual_buffer = (size_t*)(dev->ring_buffer + dev->ring_tail*(sizeof(size_t)+dev->interrupt_in_endpoint_size));
    + if (*actual_buffer > dev->interrupt_in_endpoint_size) {
    + retval = -EIO;
    + goto unlock_exit;
    + }
    bytes_to_read = min(count, *actual_buffer);
    if (bytes_to_read < *actual_buffer)
    dev_warn(&dev->intf->dev, "Read buffer overflow, %zd bytes dropped\n",
    @@ -703,7 +708,9 @@ static int ld_usb_probe(struct usb_inter
    dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n");

    dev->interrupt_in_endpoint_size = usb_endpoint_maxp(dev->interrupt_in_endpoint);
    - dev->ring_buffer = kmalloc(ring_buffer_size*(sizeof(size_t)+dev->interrupt_in_endpoint_size), GFP_KERNEL);
    + dev->ring_buffer = kcalloc(ring_buffer_size,
    + sizeof(size_t) + dev->interrupt_in_endpoint_size,
    + GFP_KERNEL);
    if (!dev->ring_buffer) {
    dev_err(&intf->dev, "Couldn't allocate ring_buffer\n");
    goto error;

    \
     
     \ /
      Last update: 2019-10-27 22:03    [W:2.160 / U:0.016 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site