From nobody Sat Jul 25 20:04:27 2026 Received: from mail-pj1-f43.google.com (mail-pj1-f43.google.com [209.85.216.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5E37235E922 for ; Thu, 16 Jul 2026 10:42:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784198554; cv=none; b=sqzLrXJjkVveKEoL3JlnaBHtOZytAH44dfhDxNPX3lsjSZE1hrp3uGC2CWPmnLF3ApoCI97rXZuPz4c3VE9xnGBPecpfrugqqHK87lx4o4C6QoaVdBv1NJE0ZUFBYW156y7HmI8O5kkDnQH6r+4aibYfzNJvE9SA3lGQvdZ/mPo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784198554; c=relaxed/simple; bh=Gk998eYN7gUwcRYe8PxnBJe6Xr7AbEqyGz3sBixO3B4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BbohL1m+Tl9ovdUhVnwZlnc1AhR72zQtNpS8CKdvy1932+YISDIIjWwGdvMSRqiZkvGGroE2jfW7g9LE41ku5hPsP+tpR3ZVYjrmrypNFfaVECrDhwAmXUvF57lOADfug5ppyOBhS7FW6lK3dl3AbAeELEIDViRGzbE4UgUtmQU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=XnsQklVu; arc=none smtp.client-ip=209.85.216.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XnsQklVu" Received: by mail-pj1-f43.google.com with SMTP id 98e67ed59e1d1-38759bcd877so5626555a91.2 for ; Thu, 16 Jul 2026 03:42:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784198552; x=1784803352; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=wTeYglMh2Ky0UImAmNlYDWZ+C9HdKGNWJyEuofDOr2k=; b=XnsQklVuQ++rpvMOCbBs31KZAAIj7Umua3AVh/yHgrLbD15zp7oFE9aV9sved6ntRR QJgNKMc5SrPO7RTNXMDzZKvGpj4d3MQV/CNLvRaXkrSHoG94t2NUu8reMGm0OQ6Tx1uA tuVolCxmI01eIksFv/iHY9lSguwXmk1LK4HtC7fKCabRJV69rizQ5eWZprACrHyMQs1R Q2P0lJQoR8F9UOAWgDUn6gnze8B/rFqktINhhDjexh0oOH6BVjaIs01eYNwnHYlaZPf3 QBXNhUKNPPTPjg27LBV9UdcsPYXj7I225uACp0HSMFMoa0t3UF5cd6okznHhMdFAaYkf AH9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784198552; x=1784803352; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=wTeYglMh2Ky0UImAmNlYDWZ+C9HdKGNWJyEuofDOr2k=; b=sEu5I4FBH2M07Wp0C5AAzbLv+hx6ZgnF+UnYTBAas+09I+6HVWpa7fEZBk7ap8g079 p58KjYGkDnnOTzV5K0EpfsEUYcR16OTGusozEYjbUBXN1ZtYHjxbp7L4p1u0DxU2SOC/ YI2ho001OCwihZVrEDtJE8Ekf2jjuY9eZFjpRBmXdxMoE8F8QvmEicS/p/VUgP2Jyx1X L3UQtPy/obRxEcOU3RaP6a+1nic41SYRZKYbXC6TsEV9qxw5x/LqpK2jWbUI2nOczci5 NdQm3mgQvLJXXKpUFPGTLfL2elC/04vtT/Mk9yFsnTNvqfp8/Mq6+ij0PiInJK+pluKW 2VeA== X-Forwarded-Encrypted: i=1; AHgh+Rp3IIeLiCLX55+Cr+ZioXbDfsL8WYu+d8EPK/23yFI/kRTCm8ylGunymA0zNpsDdk++uofaJGvjN4ljJmQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yy7axUc8aVo4ZF0uDLjYtvGTyLgWroNtaZ9KnKTtO1lV4yiAEUV on54FWF4q1/CFOtPJahMBv+CNB6wsmUe/gR+ws6DSWD57NS2a/CnkNla X-Gm-Gg: AfdE7ckKtgNEc0h/NnNG+BmeW6/DdXqVgueJPW7pDJzhkzD/CYfHV4Mk6DUHmAGaaMh KU+sXI9vkTXhjEY3ssNZL+SaFAVIcVzdZ7Ls/cOgvP5tMJf2to2pAxjpp4j/UZPorUub/hmcSa8 73lIYLCgm8iAOc4dyQwv0TNiPzcoAMrM3SuUrV3sx7EtUPcWS65EtWAoy2TGdSNMNzBRfieHFn/ 0O6UlUqalGx4Pm1zkYK+/HSeN7Ov/5k3TQ2a1ZQifjk9KaXiyD4/aF4imEiKdfkzfifpq82WRhI LQpBbEw1AVtv+WKVN5uafjx/TH9iXt3qUKrwfhHgNXeh7Vbn/2h1NWY4drFsvh96gOScf9Ck7fQ wFt8HnE9H5Muquz8yAgTNXwCMyExNqP5yRIEzZdp9dnb4ka9lzrTMHC7DcXOGB71aO9+Ku4TUaz EJfg== X-Received: by 2002:a17:90b:2650:b0:37f:ef32:d444 with SMTP id 98e67ed59e1d1-38e29ff9805mr5615043a91.1.1784198551431; Thu, 16 Jul 2026 03:42:31 -0700 (PDT) Received: from localhost ([144.24.58.22]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3140e6a4779sm13552089eec.17.2026.07.16.03.42.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Jul 2026 03:42:31 -0700 (PDT) From: Jinchao Wang To: gregkh@linuxfoundation.org, stern@rowland.harvard.edu Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, eeodqql09@gmail.com, surban@surban.net, bigeasy@linutronix.de, kees@kernel.org, Jinchao Wang , syzbot+faf3a6cf579fc65591ca@syzkaller.appspotmail.com, stable@vger.kernel.org Subject: [PATCH v2] usb: gadget: dummy_hcd: prevent fifo_req reuse during giveback Date: Thu, 16 Jul 2026 06:42:17 -0400 Message-ID: <5db8bba5b3499a86cd2e776f9918126b68b2508b.1784198306.git.wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714064829.172098-1-wangjinchao600@gmail.com> References: <20260714064829.172098-1-wangjinchao600@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" dummy_hcd embeds a single shared usb_request (dum->fifo_req) that the "emulated single-request FIFO" fast-path in dummy_queue() reuses for small IN transfers: it copies the caller's request into it (req->req =3D *_req) and queues it, treating list_empty(&fifo_req.queue) as "the slot is free". The completion side (dummy_timer/transfer/nuke/dummy_dequeue) follows the standard pattern: list_del_init(&req->queue) unlinks the request, then the lock is dropped and usb_gadget_giveback_request() invokes req->complete(). But list_del_init() makes fifo_req.queue look empty *before* the completion callback returns, so a concurrent dummy_queue() on another CPU sees the slot as free, reuses fifo_req and runs req->req =3D *_req -- overwriting req->complete while dummy_timer is mid-calling it. The indirect call then jumps to a clobbered pointer, causing a general protection fault / page fault in dummy_timer (syzkaller extid faf3a6cf579fc65591ca). The clobbering write is an in-bounds memcpy on a live shared object, so KASAN cannot flag it. Add a fifo_req_busy bit covering the shared request's whole lifetime: set it in dummy_queue() when the FIFO fast-path takes fifo_req (making it the fast-path guard, replacing the list_empty(&fifo_req.queue) test), and clear it after the completion callback has returned, via a dummy_giveback() helper used at all four gadget-request giveback sites. The shared slot can no longer be reused until its completion callback has finished. Reported-by: syzbot+faf3a6cf579fc65591ca@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3Dfaf3a6cf579fc65591ca Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Jinchao Wang Reviewed-by: Alan Stern --- Changes in v2 (per Alan Stern's review of v1 [1]): - Move dummy_giveback() above the "/* called with spinlock held */" comment so the comment is again immediately before nuke(). - Set fifo_req_busy in dummy_queue() when the FIFO fast-path takes the shared request (covering its whole in-use lifetime) instead of in dummy_giveback(), and drop the now-redundant list_empty(&fifo_req.queue) test from the fast-path guard. [1] https://lore.kernel.org/all/20260714064829.172098-1-wangjinchao600@gmai= l.com/ drivers/usb/gadget/udc/dummy_hcd.c | 40 ++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/du= mmy_hcd.c index f47903461ed5..c0e40fa6dde5 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -278,6 +278,7 @@ struct dummy { unsigned ints_enabled:1; unsigned udc_suspended:1; unsigned pullup:1; + unsigned fifo_req_busy:1; =20 /* * HOST side support @@ -329,6 +330,26 @@ static inline struct dummy *gadget_dev_to_dummy(struct= device *dev) =20 /* DEVICE/GADGET SIDE UTILITY ROUTINES */ =20 +/* + * Give back a gadget request with dum->lock dropped around the callback. + * If @req is the shared fifo_req, clear fifo_req_busy afterward: the flag + * was set in dummy_queue() when the shared request was taken and must stay + * set until its completion callback has returned; list_del_init() alone + * makes the request look idle while the callback is still running. + * Caller holds dum->lock and has already done list_del_init() + status. + */ +static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep, + struct dummy_request *req) +{ + bool fifo =3D req =3D=3D &dum->fifo_req; + + spin_unlock(&dum->lock); + usb_gadget_giveback_request(_ep, &req->req); + spin_lock(&dum->lock); + if (fifo) + dum->fifo_req_busy =3D 0; +} + /* called with spinlock held */ static void nuke(struct dummy *dum, struct dummy_ep *ep) { @@ -339,9 +360,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep) list_del_init(&req->queue); req->req.status =3D -ESHUTDOWN; =20 - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); } } =20 @@ -728,10 +747,11 @@ static int dummy_queue(struct usb_ep *_ep, struct usb= _request *_req, =20 /* implement an emulated single-request FIFO */ if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && - list_empty(&dum->fifo_req.queue) && + !dum->fifo_req_busy && list_empty(&ep->queue) && _req->length <=3D FIFO_SIZE) { req =3D &dum->fifo_req; + dum->fifo_req_busy =3D 1; req->req =3D *_req; req->req.buf =3D dum->fifo_buf; memcpy(dum->fifo_buf, _req->buf, _req->length); @@ -785,9 +805,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb= _request *_req) dev_dbg(udc_dev(dum), "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); - spin_unlock(&dum->lock); - usb_gadget_giveback_request(_ep, _req); - spin_lock(&dum->lock); + dummy_giveback(dum, _ep, req); } spin_unlock_irqrestore(&dum->lock, flags); return retval; @@ -1523,9 +1541,7 @@ static int transfer(struct dummy_hcd *dum_hcd, struct= urb *urb, if (req->req.status !=3D -EINPROGRESS) { list_del_init(&req->queue); =20 - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); =20 /* requests might have been unlinked... */ rescan =3D 1; @@ -1910,9 +1926,7 @@ static enum hrtimer_restart dummy_timer(struct hrtime= r *t) dev_dbg(udc_dev(dum), "stale req =3D %p\n", req); =20 - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); ep->already_seen =3D 0; goto restart; } --=20 2.53.0