[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <CALEuBamV3oXcTm1bE=m6O6XcXR=rrFaMgjaLyZqSOG2ghDDqqw@mail.gmail.com>
Date: Thu, 15 Jan 2026 04:32:21 +0800
From: 齐柯宇 <qikeyu2017@...il.com>
To: pawell@...ence.com, gregkh@...uxfoundation.org
Cc: linux-usb@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: [PATCH] usb: cdns2: fix use-after-free in cdns2_gadget_giveback
This fix addresses a use-after-free vulnerability discovered through
static code analysis of the cdns2_gadget_giveback() function.
The vulnerability exists because after usb_gadget_giveback_request()
is called, the code continues to access request->buf. However,
usb_gadget_giveback_request() invokes the request's complete callback,
and certain gadget function drivers (such as FunctionFS with DMABUF)
may directly free the request within this callback.
Call flow leading to use-after-free:
cdns2_gadget_giveback()
-> usb_gadget_giveback_request()
-> request->complete() [e.g., ffs_epfile_dmabuf_io_complete]
-> usb_ep_free_request() // request is freed here
-> if (request->buf == pdev->zlp_buf) // UAF: accessing freed memory
-> cdns2_gadget_ep_free_request() // potential double-free
Data flow analysis shows that this vulnerability can be triggered when:
1. A user application uses FunctionFS with DMABUF transfer capability
2. The user attaches a DMABUF via FUNCTIONFS_DMABUF_ATTACH ioctl
3. The user initiates a transfer via FUNCTIONFS_DMABUF_TRANSFER ioctl
4. Upon transfer completion, ffs_epfile_dmabuf_io_complete() is called
as the complete callback, which frees the request
5. cdns2_gadget_giveback() then accesses the freed request->buf field
Evidence that complete callback can free the request (f_fs.c):
static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep,
struct usb_request *req)
{
ffs_dmabuf_signal_done(req->context, req->status);
usb_ep_free_request(ep, req); // frees the request directly
}
The fix saves the ZLP check result before calling the complete callback
and uses mutually exclusive logic: requests with complete callbacks are
owned by the gadget function driver, while only ZLP requests without
complete callbacks are freed by the UDC driver.
Fixes: 3eb1f1efe204 ("usb: cdns2: Add main part of Cadence USBHS driver")
Signed-off-by: Kery Qi <qikeyu2017@...il.com>
---
drivers/usb/gadget/udc/cdns2/cdns2-gadget.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
index 9b53daf76583..8997623cca5a 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
@@ -240,6 +240,7 @@ void cdns2_gadget_giveback(struct cdns2_endpoint *pep,
{
struct usb_request *request = &preq->request;
struct cdns2_device *pdev = pep->pdev;
+ bool is_zlp = (request->buf == pdev->zlp_buf);
list_del_init(&preq->list);
@@ -257,10 +258,14 @@ void cdns2_gadget_giveback(struct cdns2_endpoint *pep,
spin_unlock(&pdev->lock);
usb_gadget_giveback_request(&pep->endpoint, request);
spin_lock(&pdev->lock);
- }
-
- if (request->buf == pdev->zlp_buf)
+ } else if (is_zlp) {
+ /*
+ * Only ZLP requests without a complete callback are freed
+ * by the driver. Requests with complete callbacks are
+ * owned by the gadget function driver.
+ */
cdns2_gadget_ep_free_request(&pep->endpoint, request);
+ }
}
static void cdns2_wa1_restore_cycle_bit(struct cdns2_endpoint *pep)
--
2.34.1
Powered by blists - more mailing lists