From: Vincent Mailhol <mailhol@kernel.org>
To: Maoyi Xie <maoyixie.tju@gmail.com>,
Marc Kleine-Budde <mkl@pengutronix.de>
Cc: "Stéphane Grosjean" <stephane.grosjean@hms-networks.com>,
linux-can@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: peak_usb_start(): double free of RX buffer when usb_submit_urb() fails
Date: Tue, 16 Jun 2026 12:58:27 +0200 [thread overview]
Message-ID: <d4b8d6ed-e3ab-45a4-a278-a2f240425f31@kernel.org> (raw)
In-Reply-To: <178159320216.2154888.16953451793788581739@maoyixie.com>
On 16/06/2026 at 09:00, Maoyi Xie wrote:
> Hi all,
>
> I was going over the USB CAN drivers that use URB_FREE_BUFFER after the lan78xx
> double-free fix, and I think peak_usb_start() in
> drivers/net/can/usb/peak_usb/pcan_usb_core.c can free an RX transfer buffer
> twice on the usb_submit_urb() error path. I am not totally sure, so I would
> appreciate it if you could let me know whether you see this as a real problem.
>
> This is the relevant part of the RX URB loop in 7.1-rc7.
>
> buf = kmalloc(dev->adapter->rx_buffer_size, GFP_KERNEL);
> ...
> usb_fill_bulk_urb(urb, dev->udev,
> usb_rcvbulkpipe(dev->udev, dev->ep_msg_in),
> buf, dev->adapter->rx_buffer_size,
> peak_usb_read_bulk_callback, dev);
>
> /* ask last usb_free_urb() to also kfree() transfer_buffer */
> urb->transfer_flags |= URB_FREE_BUFFER;
> usb_anchor_urb(urb, &dev->rx_submitted);
>
> err = usb_submit_urb(urb, GFP_KERNEL);
> if (err) {
> if (err == -ENODEV)
> netif_device_detach(dev->netdev);
>
> usb_unanchor_urb(urb);
> kfree(buf);
> usb_free_urb(urb);
> break;
> }
>
> URB_FREE_BUFFER is set on the URB before submit, so buf belongs to the URB. When
> the URB is finally destroyed, urb_destroy() frees that buffer for us with
>
> if (urb->transfer_flags & URB_FREE_BUFFER)
> kfree(urb->transfer_buffer);
>
> So in the error path kfree(buf) frees the buffer once, and then usb_free_urb(urb)
> frees the same buffer a second time through urb_destroy(). That looks like a
> double free of the kmalloc'd RX buffer.
>
> The error path is taken when usb_submit_urb() returns non-zero, e.g. -ENODEV if
> the device is removed while the CAN interface is being brought up (ip link set
> canX up), or -ENOMEM / endpoint errors. A device that disconnects at the wrong
> moment, or otherwise makes the submit fail, would hit it.
>
> I do not have a PCAN-USB adapter, so I reproduced just the buffer handling on
> 7.1-rc7 under KASAN. A small harness replays the exact sequence above
> (URB_FREE_BUFFER set, then kfree(buf), then usb_free_urb(urb)), and KASAN reports
> the second free.
>
> BUG: KASAN: double-free in usb_free_urb.part.0+0x91/0xb0
> Free of addr ffff8881069ccb80 by task trigger.sh/285
> kfree+0x113/0x3c0
> usb_free_urb.part.0+0x91/0xb0 <- second free, via urb_destroy()
> ...
> Freed by task 285:
> kfree+0x137/0x3c0 <- first free, the explicit kfree(buf)
> ...
> The buggy address belongs to the object at ffff8881069ccb80
> which belongs to the cache kmalloc-64 of size 64
>
> (The harness allocates a 64-byte buffer, which is why KASAN shows kmalloc-64,
> while the real driver allocates dev->adapter->rx_buffer_size.) The first free is
> the explicit kfree(buf), and the second is usb_free_urb() going through
> urb_destroy() and freeing urb->transfer_buffer because URB_FREE_BUFFER is set.
>
> The smallest fix I could see is to drop the redundant kfree(buf), since
> usb_free_urb() already releases the buffer. This is what commit 03819abbeb11
> ("net: usb: lan78xx: Fix double free issue with interrupt buffer allocation")
> did for the same pattern.
>
> usb_unanchor_urb(urb);
> - kfree(buf);
> usb_free_urb(urb);
> break;
>
> With that one-line change the same harness frees the buffer exactly once and the
> KASAN report is gone.
>
> It looks like this has been there since the driver was first merged
> (bb4785551f64), so if it is a real bug it would be a stable candidate.
>
> I would appreciate it if you could let me know whether you agree this is a real
> double free and whether dropping the kfree(buf) is the right fix. I am happy to
> send a proper patch with the reproducer once you confirm.
Thanks for the well written report. I looked at the code, and I can
confirm that usb_free_urb() calls usb_destroy() if no one is using the
urb anymore. And finally usb_destroy() calls
kfree(urb->transfer_buffer);
leading in the double free.
So, same conclusion as you. You can send a patch. Now that you sent a
report, don't forget to add the Closes tag to your patch:
Closes: https://lore.kernel.org/linux-can/178159320216.2154888.16953451793788581739@maoyixie.com/T/#u
Not sure that you need to include the full reproducer in your patch
message. The BUG: KASAN log is enough. But if you want, you can still
post your reproducer in this thread.
Yours sincerely,
Vincent Mailhol
prev parent reply other threads:[~2026-06-16 10:58 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-16 7:00 peak_usb_start(): double free of RX buffer when usb_submit_urb() fails Maoyi Xie
2026-06-16 10:58 ` Vincent Mailhol [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d4b8d6ed-e3ab-45a4-a278-a2f240425f31@kernel.org \
--to=mailhol@kernel.org \
--cc=linux-can@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maoyixie.tju@gmail.com \
--cc=mkl@pengutronix.de \
--cc=stephane.grosjean@hms-networks.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox