[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <Y+GcoFKiAkrCoAsv@rowland.harvard.edu>
Date: Mon, 6 Feb 2023 19:34:40 -0500
From: Alan Stern <stern@...land.harvard.edu>
To: Xin Zhao <xnzhao@...gle.com>
Cc: gregkh@...uxfoundation.org, jakobkoschel@...il.com,
rdunlap@...radead.org, ira.weiny@...el.com,
linux-usb@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH] usb: gadget: dummyhcd: Fix use-after-free in
dummy_free_request
On Mon, Feb 06, 2023 at 10:52:58PM +0000, Xin Zhao wrote:
> DummyHCD assume when dummy_free_request is called, the request
> is already detached from request queues. It is correct in most
> cases.
> But when DummyHCD is detached from gadget configfs with pending
> requests and some requests are still in pending queue,
> dummy_free_request would free them directly.
> Later on, dummy_udc_stop would iterate pending queue to release
> the requests again.
>
> Stacktrace for dummy_free_reqeust
> ```
> kfree(const void * x) (slub.c:4200)
> dummy_free_request(struct usb_ep * _ep, struct usb_request * _req) (dummy_hcd.c:691)
> usb_ep_free_request(struct usb_ep * ep, struct usb_request * req) (core.c:201)
> functionfs_unbind(struct ffs_data * ffs) (f_fs.c:1894)
That's the bug right there. The kerneldoc for usb_ep_free_request()
says "Caller guarantees the request is not queued". So it looks like
the real solution is to fix functionfs_unbind().
> ffs_func_unbind(struct usb_function * f) (f_fs.c:3614)
> purge_configs_funcs(struct gadget_info * gi) (configfs.c:1303)
> configfs_composite_unbind(struct usb_gadget * gadget) (configfs.c:1528)
> usb_gadget_remove_driver(struct usb_udc * udc) (core.c:1436)
> usb_gadget_unregister_driver(struct usb_gadget_driver * driver) (core.c:1585)
> unregister_gadget(struct gadget_info * gi) (configfs.c:281)
> gadget_dev_desc_UDC_store(struct config_item * item) (configfs.c:308)
> flush_write_buffer(struct file * file, struct configfs_buffer * buffer, size_t count) (file.c:251)
> ```
>
> Stacktrace of use-after-free
> ```
> list_del_init(struct list_head * entry) (list.h:204)
> nuke(struct dummy * dum, struct dummy_ep * ep) (dummy_hcd.c:344)
> stop_activity(struct dummy * dum) (dummy_hcd.c:366)
> dummy_udc_stop(struct usb_gadget * g) (dummy_hcd.c:1032)
> usb_gadget_udc_stop(struct usb_udc * udc) (core.c:1141)
> usb_gadget_remove_driver(struct usb_udc * udc) (core.c:1437)
> usb_gadget_unregister_driver(struct usb_gadget_driver * driver) (core.c:1585)
> unregister_gadget(struct gadget_info * gi) (configfs.c:281)
> gadget_dev_desc_UDC_store(struct config_item * item) (configfs.c:308)
> flush_write_buffer(struct file * file, struct configfs_buffer * buffer, size_t count) (file.c:251)
> configfs_write_file(struct file * file)
> ```
>
> Signed-off-by: Xin Zhao <xnzhao@...gle.com>
> ---
> drivers/usb/gadget/udc/dummy_hcd.c | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
> index 899ac9f9c279..afead69d7487 100644
> --- a/drivers/usb/gadget/udc/dummy_hcd.c
> +++ b/drivers/usb/gadget/udc/dummy_hcd.c
> @@ -679,7 +679,11 @@ static void dummy_free_request(struct usb_ep *_ep, struct usb_request *_req)
> }
>
> req = usb_request_to_dummy_request(_req);
> - WARN_ON(!list_empty(&req->queue));
> + if (!list_empty(&req->queue)) {
> + WARN_ON(1);
> + return;
> + }
Once the bug in functionfs_unbind() is fixed, this change won't be
necessary.
Alan Stern
Powered by blists - more mailing lists