From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-oi1-f197.google.com (mail-oi1-f197.google.com [209.85.167.197]) (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 3A5FF2DB780 for ; Fri, 31 Jul 2026 04:26:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.197 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785472008; cv=none; b=Fml/qni0VMGTFQTPhA8ynZpvrAbWlgMJgtUoFiPLX5XNZa8uADbBR6+0wI4THXLMExW9hRnGkGxkeY9t2kQfNgvSMPTd9yMrEn0mSvzbjY/UlMtYobpe5O+tIEIkdSc9in9hm9cLHTmi2Do+/PyBZ5wFty5f038Z4/YU+W7L/V8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785472008; c=relaxed/simple; bh=l73ErXwfsxsU6w40peEO2pr3LbkrwzRRozKV3HceBfg=; h=MIME-Version:Date:In-Reply-To:Message-ID:Subject:From:To: Content-Type; b=O2R59g4ZjCqhFszrimMhYevFs5PUKmyOhWCZwCF68BJmQAcTwNtxyGP9sar9VoRn/UdAlu0jfSuK7hsJED2/QDZIEz87puGx1n+hfO7JvUBOMG9yZ8YhUHNRIOSbsKqmN52tWerVXg7xrIT05++qSm+kBTUJTdAeM1bLFXsGOzU= 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.197 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-f197.google.com with SMTP id 5614622812f47-4a45b3f0bdfso715182b6e.1 for ; Thu, 30 Jul 2026 21:26:46 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785472006; x=1786076806; 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=0Uu/XzH7E/fVXgrU9+uX6kyF85Dm+UvnY8Ne1Mh9R+Y=; b=VzEBgHI1g0XTTYSsvtRInmxjhgVLlpMckWiMffGO+IFARmayfjkaJ1rZEqyjJmUHN4 tX9pac8WQi5AltlIk/tWbYs7KOqO29FRBxCLbD/SGllamvSyIwQbypNaCT6znqioR6c9 zvEWkx+IhrNA0Zb3Fb8BE8TzfWb3lN31X83uGOS+9Kik2hzpT7IJrAnkRLOEHZzSdgsl d6t+G1nfwZ5OhAFSkzF+MJTaSnZtgF00aXL37sJ+TFd3uv6R8fAyg+MlVnKikCziDJgv fIzlNY5joIrvI5XAyS9gb4hZngHAqu4gEc9lNl4i1erEk5TCFoUUxDGG44BLJeLn64K6 DgvA== X-Gm-Message-State: AOJu0YzZRoWSRUpZnEBfh0MP0oYg52JH6IIxbQn19w59on+ZxZg0OV3T j+mknTmAjKZIchJkdvqtHek1MCCTprfcIuzXk5MpHXjanEFgCNyp8O+6JrmK1wVkeZWh7TVEYNq YAlwtMciMTC7m8rfogmMmT00+rhGYKd9vin4xXgml3ODz4HKxLacZ6QzjwFY= 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:6808:690c:b0:495:f519:70f1 with SMTP id 5614622812f47-4ae5abc34afmr346894b6e.31.1785472005997; Thu, 30 Jul 2026 21:26:45 -0700 (PDT) Date: Thu, 30 Jul 2026 21:26:45 -0700 In-Reply-To: <6a6bfa43.1b55b669.19788.0016.GAE@google.com> X-Google-Appengine-App-Id: s~syzkaller X-Google-Appengine-App-Id-Alias: syzkaller Message-ID: <6a6c2405.77639fcc.3d4fd0.001a.GAE@google.com> Subject: Forwarded: [PATCH] usb: gadgetfs: fix use-after-free in ep_open() 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: gadgetfs: fix use-after-free in ep_open() Author: kartikey406@gmail.com #syz test: git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-kernelci ep_open() dereferenced inode->i_private (an ep_data pointer) and locked its embedded mutex before ever validating that the object was still alive: struct ep_data *data = inode->i_private; if (mutex_lock_interruptible(&data->lock) != 0) return -EINTR; spin_lock_irq(&data->dev->lock); if (data->dev->state == STATE_DEV_UNBOUND) ... The liveness check (dev->state == STATE_DEV_UNBOUND) only happened *after* data->lock had already been touched. destroy_ep_files(), called from gadgetfs_unbind(), can free the same ep_data concurrently via put_ep(), since nothing prevented an in-flight open() from racing the teardown. KASAN reports a slab-use-after-free read inside __mutex_lock_common() from ep_open(), with the object freed by a concurrent gadgetfs_unbind() -> destroy_ep_files() -> put_ep() -> kfree() on another task. This exact race was identified by Al Viro on LKML in 2006 ("races in drivers/usb/gadget/inode.c"), including the correct fix direction, but it was never applied to ep_open() itself. Later fixes to this file (the udc_usage counter, CVE-2022-4382) hardened the I/O paths and the mount/unmount race, but left this specific ordering bug in place. Fix it by reordering ep_open() so that dev->state and inode->i_private are only read while holding dev->lock -- reached via inode->i_sb->s_fs_info, which stays valid for the life of the mount, independent of any individual ep_data -- and by pinning the object with get_ep() before dev->lock is dropped and before data->lock is touched. This guarantees put_ep()/kfree() cannot race the mutex lock in ep_open(). This fix depends on dev->state being set to STATE_DEV_UNBOUND under dev->lock before any call to destroy_ep_files(), so that ep_open() seeing a "bound" state is a guarantee that no free is in progress. gadgetfs_unbind() already does this correctly. activate_ep_files()'s enomem0 error-cleanup path did not: it called destroy_ep_files() directly without first setting dev->state, which would have left the same race open on the bind-failure path. This patch adds the missing state transition there as well, so the invariant holds for every caller of destroy_ep_files(). Reported-by: syzbot+df9e891bf8ea586f846b@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=df9e891bf8ea586f846b Signed-off-by: Deepanshu Kartikey --- drivers/usb/gadget/legacy/inode.c | 37 ++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index d87a8ab51510..95e3bcedd33d 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -817,25 +817,39 @@ ep_config (struct ep_data *data, const char *buf, size_t len) static int ep_open (struct inode *inode, struct file *fd) { - struct ep_data *data = inode->i_private; - int value = -EBUSY; + struct dev_data *dev = inode->i_sb->s_fs_info; + struct ep_data *data; + int value = -ENODEV; + + spin_lock_irq (&dev->lock); + if (dev->state == STATE_DEV_UNBOUND) { + spin_unlock_irq (&dev->lock); + return -ENOENT; + } + data = inode->i_private; + get_ep (data); + spin_unlock_irq (&dev->lock); - if (mutex_lock_interruptible(&data->lock) != 0) + if (mutex_lock_interruptible(&data->lock) != 0) { + put_ep (data); return -EINTR; - spin_lock_irq (&data->dev->lock); - if (data->dev->state == STATE_DEV_UNBOUND) + } + + value = -EBUSY; + spin_lock_irq (&dev->lock); + if (dev->state == STATE_DEV_UNBOUND) value = -ENOENT; else if (data->state == STATE_EP_DISABLED) { value = 0; data->state = STATE_EP_READY; - get_ep (data); fd->private_data = data; - VDEBUG (data->dev, "%s ready\n", data->name); + VDEBUG (dev, "%s ready\n", data->name); } else - DBG (data->dev, "%s state %d\n", - data->name, data->state); - spin_unlock_irq (&data->dev->lock); + DBG (dev, "%s state %d\n", data->name, data->state); + spin_unlock_irq (&dev->lock); mutex_unlock(&data->lock); + if (value) + put_ep (data); return value; } @@ -1632,6 +1646,9 @@ static int activate_ep_files (struct dev_data *dev) kfree (data); enomem0: DBG (dev, "%s enomem\n", __func__); + spin_lock_irq (&dev->lock); + dev->state = STATE_DEV_UNBOUND; + spin_unlock_irq (&dev->lock); destroy_ep_files (dev); return -ENOMEM; } -- 2.43.0