Re: [PATCH v2] usb: gadget: aspeed_udc: avoid past-the-end iterator in dequeue
Andrew Jeffery <[email protected]>
| Newsgroups | org.ozlabs.lists.linux-aspeed,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-usb |
|---|---|
| Message-ID | <eeadb3378066f18cf36469adfc3a70be3ad7b787.camel@codeconstruct.com.au> |
On Tue, 2026-05-19 at 16:02 +0800, Maoyi Xie wrote: > ast_udc_ep_dequeue() declares the loop cursor `req` outside the > list_for_each_entry(). After the loop it tests `&req->req != _req` > to decide whether the request was found. If the queue holds no > match, `req` is past-the-end. It then aliases > container_of(&ep->queue, struct ast_udc_request, queue) via offset > cancellation. Whether that synthetic address equals `_req` depends > on heap layout. The function can return 0 without dequeueing > anything. > > Walk the list with a separate `iter`. Set `req` only when a > request matches. After the loop, `req` is NULL if nothing > matched. > > Suggested-by: Alan Stern <[email protected]> > Signed-off-by: Maoyi Xie <[email protected]> > --- > v2: Switch the loop body to Alan Stern's shape: test inside > the if, assign `req`, break. Same behaviour as v1. > v1: https://lore.kernel.org/linux-usb/[email protected]/ > > drivers/usb/gadget/udc/aspeed_udc.c | 20 ++++++++++++-------- > 1 file changed, 12 insertions(+), 8 deletions(-) > > --- a/drivers/usb/gadget/udc/aspeed_udc.c 2026-05-19 15:29:28.690931576 +0800 > +++ b/drivers/usb/gadget/udc/aspeed_udc.c 2026-05-19 15:29:59.482953528 +0800 > @@ -692,26 +692,30 @@ > { > struct ast_udc_ep *ep = to_ast_ep(_ep); > struct ast_udc_dev *udc = ep->udc; > - struct ast_udc_request *req; > + struct ast_udc_request *req = NULL, *iter; > unsigned long flags; > int rc = 0; > > spin_lock_irqsave(&udc->lock, flags); > > /* make sure it's actually queued on this endpoint */ > - list_for_each_entry(req, &ep->queue, queue) { > - if (&req->req == _req) { > - list_del_init(&req->queue); > - ast_udc_done(ep, req, -ESHUTDOWN); > - _req->status = -ECONNRESET; > + list_for_each_entry(iter, &ep->queue, queue) { > + if (&iter->req == _req) { > + req = iter; > break; > } > } > > - /* dequeue request not found */ > - if (&req->req != _req) > + if (!req) { > rc = -EINVAL; > + goto out; > + } > + > + list_del_init(&req->queue); > + ast_udc_done(ep, req, -ESHUTDOWN); > + _req->status = -ECONNRESET; > > +out: > spin_unlock_irqrestore(&udc->lock, flags); > > return rc; This is a bit of a bikeshed comment and doesn't solve making the code similar to other cases, however: Golfing the diff a bit, perhaps we can start from the assumption that there isn't a match, and require the search disprove that. Then we don't have to test whether we saw something after-the-fact, and we avoid the goto as proposed above. Untested: diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c index 7fc6696b7694..75f9c831b21a 100644 --- a/drivers/usb/gadget/udc/aspeed_udc.c +++ b/drivers/usb/gadget/udc/aspeed_udc.c @@ -694,7 +694,7 @@ static int ast_udc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) struct ast_udc_dev *udc = ep->udc; struct ast_udc_request *req; unsigned long flags; - int rc = 0; + int rc = -EINVAL; spin_lock_irqsave(&udc->lock, flags); @@ -704,14 +704,11 @@ static int ast_udc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) list_del_init(&req->queue); ast_udc_done(ep, req, -ESHUTDOWN); _req->status = -ECONNRESET; + rc = 0; break; } } - /* dequeue request not found */ - if (&req->req != _req) - rc = -EINVAL; - spin_unlock_irqrestore(&udc->lock, flags); return rc;