Re: [PATCH v2] usb: gadget: raw_gadget: fix double free in raw_release
From: Andrey Konovalov
Date: Thu Mar 26 2026 - 06:39:25 EST
On Thu, Mar 26, 2026 at 10:04 AM cuiyudong <cuiyudong@xxxxxxxxxx> wrote:
>
> In raw_release(), when unregister == true, there are two kref_put() calls:
> 1. Inside the unregister block (extra)
> 2. At out_put label
>
> The refcount increments are:
> - 1 ref from dev_new() in raw_open()
> - 1 ref from kref_get() in raw_ioctl_run()
>
> Total: 2 references.
>
> The original code performed 3 kref_put() operations, which causes the refcount
> to drop below zero and leads to a double free in dev_free().
So where is the 3rd one coming from? Just above you say that there are only 2.
>
> Remove the extra kref_put() inside the unregister block to balance the
> reference counter correctly.
>
> Since the extra kref_put() triggers an immediate use-after-free condition
> on the dev structure, KASAN reports a double-free instead of a refcount warning.
This doesn't explain anything. If refcount drops below 0 (as you say
above), shouldn't there be a refcount warning?
>
> BUG: KASAN: double-free in dev_free+0x424/0x740
> Fixes: f2c2e717642c ("usb: gadget: add raw-gadget interface")
> Reported-by: syzbot+25612fe5ab3dcafc3aab@xxxxxxxxxxxxxxxxxxxxxxxxx
> Closes: https://lore.kernel.org/all/69c401ad.a70a0220.23629d.0000.GAE@xxxxxxxxxx/
> Signed-off-by: cuiyudong <cuiyudong@xxxxxxxxxx>
> ---
> Changes in v2:
> - Removed Tested-by tag (syzbot did not test the patch)
> - Expanded commit description to explain refcount imbalance and KASAN report
> ---
> drivers/usb/gadget/legacy/raw_gadget.c | 4 +---
> 1 file changed, 1 insertion(+), 3 deletions(-)
>
> diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
> index 4febf8dac7ca..a1fd3fdf1323 100644
> --- a/drivers/usb/gadget/legacy/raw_gadget.c
> +++ b/drivers/usb/gadget/legacy/raw_gadget.c
> @@ -465,12 +465,10 @@ static int raw_release(struct inode *inode, struct file *fd)
> dev_err(dev->dev,
> "usb_gadget_unregister_driver() failed with %d\n",
> ret);
> - /* Matches kref_get() in raw_ioctl_run(). */
> - kref_put(&dev->count, dev_free);
> }
>
> out_put:
> - /* Matches dev_new() in raw_open(). */
> + /* Matches dev_new() in raw_open() and kref_get() in raw_ioctl_run(). */
> kref_put(&dev->count, dev_free);
> return ret;
> }
> --
> 2.25.1
>