From: Mohammad Mosafer <mohsafer@gmail.com>
To: linux-usb@vger.kernel.org
Cc: gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org,
syzbot+6227549bd2c8a1ec8ba0@syzkaller.appspotmail.com
Subject: [PATCH] usb: gadget: f_fs: fix use-after-free in ffs_closed() on umount
Date: Tue, 6 Oct 2026 16:05:23 -0500 [thread overview]
Message-ID: <20261006210523.150724-1-mohsafer@gmail.com> (raw)
ffs_free_inst() releases the ffs_dev with ffs_release_dev() and only
then re-acquires ffs_dev_lock to free it with _ffs_free_dev(). In
between, the dev is still linked on the ffs_devices list while already
marked unmounted, so a concurrent mount(2) of functionfs finds it by
name in ffs_acquire_dev() and links a fresh ffs_data to the doomed dev
(ffs_data->private_data = dev). _ffs_free_dev() then kfrees the dev,
and when that mount is torn down, ffs_closed() dereferences the stale
ffs->private_data:
BUG: KASAN: slab-use-after-free in ffs_data_clear+0x438/0x530
Write of size 1 at addr ffff88810594664a by task repro/116
ffs_data_clear+0x438/0x530
ffs_fs_kill_sb+0x7b/0x510
deactivate_locked_super+0xa9/0x200
cleanup_mnt+0x255/0x380
... reached via umount(2)
Freed by task 112:
kfree+0x127/0x3b0
ffs_free_inst+0x10c/0x1a0
usb_put_function_instance+0x8a/0xc0
configfs_rmdir+0x773/0x9c0
Allocated by task 113:
ffs_alloc_inst+0x109/0x360
function_make+0x138/0x330
configfs_mkdir+0x48b/0x1090
Hold ffs_dev_lock across the release and the free so that a released
dev is never findable, splitting ffs_release_dev() into a lock-assuming
_ffs_release_dev() (matching the _ffs_* convention in this file) plus a
locking wrapper for the remaining callers.
The race was reproduced with a multi-threaded harness racing configfs
mkdir/rmdir of the ffs instance against mount/umount of functionfs on
a KASAN kernel: the unpatched kernel reports the use-after-free
reliably (2/2 runs), the patched kernel survives an extended soak with
identical churn (2/2 runs clean).
Reported-by: syzbot+6227549bd2c8a1ec8ba0@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=6227549bd2c8a1ec8ba0
Fixes: 5920cda627688c ("usb: gadget: FunctionFS: convert to new function interface with backward compatibility")
Signed-off-by: Mohammad Mosafer <mohsafer@gmail.com>
---
drivers/usb/gadget/function/f_fs.c | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index c64a268e98a4..5e7179b4250e 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -288,6 +288,7 @@ static struct ffs_dev *_ffs_find_dev(const char *name);
static struct ffs_dev *_ffs_alloc_dev(void);
static void _ffs_free_dev(struct ffs_dev *dev);
static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data);
+static void _ffs_release_dev(struct ffs_dev *ffs_dev);
static void ffs_release_dev(struct ffs_dev *ffs_dev);
static int ffs_ready(struct ffs_data *ffs);
static void ffs_closed(struct ffs_data *ffs);
@@ -4147,8 +4148,17 @@ static void ffs_free_inst(struct usb_function_instance *f)
struct f_fs_opts *opts;
opts = to_f_fs_opts(f);
- ffs_release_dev(opts->dev);
+
+ /*
+ * Release and free the dev under a single ffs_dev_lock critical
+ * section. Between ffs_release_dev() and _ffs_free_dev() the dev
+ * would still be on the ffs_devices list while already unmounted,
+ * so a concurrent ffs_acquire_dev() could link a fresh ffs_data to
+ * the doomed dev, leaving it with a dangling ->private_data that is
+ * dereferenced in ffs_closed() when that mount is torn down.
+ */
ffs_dev_lock();
+ _ffs_release_dev(opts->dev);
_ffs_free_dev(opts->dev);
ffs_dev_unlock();
kfree(opts);
@@ -4363,10 +4373,11 @@ static int ffs_acquire_dev(const char *dev_name, struct ffs_data *ffs_data)
return ret;
}
-static void ffs_release_dev(struct ffs_dev *ffs_dev)
+/*
+ * ffs_dev_lock must be taken by the caller
+ */
+static void _ffs_release_dev(struct ffs_dev *ffs_dev)
{
- ffs_dev_lock();
-
if (ffs_dev && ffs_dev->mounted) {
ffs_dev->mounted = false;
if (ffs_dev->ffs_data) {
@@ -4377,7 +4388,12 @@ static void ffs_release_dev(struct ffs_dev *ffs_dev)
if (ffs_dev->ffs_release_dev_callback)
ffs_dev->ffs_release_dev_callback(ffs_dev);
}
+}
+static void ffs_release_dev(struct ffs_dev *ffs_dev)
+{
+ ffs_dev_lock();
+ _ffs_release_dev(ffs_dev);
ffs_dev_unlock();
}
--
2.34.1
next reply other threads:[~2026-10-06 21:05 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-06 21:05 Mohammad Mosafer [this message]
2026-10-06 21:49 ` [PATCH v2] " Mohammad Mosafer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261006210523.150724-1-mohsafer@gmail.com \
--to=mohsafer@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=syzbot+6227549bd2c8a1ec8ba0@syzkaller.appspotmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®