From: "Pandey, Radhey Shyam" <radheys@amd.com>
To: Haofeng Li <lihaofeng@kylinos.cn>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Michal Simek <michal.simek@amd.com>,
linux-usb@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Haofeng Li <13266079573@163.com>
Subject: Re: [PATCH] usb: gadget: udc-xilinx: cap non-EP0 OUT transfers to the request buffer
Date: Sun, 6 Sep 2026 17:56:55 +0530 [thread overview]
Message-ID: <ed7c3959-7222-4d3b-9195-7f3ab6a003f2@amd.com> (raw)
In-Reply-To: <20260826133600.3797953-1-lihaofeng@kylinos.cn>
On 8/26/2026 7:06 PM, Haofeng Li wrote:
> xudc_read_fifo() takes the transfer size from the endpoint count
> register (device/host-controlled for OUT packets) and, if the request
only host controlled ?
> still has some room (bufferspace != 0), passes it straight to
> xudc_eptxrx():
>
> count = udc->read_fn(udc->addr + ep->offset + bufoffset);
> bufferspace = req->usb_req.length - req->usb_req.actual;
> if (!bufferspace) { ... } /* only the ==0 case handled */
> ret = xudc_eptxrx(ep, req, buf, count); /* PIO: memcpy_toio(buf, epram, count) */
> req->usb_req.actual += min(count, bufferspace);
>
> There is no cap of count against bufferspace. The PIO path copies the
> full count bytes into req->usb_req.buf + actual (memcpy_toio), and the
> DMA path programs a DMA of length count - both will overflow the
> request buffer whenever count > bufferspace, with only the bookkeeping
> (actual += min(count, bufferspace)) clamped afterwards.
>
> Attack chain (USB peripheral mode, non-EP0 OUT endpoint; attacker is
> the USB host):
>
> malicious host -> OUT packet on epX -> UDC interrupt
> -> xudc_read_fifo() -> count = endpoint count register (e.g. 64)
> -> request with req.length = 8, req.actual = 7 (1 byte left)
> -> bufferspace = 1, count > bufferspace, bufferspace != 0
> -> xudc_eptxrx() PIO OUT: memcpy_toio(req.buf + 7, epram, 64)
> -> 63 bytes past the 8-byte request buffer
> (dwc2-style FunctionFS / f_tcm gadgets submit short requests, so
> the interface does not guarantee a buffer as large as the
> endpoint max packet size.)
>
> Reproduced (kernel 7.2.0+, KASAN/SLUB debug): calling the real
> xudc_read_fifo() with the above state leaves req->usb_req.actual = 8
> (count 64, bufferspace 1, actual += min = 1) while the PIO copy has
> already written 64 bytes: byte 7 of the 8-byte buffer changes from the
> 0xBB marker to FIFO data and the SLUB redzone plus neighbouring slab
> objects past the buffer are overwritten (65 of 72 dumped bytes differ
> from the pre-use pattern) - the out-of-bounds write is observed
> byte-for-byte. (memcpy_toio goes through an architecture copy routine
> that generic KASAN does not instrument, so the surrounding-memory clobber
> is the forensic evidence.)
>
> Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
> Assisted-by: opencode:deepseek-v4-flash-free
> ---
> drivers/usb/gadget/udc/udc-xilinx.c | 18 ++++++++++++++++++
> 1 file changed, 18 insertions(+)
>
> diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
> index bef06fe7543b..63749fd97625 100644
> --- a/drivers/usb/gadget/udc/udc-xilinx.c
> +++ b/drivers/usb/gadget/udc/udc-xilinx.c
> @@ -627,6 +627,24 @@ static int xudc_read_fifo(struct xusb_ep *ep, struct xusb_req *req)
> return 0;
> }
>
> + if (count > bufferspace) {
Can we merge this check with above . It should avoid duplication?
- if (unlikely(!bufferspace)) {
+ if (unlikely(!bufferspace || count > bufferspace)) {
In v2 , also add fixes and stable kernel tag.
> + /*
> + * The host sent a packet larger than the request buffer.
> + * The PIO path would memcpy_toio() the whole packet into
> + * req->usb_req.buf (and the DMA path would program a DMA
> + * transfer of count bytes), overflowing the buffer; only the
> + * bookkeeping actual += min(count, bufferspace) afterwards
> + * would be clamped. Complete the request with -EOVERFLOW
> + * instead of copying past it.
> + */
> + if (req->usb_req.status != -EOVERFLOW)
> + dev_dbg(udc->dev, "%s overflow %d into %u\n",
> + ep->ep_usb.name, count, bufferspace);
> + req->usb_req.status = -EOVERFLOW;
> + xudc_done(ep, req, -EOVERFLOW);
> + return 0;
> + }
> +
> ret = xudc_eptxrx(ep, req, buf, count);
> switch (ret) {
> case 0:
prev parent reply other threads:[~2026-09-06 12:27 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-26 13:36 Haofeng Li
2026-09-06 12:26 ` Pandey, Radhey Shyam [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=ed7c3959-7222-4d3b-9195-7f3ab6a003f2@amd.com \
--to=radheys@amd.com \
--cc=13266079573@163.com \
--cc=gregkh@linuxfoundation.org \
--cc=lihaofeng@kylinos.cn \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=michal.simek@amd.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
all inboxes | Powered by JetHome®