FAILED: patch "[PATCH] usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile" failed to apply to 6.18-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.18-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.18.y
git checkout FETCH_HEAD
git cherry-pick -x 8bdcf96eb135aebacac319667f87db034fb38406
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026071425-shield-overhung-4ba9@gregkh' --subject-prefix 'PATCH 6.18.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 8bdcf96eb135aebacac319667f87db034fb38406 Mon Sep 17 00:00:00 2001
From: Neill Kapron <[email protected]>
Date: Fri, 19 Jun 2026 04:06:04 +0000
Subject: [PATCH] usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Currently, ffs_epfile_release unconditionally frees the endpoint's read_buffer when a file descriptor is closed. If userspace explicitly opens the endpoint multiple times and closes one, the read_buffer is destroyed. This can lead to silent data loss if other file descriptors are still actively reading from the endpoint.
By tying the lifetime of the read_buffer to the ffs_epfile structure itself (which is destroyed when the functionfs instance is torn down in ffs_epfiles_destroy), we eliminate the brittle dependency on open/release calls while correctly matching the conceptual lifetime of unread data on the hardware endpoint.
drivers/usb/gadget/function/f_fs.c +1 -1
@@ -1375,7 +1375,6 @@ ffs_epfile_release(struct inode *inode, struct file *file)mutex_unlock (&epfile->dmabufs_mutex); -__ffs_epfile_read_buffer_free (epfile);ffs_data_closed (epfile->ffs);return 0 ;
@@ -2393,6 +2392,7 @@ static void ffs_epfiles_destroy(struct super_block *sb,for (; count; --count, ++epfile) {BUG_ON (mutex_is_locked (&epfile->mutex)); +__ffs_epfile_read_buffer_free (epfile);simple_remove_by_name (root, epfile->name, clear_one); }
Sasha Levin
maintainerPATCH 1/3 new helper: simple_remove_by_name()
From: Al Viro <[email protected]>
[ Upstream commit 4051a9115ad24bb9a691774730ca9c1dd56de665 ]
simple_recursive_removal(), but instead of victim dentry it takes parent + name.
Used to be open-coded in fs/fuse/control.c, but there's no need to expose the guts of that thing there and there are other potential users, so let's lift it into libfs...
Stable-dep-of: 8bdcf96eb135 ("usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile")
--- fs/fuse/control.c | 7 +------ fs/libfs.c | 13 +++++++++++++ include/linux/fs.h | 2 ++ 3 files changed, 16 insertions(+), 6 deletions(-)
fs/fuse/control.c +1 -6
@@ -290,18 +290,13 @@ static void remove_one(struct dentry *dentry)*/ void fuse_ctl_remove_conn (struct fuse_conn *fc) { -struct dentry *dentry;char name[32 ];if (!fuse_control_sb || fc->no_control)return ;sprintf (name,"%u" , fc->dev); - dentry =lookup_noperm_positive_unlocked (&QSTR (name), fuse_control_sb->s_root); -if (!IS_ERR (dentry)) { -simple_recursive_removal (dentry, remove_one); -dput (dentry);// paired with lookup_noperm_positive_unlocked() - } +simple_remove_by_name (fuse_control_sb->s_root, name, remove_one); }static int fuse_ctl_fill_super (struct super_block *sb,struct fs_context *fsc)
fs/libfs.c +13 -0
@@ -647,6 +647,19 @@ void simple_recursive_removal(struct dentry *dentry, }EXPORT_SYMBOL (simple_recursive_removal); +void simple_remove_by_name (struct dentry *parent,const char *name, +void (*callback)(struct dentry *)) +{ +struct dentry *dentry; + + dentry =lookup_noperm_positive_unlocked (&QSTR (name), parent); +if (!IS_ERR (dentry)) { +simple_recursive_removal (dentry, callback); +dput (dentry);// paired with lookup_noperm_positive_unlocked() + } +} +EXPORT_SYMBOL (simple_remove_by_name); +/* caller holds parent directory with I_MUTEX_PARENT */ void locked_recursive_removal (struct dentry *dentry,void (*callback)(struct dentry *))
include/linux/fs.h +2 -0
@@ -3654,6 +3654,8 @@ extern int simple_rename(struct mnt_idmap *, struct inode *, unsigned int);extern void simple_recursive_removal (struct dentry *,void (*callback)(struct dentry *)); +extern void simple_remove_by_name (struct dentry *,const char *, +void (*callback)(struct dentry *));extern void locked_recursive_removal (struct dentry *,void (*callback)(struct dentry *));extern int noop_fsync (struct file *,loff_t ,loff_t ,int );
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 functionfs: switch to simple_remove_by_name()
From: Al Viro <[email protected]>
[ Upstream commit c7747fafaba0dcdad3d7da240e961927b4865f98 ]
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.
Stable-dep-of: 8bdcf96eb135 ("usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile")
--- drivers/usb/gadget/function/f_fs.c | 51 +++++++++++++----------------- 1 file changed, 22 insertions(+), 29 deletions(-)
drivers/usb/gadget/function/f_fs.c +22 -29
@@ -162,8 +162,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.
@@ -273,11 +271,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 *******************************************************/
@@ -1881,9 +1879,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;
@@ -1891,16 +1888,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 */
@@ -1943,10 +1940,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 {
@@ -2176,7 +2170,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 )
@@ -2241,7 +2235,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 ; }
@@ -2338,6 +2332,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 );
@@ -2355,12 +2350,11 @@ static int ffs_epfiles_create(struct ffs_data *ffs)else sprintf (epfile->name,"ep%u" , i); epfile->in = (ffs->eps_addrmap[i] &USB_ENDPOINT_DIR_MASK ) ?1 :0 ; - 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; } }
@@ -2368,16 +2362,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);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile
In reply to Sasha LevinFrom: Neill Kapron <[email protected]>
[ Upstream commit 8bdcf96eb135aebacac319667f87db034fb38406 ]
Currently, ffs_epfile_release unconditionally frees the endpoint's read_buffer when a file descriptor is closed. If userspace explicitly opens the endpoint multiple times and closes one, the read_buffer is destroyed. This can lead to silent data loss if other file descriptors are still actively reading from the endpoint.
By tying the lifetime of the read_buffer to the ffs_epfile structure itself (which is destroyed when the functionfs instance is torn down in ffs_epfiles_destroy), we eliminate the brittle dependency on open/release calls while correctly matching the conceptual lifetime of unread data on the hardware endpoint.
--- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/gadget/function/f_fs.c +1 -1
@@ -1352,7 +1352,6 @@ ffs_epfile_release(struct inode *inode, struct file *file)mutex_unlock (&epfile->dmabufs_mutex); -__ffs_epfile_read_buffer_free (epfile);ffs_data_closed (epfile->ffs);return 0 ;
@@ -2370,6 +2369,7 @@ static void ffs_epfiles_destroy(struct super_block *sb,for (; count; --count, ++epfile) {BUG_ON (mutex_is_locked (&epfile->mutex)); +__ffs_epfile_read_buffer_free (epfile);simple_remove_by_name (root, epfile->name,NULL ); }
Signature · 1 line
2.53.0