* [PATCH] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF
@ 2026-10-18 13:05 Yu Zhang
2026-10-18 13:08 ` sashiko-bot
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Yu Zhang @ 2026-10-18 13:05 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb, linux-kernel, Yu Zhang, stable
fsg_store_nofua() syncs the LUN's backing file when switching from async
to sync mode:
if (!nofua && curlun->nofua)
fsg_lun_fsync_sub(curlun);
fsg_lun_fsync_sub() dereferences curlun->filp (vfs_fsync(filp)). Unlike
every other store that touches the backing file -- fsg_store_ro() and
fsg_store_cdrom() (down_read), fsg_store_file() (down_write) --
fsg_store_nofua() holds no filesem while doing so.
A concurrent close of the backing file runs under down_write(filesem) and
does fput(curlun->filp) in fsg_lun_close() (fsg_store_file() with an empty
path, the SCSI START_STOP eject in do_start_stop(), or forced_eject), so
it can free the file while fsg_store_nofua() is dereferencing it:
BUG: KASAN: slab-use-after-free in vfs_fsync+0x20/0xe0
vfs_fsync+0x20/0xe0
fsg_lun_fsync_sub+0xa0/0xd0
fsg_store_nofua+0x138/0x160
configfs_write_iter+0x1a7/0x250
vfs_write+0x5df/0x940
Allocated by task 3459:
fsg_store_file+0x150/0x1f0
configfs_write_iter+0x1a7/0x250
Freed by task 3459:
__fput+0x456/0x6c0
__fput_deferred+0x10d/0x330
fsg_store_file+0x9e/0x1f0
configfs_write_iter+0x1a7/0x250
Take filesem for read around the fsync in fsg_store_nofua(), as the sibling
stores do, so the backing file cannot be closed underneath it.
Reproduced on a mass_storage gadget over dummy_hcd by writing lun.N/nofua
(1 then 0) from one task while another task rewrites and clears lun.N/file.
Cc: stable@vger.kernel.org
Signed-off-by: Yu Zhang <yuz08559@gmail.com>
---
drivers/usb/gadget/function/f_mass_storage.c | 6 ++++--
drivers/usb/gadget/function/storage_common.c | 5 ++++-
drivers/usb/gadget/function/storage_common.h | 3 ++-
3 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 0000001..0000002 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2663,8 +2663,9 @@
const char *buf, size_t count)
{
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
- return fsg_store_nofua(curlun, buf, count);
+ return fsg_store_nofua(curlun, filesem, buf, count);
}
static ssize_t file_store(struct device *dev, struct device_attribute *attr,
@@ -3239,7 +3240,10 @@
static ssize_t fsg_lun_opts_nofua_store(struct config_item *item,
const char *page, size_t len)
{
- return fsg_store_nofua(to_fsg_lun_opts(item)->lun, page, len);
+ struct fsg_lun_opts *opts = to_fsg_lun_opts(item);
+ struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent);
+
+ return fsg_store_nofua(opts->lun, &fsg_opts->common->filesem, page, len);
}
CONFIGFS_ATTR(fsg_lun_opts_, nofua);
diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c
index 0000003..0000004 100644
--- a/drivers/usb/gadget/function/storage_common.c
+++ b/drivers/usb/gadget/function/storage_common.c
@@ -415,7 +415,8 @@
}
EXPORT_SYMBOL_GPL(fsg_store_ro);
-ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
+ssize_t fsg_store_nofua(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+ const char *buf, size_t count)
{
bool nofua;
int ret;
@@ -424,11 +425,13 @@
if (ret)
return ret;
+ down_read(filesem);
/* Sync data when switching from async mode to sync */
if (!nofua && curlun->nofua)
fsg_lun_fsync_sub(curlun);
curlun->nofua = nofua;
+ up_read(filesem);
return count;
}
diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h
index 0000005..0000006 100644
--- a/drivers/usb/gadget/function/storage_common.h
+++ b/drivers/usb/gadget/function/storage_common.h
@@ -210,7 +210,8 @@
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count);
-ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count);
+ssize_t fsg_store_nofua(struct fsg_lun *curlun, struct rw_semaphore *filesem,
+ const char *buf, size_t count);
ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count);
ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF 2026-10-18 13:05 [PATCH] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF Yu Zhang @ 2026-10-18 13:08 ` sashiko-bot 2026-10-18 13:34 ` Greg Kroah-Hartman ` (2 subsequent siblings) 3 siblings, 0 replies; 7+ messages in thread From: sashiko-bot @ 2026-10-18 13:08 UTC (permalink / raw) To: Yu Zhang; +Cc: linux-usb > fsg_store_nofua() syncs the LUN's backing file when switching from async > to sync mode: > > if (!nofua && curlun->nofua) > fsg_lun_fsync_sub(curlun); > > fsg_lun_fsync_sub() dereferences curlun->filp (vfs_fsync(filp)). Unlike > every other store that touches the backing file -- fsg_store_ro() and > fsg_store_cdrom() (down_read), fsg_store_file() (down_write) -- > fsg_store_nofua() holds no filesem while doing so. > > A concurrent close of the backing file runs under down_write(filesem) and > does fput(curlun->filp) in fsg_lun_close() (fsg_store_file() with an empty > path, the SCSI START_STOP eject in do_start_stop(), or forced_eject), so > it can free the file while fsg_store_nofua() is dereferencing it: > [ ... ] > Reproduced on a mass_storage gadget over dummy_hcd by writing lun.N/nofua > (1 then 0) from one task while another task rewrites and clears lun.N/file. > > Cc: stable@vger.kernel.org > Signed-off-by: Yu Zhang <yuz08559@gmail.com> Sashiko has reviewed this patch and found no issues. -- Sashiko AI review · https://sashiko.dev/#/patchset/20261018130519.65850-1-yuz08559@gmail.com?part=1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF 2026-10-18 13:05 [PATCH] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF Yu Zhang 2026-10-18 13:08 ` sashiko-bot @ 2026-10-18 13:34 ` Greg Kroah-Hartman 2026-10-18 15:50 ` Alan Stern 2026-10-20 2:30 ` [PATCH v2] " Yu Zhang 3 siblings, 0 replies; 7+ messages in thread From: Greg Kroah-Hartman @ 2026-10-18 13:34 UTC (permalink / raw) To: Yu Zhang; +Cc: linux-usb, linux-kernel, stable On Sun, Oct 18, 2026 at 11:05:19PM +1000, Yu Zhang wrote: > fsg_store_nofua() syncs the LUN's backing file when switching from async > to sync mode: > > if (!nofua && curlun->nofua) > fsg_lun_fsync_sub(curlun); > > fsg_lun_fsync_sub() dereferences curlun->filp (vfs_fsync(filp)). Unlike > every other store that touches the backing file -- fsg_store_ro() and > fsg_store_cdrom() (down_read), fsg_store_file() (down_write) -- > fsg_store_nofua() holds no filesem while doing so. > > A concurrent close of the backing file runs under down_write(filesem) and > does fput(curlun->filp) in fsg_lun_close() (fsg_store_file() with an empty > path, the SCSI START_STOP eject in do_start_stop(), or forced_eject), so > it can free the file while fsg_store_nofua() is dereferencing it: > > BUG: KASAN: slab-use-after-free in vfs_fsync+0x20/0xe0 > vfs_fsync+0x20/0xe0 > fsg_lun_fsync_sub+0xa0/0xd0 > fsg_store_nofua+0x138/0x160 > configfs_write_iter+0x1a7/0x250 > vfs_write+0x5df/0x940 > Allocated by task 3459: > fsg_store_file+0x150/0x1f0 > configfs_write_iter+0x1a7/0x250 > Freed by task 3459: > __fput+0x456/0x6c0 > __fput_deferred+0x10d/0x330 > fsg_store_file+0x9e/0x1f0 > configfs_write_iter+0x1a7/0x250 > > Take filesem for read around the fsync in fsg_store_nofua(), as the sibling > stores do, so the backing file cannot be closed underneath it. > > Reproduced on a mass_storage gadget over dummy_hcd by writing lun.N/nofua > (1 then 0) from one task while another task rewrites and clears lun.N/file. > > Cc: stable@vger.kernel.org > Signed-off-by: Yu Zhang <yuz08559@gmail.com> > --- > drivers/usb/gadget/function/f_mass_storage.c | 6 ++++-- > drivers/usb/gadget/function/storage_common.c | 5 ++++- > drivers/usb/gadget/function/storage_common.h | 3 ++- > 3 files changed, 12 insertions(+), 4 deletions(-) > > diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c > index 0000001..0000002 100644 > --- a/drivers/usb/gadget/function/f_mass_storage.c > +++ b/drivers/usb/gadget/function/f_mass_storage.c > @@ -2663,8 +2663,9 @@ > const char *buf, size_t count) > { > struct fsg_lun *curlun = fsg_lun_from_dev(dev); > + struct rw_semaphore *filesem = dev_get_drvdata(dev); > > - return fsg_store_nofua(curlun, buf, count); > + return fsg_store_nofua(curlun, filesem, buf, count); > } > > static ssize_t file_store(struct device *dev, struct device_attribute *attr, > @@ -3239,7 +3240,10 @@ > static ssize_t fsg_lun_opts_nofua_store(struct config_item *item, > const char *page, size_t len) > { > - return fsg_store_nofua(to_fsg_lun_opts(item)->lun, page, len); > + struct fsg_lun_opts *opts = to_fsg_lun_opts(item); > + struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); > + > + return fsg_store_nofua(opts->lun, &fsg_opts->common->filesem, page, len); > } > > CONFIGFS_ATTR(fsg_lun_opts_, nofua); > diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c > index 0000003..0000004 100644 > --- a/drivers/usb/gadget/function/storage_common.c > +++ b/drivers/usb/gadget/function/storage_common.c > @@ -415,7 +415,8 @@ > } > EXPORT_SYMBOL_GPL(fsg_store_ro); > > -ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) > +ssize_t fsg_store_nofua(struct fsg_lun *curlun, struct rw_semaphore *filesem, > + const char *buf, size_t count) > { > bool nofua; > int ret; > @@ -424,11 +425,13 @@ > if (ret) > return ret; > > + down_read(filesem); guard()? That's what happens when you use a LLM for kernel work, it only knows old-styles of what to do. thanks, greg k-h ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF 2026-10-18 13:05 [PATCH] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF Yu Zhang 2026-10-18 13:08 ` sashiko-bot 2026-10-18 13:34 ` Greg Kroah-Hartman @ 2026-10-18 15:50 ` Alan Stern 2026-10-20 2:30 ` [PATCH v2] " Yu Zhang 3 siblings, 0 replies; 7+ messages in thread From: Alan Stern @ 2026-10-18 15:50 UTC (permalink / raw) To: Yu Zhang; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable On Sun, Oct 18, 2026 at 11:05:19PM +1000, Yu Zhang wrote: > fsg_store_nofua() syncs the LUN's backing file when switching from async > to sync mode: > > if (!nofua && curlun->nofua) > fsg_lun_fsync_sub(curlun); > > fsg_lun_fsync_sub() dereferences curlun->filp (vfs_fsync(filp)). Unlike > every other store that touches the backing file -- fsg_store_ro() and > fsg_store_cdrom() (down_read), fsg_store_file() (down_write) -- > fsg_store_nofua() holds no filesem while doing so. > diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c > index 0000003..0000004 100644 > --- a/drivers/usb/gadget/function/storage_common.c > +++ b/drivers/usb/gadget/function/storage_common.c > @@ -415,7 +415,8 @@ > } > EXPORT_SYMBOL_GPL(fsg_store_ro); > > -ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) > +ssize_t fsg_store_nofua(struct fsg_lun *curlun, struct rw_semaphore *filesem, > + const char *buf, size_t count) > { > bool nofua; > int ret; > @@ -424,11 +425,13 @@ > if (ret) > return ret; > > + down_read(filesem); > /* Sync data when switching from async mode to sync */ > if (!nofua && curlun->nofua) > fsg_lun_fsync_sub(curlun); > > curlun->nofua = nofua; > + up_read(filesem); Shouldn't filesem be held only within the body of the conditional? There's no reason to take it if fsg_lun_fsync_sub() isn't going to be called. Alan Stern ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF 2026-10-18 13:05 [PATCH] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF Yu Zhang ` (2 preceding siblings ...) 2026-10-18 15:50 ` Alan Stern @ 2026-10-20 2:30 ` Yu Zhang 2026-10-20 8:03 ` sashiko-bot 2026-10-20 14:11 ` Alan Stern 3 siblings, 2 replies; 7+ messages in thread From: Yu Zhang @ 2026-10-20 2:30 UTC (permalink / raw) To: Greg Kroah-Hartman; +Cc: Alan Stern, linux-usb, linux-kernel, Yu Zhang, stable fsg_store_nofua() syncs the LUN's backing file when switching from async to sync mode: if (!nofua && curlun->nofua) fsg_lun_fsync_sub(curlun); fsg_lun_fsync_sub() dereferences curlun->filp (vfs_fsync(filp)). Unlike every other store that touches the backing file -- fsg_store_ro() and fsg_store_cdrom() (down_read), fsg_store_file() (down_write) -- fsg_store_nofua() holds no filesem while doing so. A concurrent close of the backing file runs under down_write(filesem) and does fput(curlun->filp) in fsg_lun_close() (fsg_store_file() with an empty path, the SCSI START_STOP eject in do_start_stop(), or forced_eject), so it can free the file while fsg_store_nofua() is dereferencing it: BUG: KASAN: slab-use-after-free in vfs_fsync+0x20/0xe0 vfs_fsync+0x20/0xe0 fsg_lun_fsync_sub+0xa0/0xd0 fsg_store_nofua+0x138/0x160 configfs_write_iter+0x1a7/0x250 vfs_write+0x5df/0x940 Allocated by task 3459: fsg_store_file+0x150/0x1f0 configfs_write_iter+0x1a7/0x250 Freed by task 3459: __fput+0x456/0x6c0 __fput_deferred+0x10d/0x330 fsg_store_file+0x9e/0x1f0 configfs_write_iter+0x1a7/0x250 Hold filesem for read around the fsync in fsg_store_nofua() so the backing file cannot be closed underneath it. The lock is only needed when the fsync actually runs, so it is taken only in that branch. Reproduced on a mass_storage gadget over dummy_hcd by writing lun.N/nofua (1 then 0) from one task while another task rewrites and clears lun.N/file. Cc: stable@vger.kernel.org Signed-off-by: Yu Zhang <yuz08559@gmail.com> --- v2: - Use guard(rwsem_read) instead of a bare down_read()/up_read() pair (Greg KH). - Take filesem only inside the branch that actually calls fsg_lun_fsync_sub(), not around the whole function / the curlun->nofua assignment, since the lock is only needed for the sync (Alan Stern). v1: https://lore.kernel.org/all/20261018130519.65850-1-yuz08559@gmail.com/ drivers/usb/gadget/function/f_mass_storage.c | 8 ++++++-- drivers/usb/gadget/function/storage_common.c | 7 +++++-- drivers/usb/gadget/function/storage_common.h | 3 ++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index b7b06cb..6712566 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2663,8 +2663,9 @@ static ssize_t nofua_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); - return fsg_store_nofua(curlun, buf, count); + return fsg_store_nofua(curlun, filesem, buf, count); } static ssize_t file_store(struct device *dev, struct device_attribute *attr, @@ -3239,7 +3240,10 @@ static ssize_t fsg_lun_opts_nofua_show(struct config_item *item, char *page) static ssize_t fsg_lun_opts_nofua_store(struct config_item *item, const char *page, size_t len) { - return fsg_store_nofua(to_fsg_lun_opts(item)->lun, page, len); + struct fsg_lun_opts *opts = to_fsg_lun_opts(item); + struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_nofua(opts->lun, &fsg_opts->common->filesem, page, len); } CONFIGFS_ATTR(fsg_lun_opts_, nofua); diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c index 75831f2..51a70f2 100644 --- a/drivers/usb/gadget/function/storage_common.c +++ b/drivers/usb/gadget/function/storage_common.c @@ -415,7 +415,8 @@ ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, } EXPORT_SYMBOL_GPL(fsg_store_ro); -ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) +ssize_t fsg_store_nofua(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) { bool nofua; int ret; @@ -425,8 +426,10 @@ ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) return ret; /* Sync data when switching from async mode to sync */ - if (!nofua && curlun->nofua) + if (!nofua && curlun->nofua) { + guard(rwsem_read)(filesem); fsg_lun_fsync_sub(curlun); + } curlun->nofua = nofua; diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h index 11ac785..347a86b 100644 --- a/drivers/usb/gadget/function/storage_common.h +++ b/drivers/usb/gadget/function/storage_common.h @@ -210,7 +210,8 @@ ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf); ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf); ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, const char *buf, size_t count); -ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count); +ssize_t fsg_store_nofua(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, const char *buf, size_t count); ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF 2026-10-20 2:30 ` [PATCH v2] " Yu Zhang @ 2026-10-20 8:03 ` sashiko-bot 2026-10-20 14:11 ` Alan Stern 1 sibling, 0 replies; 7+ messages in thread From: sashiko-bot @ 2026-10-20 8:03 UTC (permalink / raw) To: Yu Zhang; +Cc: linux-usb > fsg_store_nofua() syncs the LUN's backing file when switching from async > to sync mode: > > if (!nofua && curlun->nofua) > fsg_lun_fsync_sub(curlun); > > fsg_lun_fsync_sub() dereferences curlun->filp (vfs_fsync(filp)). Unlike > every other store that touches the backing file -- fsg_store_ro() and > fsg_store_cdrom() (down_read), fsg_store_file() (down_write) -- > fsg_store_nofua() holds no filesem while doing so. > > A concurrent close of the backing file runs under down_write(filesem) and > does fput(curlun->filp) in fsg_lun_close() (fsg_store_file() with an empty > path, the SCSI START_STOP eject in do_start_stop(), or forced_eject), so > it can free the file while fsg_store_nofua() is dereferencing it: > [ ... ] > Reproduced on a mass_storage gadget over dummy_hcd by writing lun.N/nofua > (1 then 0) from one task while another task rewrites and clears lun.N/file. > > Cc: stable@vger.kernel.org > Signed-off-by: Yu Zhang <yuz08559@gmail.com> Sashiko has reviewed this patch and found no issues. -- Sashiko AI review · https://sashiko.dev/#/patchset/20261020023050.87701-1-yuz08559@gmail.com?part=1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF 2026-10-20 2:30 ` [PATCH v2] " Yu Zhang 2026-10-20 8:03 ` sashiko-bot @ 2026-10-20 14:11 ` Alan Stern 1 sibling, 0 replies; 7+ messages in thread From: Alan Stern @ 2026-10-20 14:11 UTC (permalink / raw) To: Yu Zhang; +Cc: Greg Kroah-Hartman, linux-usb, linux-kernel, stable On Tue, Oct 20, 2026 at 12:30:50PM +1000, Yu Zhang wrote: > fsg_store_nofua() syncs the LUN's backing file when switching from async > to sync mode: > > if (!nofua && curlun->nofua) > fsg_lun_fsync_sub(curlun); > > fsg_lun_fsync_sub() dereferences curlun->filp (vfs_fsync(filp)). Unlike > every other store that touches the backing file -- fsg_store_ro() and > fsg_store_cdrom() (down_read), fsg_store_file() (down_write) -- > fsg_store_nofua() holds no filesem while doing so. > > A concurrent close of the backing file runs under down_write(filesem) and > does fput(curlun->filp) in fsg_lun_close() (fsg_store_file() with an empty > path, the SCSI START_STOP eject in do_start_stop(), or forced_eject), so > it can free the file while fsg_store_nofua() is dereferencing it: > > BUG: KASAN: slab-use-after-free in vfs_fsync+0x20/0xe0 > vfs_fsync+0x20/0xe0 > fsg_lun_fsync_sub+0xa0/0xd0 > fsg_store_nofua+0x138/0x160 > configfs_write_iter+0x1a7/0x250 > vfs_write+0x5df/0x940 > Allocated by task 3459: > fsg_store_file+0x150/0x1f0 > configfs_write_iter+0x1a7/0x250 > Freed by task 3459: > __fput+0x456/0x6c0 > __fput_deferred+0x10d/0x330 > fsg_store_file+0x9e/0x1f0 > configfs_write_iter+0x1a7/0x250 > > Hold filesem for read around the fsync in fsg_store_nofua() so the backing > file cannot be closed underneath it. The lock is only needed when the > fsync actually runs, so it is taken only in that branch. > > Reproduced on a mass_storage gadget over dummy_hcd by writing lun.N/nofua > (1 then 0) from one task while another task rewrites and clears lun.N/file. > > Cc: stable@vger.kernel.org > Signed-off-by: Yu Zhang <yuz08559@gmail.com> > --- > v2: > - Use guard(rwsem_read) instead of a bare down_read()/up_read() pair > (Greg KH). > - Take filesem only inside the branch that actually calls > fsg_lun_fsync_sub(), not around the whole function / the > curlun->nofua assignment, since the lock is only needed for the sync > (Alan Stern). > > v1: https://lore.kernel.org/all/20261018130519.65850-1-yuz08559@gmail.com/ > > drivers/usb/gadget/function/f_mass_storage.c | 8 ++++++-- > drivers/usb/gadget/function/storage_common.c | 7 +++++-- > drivers/usb/gadget/function/storage_common.h | 3 ++- > 3 files changed, 13 insertions(+), 5 deletions(-) Acked-by: Alan Stern <stern@rowland.harvard.edu> > > diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c > index b7b06cb..6712566 100644 > --- a/drivers/usb/gadget/function/f_mass_storage.c > +++ b/drivers/usb/gadget/function/f_mass_storage.c > @@ -2663,8 +2663,9 @@ static ssize_t nofua_store(struct device *dev, struct device_attribute *attr, > const char *buf, size_t count) > { > struct fsg_lun *curlun = fsg_lun_from_dev(dev); > + struct rw_semaphore *filesem = dev_get_drvdata(dev); > > - return fsg_store_nofua(curlun, buf, count); > + return fsg_store_nofua(curlun, filesem, buf, count); > } > > static ssize_t file_store(struct device *dev, struct device_attribute *attr, > @@ -3239,7 +3240,10 @@ static ssize_t fsg_lun_opts_nofua_show(struct config_item *item, char *page) > static ssize_t fsg_lun_opts_nofua_store(struct config_item *item, > const char *page, size_t len) > { > - return fsg_store_nofua(to_fsg_lun_opts(item)->lun, page, len); > + struct fsg_lun_opts *opts = to_fsg_lun_opts(item); > + struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); > + > + return fsg_store_nofua(opts->lun, &fsg_opts->common->filesem, page, len); > } > > CONFIGFS_ATTR(fsg_lun_opts_, nofua); > diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c > index 75831f2..51a70f2 100644 > --- a/drivers/usb/gadget/function/storage_common.c > +++ b/drivers/usb/gadget/function/storage_common.c > @@ -415,7 +415,8 @@ ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, > } > EXPORT_SYMBOL_GPL(fsg_store_ro); > > -ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) > +ssize_t fsg_store_nofua(struct fsg_lun *curlun, struct rw_semaphore *filesem, > + const char *buf, size_t count) > { > bool nofua; > int ret; > @@ -425,8 +426,10 @@ ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) > return ret; > > /* Sync data when switching from async mode to sync */ > - if (!nofua && curlun->nofua) > + if (!nofua && curlun->nofua) { > + guard(rwsem_read)(filesem); > fsg_lun_fsync_sub(curlun); > + } > > curlun->nofua = nofua; > > diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h > index 11ac785..347a86b 100644 > --- a/drivers/usb/gadget/function/storage_common.h > +++ b/drivers/usb/gadget/function/storage_common.h > @@ -210,7 +210,8 @@ ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf); > ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf); > ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, > const char *buf, size_t count); > -ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count); > +ssize_t fsg_store_nofua(struct fsg_lun *curlun, struct rw_semaphore *filesem, > + const char *buf, size_t count); > ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, > const char *buf, size_t count); > ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-20 14:11 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-18 13:05 [PATCH] usb: gadget: f_mass_storage: take filesem in nofua store to fix backing-file UAF Yu Zhang 2026-10-18 13:08 ` sashiko-bot 2026-10-18 13:34 ` Greg Kroah-Hartman 2026-10-18 15:50 ` Alan Stern 2026-10-20 2:30 ` [PATCH v2] " Yu Zhang 2026-10-20 8:03 ` sashiko-bot 2026-10-20 14:11 ` Alan Stern
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox