| From e4a9ccdd1c03b3dc58214874399d24331ea0a3ab Mon Sep 17 00:00:00 2001 |
| From: Greg Kurz <groug@kaod.org> |
| Date: Fri, 4 Jun 2021 18:11:52 +0200 |
| Subject: fuse: Fix infinite loop in sget_fc() |
| |
| From: Greg Kurz <groug@kaod.org> |
| |
| commit e4a9ccdd1c03b3dc58214874399d24331ea0a3ab upstream. |
| |
| We don't set the SB_BORN flag on submounts. This is wrong as these |
| superblocks are then considered as partially constructed or dying |
| in the rest of the code and can break some assumptions. |
| |
| One such case is when you have a virtiofs filesystem with submounts |
| and you try to mount it again : virtio_fs_get_tree() tries to obtain |
| a superblock with sget_fc(). The logic in sget_fc() is to loop until |
| it has either found an existing matching superblock with SB_BORN set |
| or to create a brand new one. It is assumed that a superblock without |
| SB_BORN is transient and the loop is restarted. Forgetting to set |
| SB_BORN on submounts hence causes sget_fc() to retry forever. |
| |
| Setting SB_BORN requires special care, i.e. a write barrier for |
| super_cache_count() which can check SB_BORN without taking any lock. |
| We should call vfs_get_tree() to deal with that but this requires |
| to have a proper ->get_tree() implementation for submounts, which |
| is a bigger piece of work. Go for a simple bug fix in the meatime. |
| |
| Fixes: bf109c64040f ("fuse: implement crossmounts") |
| Cc: stable@vger.kernel.org # v5.10+ |
| Signed-off-by: Greg Kurz <groug@kaod.org> |
| Reviewed-by: Max Reitz <mreitz@redhat.com> |
| Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/fuse/dir.c | 11 +++++++++++ |
| 1 file changed, 11 insertions(+) |
| |
| --- a/fs/fuse/dir.c |
| +++ b/fs/fuse/dir.c |
| @@ -353,6 +353,17 @@ static struct vfsmount *fuse_dentry_auto |
| |
| sb->s_flags |= SB_ACTIVE; |
| fsc->root = dget(sb->s_root); |
| + |
| + /* |
| + * FIXME: setting SB_BORN requires a write barrier for |
| + * super_cache_count(). We should actually come |
| + * up with a proper ->get_tree() implementation |
| + * for submounts and call vfs_get_tree() to take |
| + * care of the write barrier. |
| + */ |
| + smp_wmb(); |
| + sb->s_flags |= SB_BORN; |
| + |
| /* We are done configuring the superblock, so unlock it */ |
| up_write(&sb->s_umount); |
| |