Skip to content

Commit e7e9423

Browse files
committed
Merge tag 'v6.6-vfs.super.fixes.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull more superblock follow-on fixes from Christian Brauner: "This contains two more small follow-up fixes for the super work this cycle. I went through all filesystems once more and detected two minor issues that still needed fixing: - Some filesystems support mtd devices (e.g., mount -t jffs2 mtd2 /mnt). The mtd infrastructure uses the sb->s_mtd pointer to find an existing superblock. When the mtd device is put and sb->s_mtd cleared the superblock can still be found fs_supers and so this risks a use-after-free. Add a small patch that aligns mtd with what we did for regular block devices and switch keying to rely on sb->s_dev. (This was tested with mtd devices and jffs2 as xfstests doesn't support mtd devices.) - Switch nfs back to rely on kill_anon_super() so the superblock is removed from the list of active supers before sb->s_fs_info is freed" * tag 'v6.6-vfs.super.fixes.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: NFS: switch back to using kill_anon_super mtd: key superblock by device number fs: export sget_dev()
2 parents f441ff7 + 5069ba8 commit e7e9423

File tree

4 files changed

+58
-56
lines changed

4 files changed

+58
-56
lines changed

drivers/mtd/mtdsuper.c

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,6 @@
1919
#include <linux/fs_context.h>
2020
#include "mtdcore.h"
2121

22-
/*
23-
* compare superblocks to see if they're equivalent
24-
* - they are if the underlying MTD device is the same
25-
*/
26-
static int mtd_test_super(struct super_block *sb, struct fs_context *fc)
27-
{
28-
struct mtd_info *mtd = fc->sget_key;
29-
30-
if (sb->s_mtd == fc->sget_key) {
31-
pr_debug("MTDSB: Match on device %d (\"%s\")\n",
32-
mtd->index, mtd->name);
33-
return 1;
34-
}
35-
36-
pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
37-
sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name);
38-
return 0;
39-
}
40-
41-
/*
42-
* mark the superblock by the MTD device it is using
43-
* - set the device number to be the correct MTD block device for pesuperstence
44-
* of NFS exports
45-
*/
46-
static int mtd_set_super(struct super_block *sb, struct fs_context *fc)
47-
{
48-
sb->s_mtd = fc->sget_key;
49-
sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, sb->s_mtd->index);
50-
sb->s_bdi = bdi_get(mtd_bdi);
51-
return 0;
52-
}
53-
5422
/*
5523
* get a superblock on an MTD-backed filesystem
5624
*/
@@ -62,8 +30,7 @@ static int mtd_get_sb(struct fs_context *fc,
6230
struct super_block *sb;
6331
int ret;
6432

65-
fc->sget_key = mtd;
66-
sb = sget_fc(fc, mtd_test_super, mtd_set_super);
33+
sb = sget_dev(fc, MKDEV(MTD_BLOCK_MAJOR, mtd->index));
6734
if (IS_ERR(sb))
6835
return PTR_ERR(sb);
6936

@@ -77,6 +44,16 @@ static int mtd_get_sb(struct fs_context *fc,
7744
pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
7845
mtd->index, mtd->name);
7946

47+
/*
48+
* Would usually have been set with @sb_lock held but in
49+
* contrast to sb->s_bdev that's checked with only
50+
* @sb_lock held, nothing checks sb->s_mtd without also
51+
* holding sb->s_umount and we're holding sb->s_umount
52+
* here.
53+
*/
54+
sb->s_mtd = mtd;
55+
sb->s_bdi = bdi_get(mtd_bdi);
56+
8057
ret = fill_super(sb, fc);
8158
if (ret < 0)
8259
goto error_sb;

fs/nfs/super.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,15 +1339,13 @@ int nfs_get_tree_common(struct fs_context *fc)
13391339
void nfs_kill_super(struct super_block *s)
13401340
{
13411341
struct nfs_server *server = NFS_SB(s);
1342-
dev_t dev = s->s_dev;
13431342

13441343
nfs_sysfs_move_sb_to_server(server);
1345-
generic_shutdown_super(s);
1344+
kill_anon_super(s);
13461345

13471346
nfs_fscache_release_super_cookie(s);
13481347

13491348
nfs_free_server(server);
1350-
free_anon_bdev(dev);
13511349
}
13521350
EXPORT_SYMBOL_GPL(nfs_kill_super);
13531351

fs/super.c

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,50 @@ int get_tree_keyed(struct fs_context *fc,
13731373
}
13741374
EXPORT_SYMBOL(get_tree_keyed);
13751375

1376+
static int set_bdev_super(struct super_block *s, void *data)
1377+
{
1378+
s->s_dev = *(dev_t *)data;
1379+
return 0;
1380+
}
1381+
1382+
static int super_s_dev_set(struct super_block *s, struct fs_context *fc)
1383+
{
1384+
return set_bdev_super(s, fc->sget_key);
1385+
}
1386+
1387+
static int super_s_dev_test(struct super_block *s, struct fs_context *fc)
1388+
{
1389+
return !(s->s_iflags & SB_I_RETIRED) &&
1390+
s->s_dev == *(dev_t *)fc->sget_key;
1391+
}
1392+
1393+
/**
1394+
* sget_dev - Find or create a superblock by device number
1395+
* @fc: Filesystem context.
1396+
* @dev: device number
1397+
*
1398+
* Find or create a superblock using the provided device number that
1399+
* will be stored in fc->sget_key.
1400+
*
1401+
* If an extant superblock is matched, then that will be returned with
1402+
* an elevated reference count that the caller must transfer or discard.
1403+
*
1404+
* If no match is made, a new superblock will be allocated and basic
1405+
* initialisation will be performed (s_type, s_fs_info, s_id, s_dev will
1406+
* be set). The superblock will be published and it will be returned in
1407+
* a partially constructed state with SB_BORN and SB_ACTIVE as yet
1408+
* unset.
1409+
*
1410+
* Return: an existing or newly created superblock on success, an error
1411+
* pointer on failure.
1412+
*/
1413+
struct super_block *sget_dev(struct fs_context *fc, dev_t dev)
1414+
{
1415+
fc->sget_key = &dev;
1416+
return sget_fc(fc, super_s_dev_test, super_s_dev_set);
1417+
}
1418+
EXPORT_SYMBOL(sget_dev);
1419+
13761420
#ifdef CONFIG_BLOCK
13771421
/*
13781422
* Lock a super block that the callers holds a reference to.
@@ -1431,23 +1475,6 @@ const struct blk_holder_ops fs_holder_ops = {
14311475
};
14321476
EXPORT_SYMBOL_GPL(fs_holder_ops);
14331477

1434-
static int set_bdev_super(struct super_block *s, void *data)
1435-
{
1436-
s->s_dev = *(dev_t *)data;
1437-
return 0;
1438-
}
1439-
1440-
static int set_bdev_super_fc(struct super_block *s, struct fs_context *fc)
1441-
{
1442-
return set_bdev_super(s, fc->sget_key);
1443-
}
1444-
1445-
static int test_bdev_super_fc(struct super_block *s, struct fs_context *fc)
1446-
{
1447-
return !(s->s_iflags & SB_I_RETIRED) &&
1448-
s->s_dev == *(dev_t *)fc->sget_key;
1449-
}
1450-
14511478
int setup_bdev_super(struct super_block *sb, int sb_flags,
14521479
struct fs_context *fc)
14531480
{
@@ -1525,8 +1552,7 @@ int get_tree_bdev(struct fs_context *fc,
15251552
}
15261553

15271554
fc->sb_flags |= SB_NOSEC;
1528-
fc->sget_key = &dev;
1529-
s = sget_fc(fc, test_bdev_super_fc, set_bdev_super_fc);
1555+
s = sget_dev(fc, dev);
15301556
if (IS_ERR(s))
15311557
return PTR_ERR(s);
15321558

include/linux/fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,7 @@ struct super_block *sget(struct file_system_type *type,
23972397
int (*test)(struct super_block *,void *),
23982398
int (*set)(struct super_block *,void *),
23992399
int flags, void *data);
2400+
struct super_block *sget_dev(struct fs_context *fc, dev_t dev);
24002401

24012402
/* Alas, no aliases. Too much hassle with bringing module.h everywhere */
24022403
#define fops_get(fops) \

0 commit comments

Comments
 (0)