All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Shunbo Shi" <shishunbo@cmss.chinamobile.com>
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH 3/3] usb: gadget: f_hid: fix race between userspace report update and GET_REPORT response
Date: Wed, 11 Nov 2026 03:03:20 +0000	[thread overview]
Message-ID: <sashiko-outbox-208153@kernel.org> (raw)
In-Reply-To: <20261111021655.37851-4-shishunbo@cmss.chinamobile.com>

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] usb: gadget: f_hid: use-after-free of get_req_buf after
  hidg_disable()
- [Medium] usb: gadget: f_hid: leak of get_req_buf in hidg_bind() and
  hidg_unbind()
- [Medium] usb: gadget: f_hid: uninitialized memory leak in
  get_report_workqueue_handler()
- [Low] usb: gadget: f_hid: unused get_report field in struct f_hidg
--

commit d402239570ba7e6af5cac6cc1d8f6985f6ff4af7
Author: Shunbo Shi <shishunbo@cmss.chinamobile.com>

usb: gadget: f_hid: fix race between userspace report update and GET_REPORT response

This commit allocates a dedicated get_req_buf buffer at bind time and
copies report_list data into it in get_report_workqueue_handler() before
queueing GET_REPORT responses on ep0.

> diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
> index d7ba30323414c..1810e31635187 100644
> --- a/drivers/usb/gadget/function/f_hid.c
> +++ b/drivers/usb/gadget/function/f_hid.c
> @@ -94,6 +94,7 @@ struct f_hidg {
>  
>  	/* get report */
>  	struct usb_request		*get_req;
> +	void				*get_req_buf;
>  	struct usb_hidg_report		get_report;
>  	bool				get_report_returned;
>  	int				get_report_req_report_id;

[Severity: Low]
Since get_report_workqueue_handler() no longer points req->buf at
hidg->get_report.data, can the unused get_report member be removed from
struct f_hidg?

> @@ -587,7 +588,8 @@ static void get_report_workqueue_handler(struct work_struct *work)
>  	ptr = f_hidg_search_for_report(hidg, hidg->get_report_req_report_id);
>  	if (ptr && !ptr->report_data.userspace_req) {
>  		/* Report exists in list and it is to be used for immediate response */
> -		req->buf = ptr->report_data.data;
> +		memcpy(req->buf, ptr->report_data.data,
> +		       min(req->length, (unsigned int)ptr->report_data.length));
>  		status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
>  		hidg->get_report_returned = true;
>  		spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
> @@ -622,10 +624,10 @@ static void get_report_workqueue_handler(struct work_struct *work)
>  			 * Either get an updated response just serviced by userspace
>  			 * or send the latest response in the list
>  			 */
> -			req->buf = ptr->report_data.data;
> +			memcpy(req->buf, ptr->report_data.data,
> +			       min(req->length, (unsigned int)ptr->report_data.length));
>  		} else {
>  			/* If there are no prevoiusly sent reports send empty report */
> -			req->buf = hidg->get_report.data;
>  			memset(req->buf, 0x0, req->length);
>  		}
>  

[Severity: Medium]
Can this send uninitialized slab memory or stale report bytes to the USB
host when ptr->report_data.length is smaller than req->length?

hidg->get_req_buf is allocated with kmalloc(), and req->length is set to
min(hidg->get_report_req_report_length, hidg->report_length,
MAX_REPORT_LENGTH).  Both paths copy only min(req->length,
ptr->report_data.length) bytes into req->buf without clamping req->length
or zeroing the remainder before usb_ep_queue() transmits req->length
bytes.

> @@ -1007,6 +1009,8 @@ static void hidg_disable(struct usb_function *f)
>  		hidg->get_req = NULL;
>  		hidg->get_report_returned = true;
>  	}
> +	kfree(hidg->get_req_buf);
> +	hidg->get_req_buf = NULL;
>  	spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
>  
>  	spin_lock_irqsave(&hidg->read_spinlock, flags);

[Severity: High]
Does freeing hidg->get_req_buf here cause a use-after-free on subsequent
GET_REPORT requests?

hidg->get_req and hidg->get_req_buf are allocated only in hidg_bind(),
where hidg->get_req->buf is set to hidg->get_req_buf.  When hidg_disable()
runs on a USB reset or disconnect while hidg->get_report_returned is true,
hidg->get_req is kept alive, but hidg->get_req_buf is unconditionally
freed and never reallocated in hidg_set_alt().  The next GET_REPORT
request after re-enumeration then runs get_report_workqueue_handler(),
which writes into the dangling req->buf via memcpy() or memset() and
queues it to ep0.  Also, because get_report_workqueue_handler() sets
hidg->get_report_returned = true as soon as usb_ep_queue() is called,
hidg_disable() can free hidg->get_req_buf while the controller is still
reading from req->buf.

> @@ -1160,6 +1164,19 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
>  	if (!hidg->get_req)
>  		return -ENOMEM;
>  
> +	/*
> +	 * The GET_REPORT response is served from a dedicated buffer, not
> +	 * from the report_list entries: userspace may update an entry
> +	 * (f_hidg_get_report()) while the controller is still reading the
> +	 * previous response, which would corrupt the data on the wire.
> +	 */
> +	hidg->get_req_buf = kmalloc(MAX_REPORT_LENGTH, GFP_KERNEL);
> +	if (!hidg->get_req_buf) {
> +		usb_ep_free_request(c->cdev->gadget->ep0, hidg->get_req);
> +		return -ENOMEM;
> +	}
> +	hidg->get_req->buf = hidg->get_req_buf;
> +
>  	hidg->get_req->zero = 0;
>  	hidg->get_req->complete = hidg_get_report_complete;
>  	hidg->get_req->context = hidg;

[Severity: Medium]
Does this leak hidg->get_req_buf on hidg_bind() failure or when
hidg_unbind() runs without a prior hidg_disable()?

In hidg_bind(), the early error return when usb_gstrings_attach() fails
and the fail label both omit kfree(hidg->get_req_buf), and
usb_ep_free_request() only frees the usb_request itself.  Likewise,
hidg_unbind() does not free hidg->get_req_buf, so unbinding a function
that was never configured and disabled by a host leaks the buffer.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261111021655.37851-1-shishunbo@cmss.chinamobile.com?part=3

  reply	other threads:[~2026-11-11  3:03 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-11  2:16 [PATCH 0/3] usb: gadget: f_hid: three GET_REPORT fixes Shunbo Shi
2026-11-11  2:16 ` [PATCH 1/3] usb: gadget: f_hid: fix sleep-in-atomic-context in f_hidg_get_report() Shunbo Shi
2026-11-11  2:48   ` sashiko-bot
2026-11-11  2:16 ` [PATCH 2/3] usb: gadget: f_hid: fix report_list entries leak Shunbo Shi
2026-11-11  2:52   ` sashiko-bot
2026-11-11  2:16 ` [PATCH 3/3] usb: gadget: f_hid: fix race between userspace report update and GET_REPORT response Shunbo Shi
2026-11-11  3:03   ` sashiko-bot [this message]
2026-11-11  8:13 ` [PATCH v2 0/4] usb: gadget: f_hid: three GET_REPORT fixes Shunbo Shi
2026-11-11  8:13   ` [PATCH v2 1/4] usb: gadget: f_hid: fix sleep-in-atomic-context in f_hidg_get_report() Shunbo Shi
2026-11-11  8:19     ` sashiko-bot
2026-11-11  8:13   ` [PATCH v2 2/4] usb: gadget: f_hid: fix report_list entries leak Shunbo Shi
2026-11-11  8:17     ` sashiko-bot
2026-11-11  8:13   ` [PATCH v2 3/4] usb: gadget: f_hid: fix race between userspace report update and GET_REPORT response Shunbo Shi
2026-11-11  8:32     ` sashiko-bot
2026-11-11  8:13   ` [PATCH v2 4/4] usb: gadget: f_hid: fix get_req leak when usb_gstrings_attach() fails Shunbo Shi
2026-11-11  8:22     ` sashiko-bot
2026-11-12  1:09 ` [PATCH v3 0/4] usb: gadget: f_hid: three GET_REPORT fixes Shunbo Shi
2026-11-12  1:09   ` [PATCH v3 1/4] usb: gadget: f_hid: fix sleep-in-atomic-context in f_hidg_get_report() Shunbo Shi
2026-11-12  1:15     ` sashiko-bot
2026-11-12  1:09   ` [PATCH v3 2/4] usb: gadget: f_hid: fix report_list entries leak Shunbo Shi
2026-11-12  1:14     ` sashiko-bot
2026-11-12  1:09   ` [PATCH v3 3/4] usb: gadget: f_hid: fix race between userspace report update and GET_REPORT response Shunbo Shi
2026-11-12  1:53     ` sashiko-bot
2026-11-12  1:09   ` [PATCH v3 4/4] usb: gadget: f_hid: fix get_req leak when usb_gstrings_attach() fails Shunbo Shi
2026-11-12  1:17     ` sashiko-bot

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=sashiko-outbox-208153@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=shishunbo@cmss.chinamobile.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.