2022-06-01 20:53:25

by Daniil Lunev

[permalink] [raw]
Subject: [PATCH v4 1/2] fs/super: function to prevent super re-use

From: Daniil Lunev <[email protected]>

The function is to be called from filesystem-specific code to mark a
superblock to be ignored by superblock test and thus never re-used. The
function also unregisters bdi if the bdi is per-superblock to avoid
collision if a new superblock is created to represent the filesystem.
generic_shutdown_super() skips unregistering bdi for a retired
superlock as it assumes retire function has already done it.

Signed-off-by: Daniil Lunev <[email protected]>
Signed-off-by: Daniil Lunev <[email protected]>
---

Changes in v4:
- Simplify condition according to Christoph Hellwig's comments.

Changes in v3:
- Back to state tracking from v1
- Use s_iflag to mark superblocked ignored
- Only unregister private bdi in retire, without freeing

Changes in v2:
- Remove super from list of superblocks instead of using a flag

fs/super.c | 28 ++++++++++++++++++++++++++--
include/linux/fs.h | 2 ++
2 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/fs/super.c b/fs/super.c
index f1d4a193602d6..3fb9fc8d61160 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -422,6 +422,30 @@ bool trylock_super(struct super_block *sb)
return false;
}

+/**
+ * retire_super - prevernts superblock from being reused
+ * @sb: superblock to retire
+ *
+ * The function marks superblock to be ignored in superblock test, which
+ * prevents it from being reused for any new mounts. If the superblock has
+ * a private bdi, it also unregisters it, but doesn't reduce the refcount
+ * of the superblock to prevent potential races. The refcount is reduced
+ * by generic_shutdown_super(). The function can not be called concurrently
+ * with generic_shutdown_super(). It is safe to call the function multiple
+ * times, subsequent calls have no effect.
+ */
+void retire_super(struct super_block *sb)
+{
+ down_write(&sb->s_umount);
+ if (sb->s_iflags & SB_I_PERSB_BDI) {
+ bdi_unregister(sb->s_bdi);
+ sb->s_iflags &= ~SB_I_PERSB_BDI;
+ }
+ sb->s_iflags |= SB_I_RETIRED;
+ up_write(&sb->s_umount);
+}
+EXPORT_SYMBOL(retire_super);
+
/**
* generic_shutdown_super - common helper for ->kill_sb()
* @sb: superblock to kill
@@ -1216,7 +1240,7 @@ static int set_bdev_super_fc(struct super_block *s, struct fs_context *fc)

static int test_bdev_super_fc(struct super_block *s, struct fs_context *fc)
{
- return s->s_bdev == fc->sget_key;
+ return !(s->s_iflags & SB_I_RETIRED) && s->s_bdev == fc->sget_key;
}

/**
@@ -1307,7 +1331,7 @@ EXPORT_SYMBOL(get_tree_bdev);

static int test_bdev_super(struct super_block *s, void *data)
{
- return (void *)s->s_bdev == data;
+ return !(s->s_iflags & SB_I_RETIRED) && (void *)s->s_bdev == data;
}

struct dentry *mount_bdev(struct file_system_type *fs_type,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index bbde95387a23a..ef392fd2361bd 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1411,6 +1411,7 @@ extern int send_sigurg(struct fown_struct *fown);
#define SB_I_SKIP_SYNC 0x00000100 /* Skip superblock at global sync */
#define SB_I_PERSB_BDI 0x00000200 /* has a per-sb bdi */
#define SB_I_TS_EXPIRY_WARNED 0x00000400 /* warned about timestamp range expiry */
+#define SB_I_RETIRED 0x00000800 /* superblock shouldn't be reused */

/* Possible states of 'frozen' field */
enum {
@@ -2424,6 +2425,7 @@ extern struct dentry *mount_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int));
extern struct dentry *mount_subtree(struct vfsmount *mnt, const char *path);
+void retire_super(struct super_block *sb);
void generic_shutdown_super(struct super_block *sb);
void kill_block_super(struct super_block *sb);
void kill_anon_super(struct super_block *sb);
--
2.31.0



2022-07-18 10:23:39

by Miklos Szeredi

[permalink] [raw]
Subject: Re: [PATCH v4 1/2] fs/super: function to prevent super re-use

On Wed, 1 Jun 2022 at 03:11, Daniil Lunev <[email protected]> wrote:
>
> From: Daniil Lunev <[email protected]>
>
> The function is to be called from filesystem-specific code to mark a
> superblock to be ignored by superblock test and thus never re-used. The
> function also unregisters bdi if the bdi is per-superblock to avoid
> collision if a new superblock is created to represent the filesystem.
> generic_shutdown_super() skips unregistering bdi for a retired
> superlock as it assumes retire function has already done it.
>
> Signed-off-by: Daniil Lunev <[email protected]>
> Signed-off-by: Daniil Lunev <[email protected]>
> ---
>
> Changes in v4:
> - Simplify condition according to Christoph Hellwig's comments.
>
> Changes in v3:
> - Back to state tracking from v1
> - Use s_iflag to mark superblocked ignored
> - Only unregister private bdi in retire, without freeing
>
> Changes in v2:
> - Remove super from list of superblocks instead of using a flag
>
> fs/super.c | 28 ++++++++++++++++++++++++++--
> include/linux/fs.h | 2 ++
> 2 files changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/fs/super.c b/fs/super.c
> index f1d4a193602d6..3fb9fc8d61160 100644
> --- a/fs/super.c
> +++ b/fs/super.c
> @@ -422,6 +422,30 @@ bool trylock_super(struct super_block *sb)
> return false;
> }
>
> +/**
> + * retire_super - prevernts superblock from being reused

s/prevernts/prevents/

> + * @sb: superblock to retire
> + *
> + * The function marks superblock to be ignored in superblock test, which
> + * prevents it from being reused for any new mounts.

This works for block supers and nothing else, at least as this patch
stands. That might be okay, but should at least be documented.

Thanks,
Miklos

2022-07-22 00:55:21

by Daniil Lunev

[permalink] [raw]
Subject: Re: [PATCH v4 1/2] fs/super: function to prevent super re-use

Hi Miklos,
Thanks for your response and apologies for my delayed reply. Do I understand
correctly that to cover non-block devices I would need to add the same check
to test_keyed_super and to test_single_super? Am I missing any other place?
--Daniil

On Mon, Jul 18, 2022 at 7:51 PM Miklos Szeredi <[email protected]> wrote:
>
> On Wed, 1 Jun 2022 at 03:11, Daniil Lunev <[email protected]> wrote:
> >
> > From: Daniil Lunev <[email protected]>
> >
> > The function is to be called from filesystem-specific code to mark a
> > superblock to be ignored by superblock test and thus never re-used. The
> > function also unregisters bdi if the bdi is per-superblock to avoid
> > collision if a new superblock is created to represent the filesystem.
> > generic_shutdown_super() skips unregistering bdi for a retired
> > superlock as it assumes retire function has already done it.
> >
> > Signed-off-by: Daniil Lunev <[email protected]>
> > Signed-off-by: Daniil Lunev <[email protected]>
> > ---
> >
> > Changes in v4:
> > - Simplify condition according to Christoph Hellwig's comments.
> >
> > Changes in v3:
> > - Back to state tracking from v1
> > - Use s_iflag to mark superblocked ignored
> > - Only unregister private bdi in retire, without freeing
> >
> > Changes in v2:
> > - Remove super from list of superblocks instead of using a flag
> >
> > fs/super.c | 28 ++++++++++++++++++++++++++--
> > include/linux/fs.h | 2 ++
> > 2 files changed, 28 insertions(+), 2 deletions(-)
> >
> > diff --git a/fs/super.c b/fs/super.c
> > index f1d4a193602d6..3fb9fc8d61160 100644
> > --- a/fs/super.c
> > +++ b/fs/super.c
> > @@ -422,6 +422,30 @@ bool trylock_super(struct super_block *sb)
> > return false;
> > }
> >
> > +/**
> > + * retire_super - prevernts superblock from being reused
>
> s/prevernts/prevents/
>
> > + * @sb: superblock to retire
> > + *
> > + * The function marks superblock to be ignored in superblock test, which
> > + * prevents it from being reused for any new mounts.
>
> This works for block supers and nothing else, at least as this patch
> stands. That might be okay, but should at least be documented.
>
> Thanks,
> Miklos