From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-oi1-f199.google.com (mail-oi1-f199.google.com [209.85.167.199]) (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 B3B913016F7 for ; Tue, 14 Jul 2026 06:07:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.199 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784009279; cv=none; b=WeQENIAGS/utAd+8/5y7rX+RvHSns1TuM0WP8sfZvhp2iVWI5r0GKiJoHtpyR9M280rV8OHq3qn0ndMw43Bygx4nnHUKRZtBU2+Qci2frSX0HMd/cky6fbQzyFpEZL6M7RwXH9ImW6fcK9cKnoDXzADNXnv/A46LaU38P7E9hl8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784009279; c=relaxed/simple; bh=Y9Xqbwfak/D31oPKTeWgrDE8ESV8T4w+j5aVl9eScNo=; h=MIME-Version:Date:In-Reply-To:Message-ID:Subject:From:To: Content-Type; b=uoPCBhF8+Fue4JwQurdICU+WTUUAPVKa1MesxWEXy/fkXiefAs5Xto9QTNtnE0EEZ15EVz65eKG5g5NL+rhIqUGQw9ghGACqDms1UpLBRIVUz3dz4C2owa0/vEyxLZMjnrAqWLtqMx9WsLlUQbytlPNsr2QSEkDBXCoBYytnJJ8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=syzkaller.appspotmail.com; spf=pass smtp.mailfrom=M3KW2WVRGUFZ5GODRSRYTGD7.apphosting.bounces.google.com; arc=none smtp.client-ip=209.85.167.199 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=syzkaller.appspotmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=M3KW2WVRGUFZ5GODRSRYTGD7.apphosting.bounces.google.com Received: by mail-oi1-f199.google.com with SMTP id 5614622812f47-487059fb570so1115645b6e.0 for ; Mon, 13 Jul 2026 23:07:57 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784009276; x=1784614076; h=content-type:to:from:subject:message-id:in-reply-to:date :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=NVni9b1m7Dm1tSbWpomPRpDGQVdl8WRwm+PhLyVmSUM=; b=A7FB5c7kEMXJrK7xWlgimlnex27jRykvvS3WNzdwZj7eAKRNT5i4Qpgy+IdDInM1sH Y3fW+JkXX+aPPBjpuS+B1/mtlGAznWUbZvbs6VfWhG1YLxemHwx+Jh1ygC2hEOwKbbim TF3UOopmU1KqeRvrIL52J3hck9XfeF/2vAu/GnAYRWUfirIMuDsjTpEFy74Axjs2SAGI ur7aKWXe59J5xwpbN95cf1hwh+ycxVHnUPuhbrGgx2JfTNJSOnuzpI1K1g/ubyPVXJEF G8IQHPiY+eUzdBamYhvDbzudXq0dcE2n91YZqFwlPP9KClopEXpdtCVxSZyWRi/cqmXf G4fw== X-Gm-Message-State: AOJu0YwxNSbnKo03V299G0l1Mikpmp5/cdjbbhwJZAiOlzISwOg/Ch1D iAs9McKJOxJeMiad5F/2hsLtDiwPJbkNv5Ef3DtslZRLrKeGrCJP0Zyyj0GmuLm8XTi6FC7/hOL 4HsE/rhlAImc6yjwYH0m1oxMN+uqwL0b9bTHh+fLMln9zT1nyCYQFOMFyK6k= Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Received: by 2002:a05:6820:81cb:b0:6a3:759d:c418 with SMTP id 006d021491bc7-6a39a863ac2mr7023458eaf.71.1784009276514; Mon, 13 Jul 2026 23:07:56 -0700 (PDT) Date: Mon, 13 Jul 2026 23:07:56 -0700 In-Reply-To: <6742b399.050a0220.1cc393.0033.GAE@google.com> X-Google-Appengine-App-Id: s~syzkaller X-Google-Appengine-App-Id-Alias: syzkaller Message-ID: <6a55d23c.c90005c7.37d349.0014.GAE@google.com> Subject: Forwarded: [PATCH] usb: gadget: dummy_hcd: prevent fifo_req reuse during giveback From: syzbot To: linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com Content-Type: text/plain; charset="UTF-8" For archival purposes, forwarding an incoming command email to linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com. *** Subject: [PATCH] usb: gadget: dummy_hcd: prevent fifo_req reuse during giveback Author: wangjinchao600@gmail.com #syz test 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 = *_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 = *_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=faf3a6cf579fc65591ca 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/dummy_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; /* * HOST side support @@ -330,6 +331,28 @@ static inline struct dummy *gadget_dev_to_dummy(struct device *dev) /* DEVICE/GADGET SIDE UTILITY ROUTINES */ /* 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 look + * empty, but the request is in flight until the completion callback returns. + * 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 = req == &dum->fifo_req; + + if (fifo) + dum->fifo_req_busy = 1; + spin_unlock(&dum->lock); + usb_gadget_giveback_request(_ep, &req->req); + spin_lock(&dum->lock); + if (fifo) + dum->fifo_req_busy = 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 = -ESHUTDOWN; - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); } } @@ -729,6 +750,7 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_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 <= FIFO_SIZE) { req = &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 != -EINPROGRESS) { list_del_init(&req->queue); - spin_unlock(&dum->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dum->lock); + dummy_giveback(dum, &ep->ep, req); /* requests might have been unlinked... */ rescan = 1; @@ -1910,9 +1928,7 @@ static enum hrtimer_restart dummy_timer(struct hrtimer *t) dev_dbg(udc_dev(dum), "stale req = %p\n", req); - 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 = 0; goto restart; } -- 2.53.0