From nobody Sat Jul 25 20:04:12 2026 Received: from mail-qk1-f178.google.com (mail-qk1-f178.google.com [209.85.222.178]) (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 8118338332A for ; Tue, 14 Jul 2026 06:48:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784011720; cv=none; b=W4PfaZ5r+DL5O/AtvZ08a5nobsG/8rDNTT8a0DYM+eamAkJb4SkObHr/v2kdXw2DYQIQ1YoNlnWP2aIVNfXHqT/rAwJiin48EOayruzfATQGM/7E0ThnPiugVJ+JIxpuH9QisU3jw2L03DxVjWfgfwfk1BfVus/qdHnnNrn0/Mo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784011720; c=relaxed/simple; bh=aZlcH4OxvFHO+ZSx63whlzSsDvYQENRMsi4F/aUDmgo=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=FHdRuV5IgBCS+3t9ohFzF1Qj8Xx51p89tz6BwpgTtdmJdyZ6sCHc8zcYLHoWfCrgaALNb75Qi8UI80XkQoSBglBZg4c6BnvKixmAKjPMMsdq9gc6gnSgspE/65pm/36l5QD3NIJg0KLIei3x3G9msawjs8K7FGQqkpcTJnQ9R2E= 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=syuhHWFb; arc=none smtp.client-ip=209.85.222.178 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="syuhHWFb" Received: by mail-qk1-f178.google.com with SMTP id af79cd13be357-92eafc94c9cso282497085a.0 for ; Mon, 13 Jul 2026 23:48:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784011716; x=1784616516; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=/MSWmAHhibOUb8+dtux4VwDB6gj9PYU7Wzltf0lKwv4=; b=syuhHWFbrKbzsA+ifX/qFe1iFlQSlOuLFfT1i0tAVviMm9fn5mUhrwGj5NY5kPNGbt UdlVI1hCf+ZCh601rW146IW2fcJ5rsqwZI5Yc5j02RK1hoOqIu2ukimuVvxN30p/jIqR HjtuoTIodEqVaD3wOXcMVh4zVE6XctT78o/EQiXsXWnXUsNFWQ3mbxo/TJwZlYPd2d9l mlEy/2jENE5YWowI0rRKP+vBQBUfORqYGVeeRzyVJmCClvs0YuZT55ee4DqkZSQlvIkm 6pHAGdr0Ci/SIvVMbbAx+iJCQ2TYpGI1xUqWjZPRAETD/l9qtkcPZNAeUnw5nY6xJtOU IzJg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784011716; x=1784616516; h=content-transfer-encoding:mime-version: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=/MSWmAHhibOUb8+dtux4VwDB6gj9PYU7Wzltf0lKwv4=; b=JY+Llj5WsIfD3MG0Bn11ayKBfAm0Df5fX2rILQhjT2YvqjAsyH8UQ3L8Ih0cMZG6e1 8ALqkqfmv4K24gCgokcjP3Bm3/ABI6dYkZjvFGhor2a7F7kA2QSLRaYXJG7I3Qy+Km7b 6C+8pKc+Lty1AcjGJy0vlLxaCU49JptyP4HhpwjF0e/eGX03dU8bT1gQ1iyUUOK7t1iA /Y69c8hx53RAA07UvL8MChBymaRssos4Z12d5SJmu/zHb0Js/Sy+Pc3zNF2ItBZFtqvd f2VmCogtV7UHF7T7gGMtqedIBSnhlJYPn76TfEaBZIH26Dv+U5sxCk8ygVk1E9F4+WgZ roJQ== X-Forwarded-Encrypted: i=1; AHgh+Rqur2SOoCAIB6y5tvPorhYCuTzeln/BrYv0y+xjc/Qut6ptbhP4oUKA2e4XIxlwvIb/SLJIRUAkm8hN4rI=@vger.kernel.org X-Gm-Message-State: AOJu0YxOSQ1GojZXzq5P7UfrRyoaMKsjV7vV/iHFGnHk9jOb5BYHMZgj qW2qVQknbWJj+jn5qb3NCFm+dbDCGvVhJOU2RMfHKK/+BoS57f5kl9I6 X-Gm-Gg: AfdE7cmDn6uePt+oqkIFObkdY+C5jnngDBehxgg2afSKGW0kqu4WGyyhxrxWEiHqbpZ H5x3i77fT00yTCVIAGjfWGnl3rZxHW2+Tjpeg5LuWqFZMwvQtjYv1f1XAxtneCFbsJSWyKBK9sK VG0h0Rwqn51JSjoZwcYbwr7clQMHvl3Z5u1yWJXUQhtMgSomYePy3Pegh/6O/0e50OtwoWQ8iLf 4cmIoSbvr7/in9TkTqf6Oces2RDUm7gsZw6mJpMjVyPPEQwiepWVwNz1WH7AxulJwlAbnL8bm0I fxvVTv6eNEnTQhHH7Fc9YDRqivmPADN0DEV11yAp1C2uoHaTpfAxgNY+LrFdjtMKfVvQszgNomp r+CbE4HleH7ST9LPEiSpcR/V3ppTK1HslMMfAfym+nQnT9D2vDEBQ2MeFjCIpzQGsvEPMZ2qdRc Il5EwkInfJeslXw2pqXFS9SJS6TZmmB9aSKuRMaIR+ut6zNbg27HU= X-Received: by 2002:a05:620a:2b82:b0:92e:6c15:24cd with SMTP id af79cd13be357-9308683e763mr104687285a.15.1784011716346; Mon, 13 Jul 2026 23:48:36 -0700 (PDT) Received: from localhost ([48.45.163.146]) by smtp.gmail.com with ESMTPSA id af79cd13be357-92ee5d33689sm1343639185a.36.2026.07.13.23.48.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Jul 2026 23:48:35 -0700 (PDT) From: Jinchao Wang To: gregkh@linuxfoundation.org, linux-usb@vger.kernel.org Cc: stern@rowland.harvard.edu, bigeasy@linutronix.de, eeodqql09@gmail.com, kees@kernel.org, surban@surban.net, linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com, stable@vger.kernel.org, wangjinchao600@gmail.com Subject: [PATCH] usb: gadget: dummy_hcd: prevent fifo_req reuse during giveback Date: Tue, 14 Jul 2026 14:48:29 +0800 Message-ID: <20260714064829.172098-1-wangjinchao600@gmail.com> X-Mailer: git-send-email 2.53.0 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, set across the lockless giveback window via a dummy_giveback() helper used at all four gadget-request giveback sites, and require !fifo_req_busy in the FIFO fast-path guard so the shared slot cannot be reused until its completion callback has returned. 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 --- drivers/usb/gadget/udc/dummy_hcd.c | 40 +++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/du= mmy_hcd.c index f47903461ed5..fce3c3ba7a63 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 @@ -330,6 +331,28 @@ static inline struct dummy *gadget_dev_to_dummy(struct= device *dev) /* DEVICE/GADGET SIDE UTILITY ROUTINES */ =20 /* called with spinlock held */ +/* + * Give back a gadget request with dum->lock dropped around the callback. + * If @req is the shared fifo_req, mark it busy across the callback so + * dummy_queue()'s FIFO fast-path (keyed on list_empty(&fifo_req.queue)) + * cannot reuse it mid-giveback: list_del_init() already made the queue lo= ok + * empty, but the request is in flight until the completion callback retur= ns. + * 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; + + if (fifo) + dum->fifo_req_busy =3D 1; + spin_unlock(&dum->lock); + usb_gadget_giveback_request(_ep, &req->req); + spin_lock(&dum->lock); + if (fifo) + dum->fifo_req_busy =3D 0; +} + static void nuke(struct dummy *dum, struct dummy_ep *ep) { while (!list_empty(&ep->queue)) { @@ -339,9 +362,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 @@ -729,6 +750,7 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_r= equest *_req, /* 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; @@ -785,9 +807,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 +1543,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 +1928,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