[functionfs] mainline UAF (was Re: [PATCH v3 36/50] functionfs: switch to simple_remove_by_name())
Al Viro
viro at zeniv.linux.org.uk
Sun Nov 16 17:30:51 AEDT 2025
On Sat, Nov 15, 2025 at 08:21:34AM -0500, Greg Kroah-Hartman wrote:
> Ugh, messy. But yes, this does look better, thanks for that. Want me
> to take it through the USB tree, or will you take it through one of
> yours? (I don't remember what started this thread...)
I'll carve it up in several chunks and push to #work.functionfs; will post
tomorrow morning. Minimal fix for ffs_epfiles_destroy() bug folded into #36
in #work.persistency - replacement for that commit below; are you OK with
that one? It's orthogonal to the rest of the mess in there.
commit b9c24b7499916a1dbee50a4429fc04ebf7e21f03
Author: Al Viro <viro at zeniv.linux.org.uk>
Date: Wed Sep 17 22:55:33 2025 -0400
functionfs: switch to simple_remove_by_name()
No need to return dentry from ffs_sb_create_file() or keep it around
afterwards.
To avoid subtle issues with getting to ffs from epfiles in
ffs_epfiles_destroy(), pass the superblock as explicit argument.
Callers have it anyway.
Signed-off-by: Al Viro <viro at zeniv.linux.org.uk>
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 47cfbe41fdff..6e6933a9fe45 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -160,8 +160,6 @@ struct ffs_epfile {
struct ffs_data *ffs;
struct ffs_ep *ep; /* P: ffs->eps_lock */
- struct dentry *dentry;
-
/*
* Buffer for holding data from partial reads which may happen since
* we’re rounding user read requests to a multiple of a max packet size.
@@ -271,11 +269,11 @@ struct ffs_desc_helper {
};
static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
-static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
+static void ffs_epfiles_destroy(struct super_block *sb,
+ struct ffs_epfile *epfiles, unsigned count);
-static struct dentry *
-ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
- const struct file_operations *fops);
+static int ffs_sb_create_file(struct super_block *sb, const char *name,
+ void *data, const struct file_operations *fops);
/* Devices management *******************************************************/
@@ -1866,9 +1864,8 @@ ffs_sb_make_inode(struct super_block *sb, void *data,
}
/* Create "regular" file */
-static struct dentry *ffs_sb_create_file(struct super_block *sb,
- const char *name, void *data,
- const struct file_operations *fops)
+static int ffs_sb_create_file(struct super_block *sb, const char *name,
+ void *data, const struct file_operations *fops)
{
struct ffs_data *ffs = sb->s_fs_info;
struct dentry *dentry;
@@ -1876,16 +1873,16 @@ static struct dentry *ffs_sb_create_file(struct super_block *sb,
dentry = d_alloc_name(sb->s_root, name);
if (!dentry)
- return NULL;
+ return -ENOMEM;
inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
if (!inode) {
dput(dentry);
- return NULL;
+ return -ENOMEM;
}
d_add(dentry, inode);
- return dentry;
+ return 0;
}
/* Super block */
@@ -1928,10 +1925,7 @@ static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc)
return -ENOMEM;
/* EP0 file */
- if (!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations))
- return -ENOMEM;
-
- return 0;
+ return ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations);
}
enum {
@@ -2161,7 +2155,7 @@ static void ffs_data_closed(struct ffs_data *ffs)
flags);
if (epfiles)
- ffs_epfiles_destroy(epfiles,
+ ffs_epfiles_destroy(ffs->sb, epfiles,
ffs->eps_count);
if (ffs->setup_state == FFS_SETUP_PENDING)
@@ -2226,7 +2220,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
* copy of epfile will save us from use-after-free.
*/
if (epfiles) {
- ffs_epfiles_destroy(epfiles, ffs->eps_count);
+ ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count);
ffs->epfiles = NULL;
}
@@ -2323,6 +2317,7 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
{
struct ffs_epfile *epfile, *epfiles;
unsigned i, count;
+ int err;
count = ffs->eps_count;
epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
@@ -2339,12 +2334,11 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
else
sprintf(epfile->name, "ep%u", i);
- epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name,
- epfile,
- &ffs_epfile_operations);
- if (!epfile->dentry) {
- ffs_epfiles_destroy(epfiles, i - 1);
- return -ENOMEM;
+ err = ffs_sb_create_file(ffs->sb, epfile->name,
+ epfile, &ffs_epfile_operations);
+ if (err) {
+ ffs_epfiles_destroy(ffs->sb, epfiles, i - 1);
+ return err;
}
}
@@ -2352,16 +2346,15 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
return 0;
}
-static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
+static void ffs_epfiles_destroy(struct super_block *sb,
+ struct ffs_epfile *epfiles, unsigned count)
{
struct ffs_epfile *epfile = epfiles;
+ struct dentry *root = sb->s_root;
for (; count; --count, ++epfile) {
BUG_ON(mutex_is_locked(&epfile->mutex));
- if (epfile->dentry) {
- simple_recursive_removal(epfile->dentry, NULL);
- epfile->dentry = NULL;
- }
+ simple_remove_by_name(root, epfile->name, NULL);
}
kfree(epfiles);
More information about the Linuxppc-dev
mailing list