Skip to content

Commit 66a97c2

Browse files
committed
Merge tag 'pull-fixes.pathwalk-rcu-2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull RCU pathwalk fixes from Al Viro: "We still have some races in filesystem methods when exposed to RCU pathwalk. This series is a result of code audit (the second round of it) and it should deal with most of that stuff. Still pending: ntfs3 ->d_hash()/->d_compare() and ceph_d_revalidate(). Up to maintainers (a note for NTFS folks - when documentation says that a method may not block, it *does* imply that blocking allocations are to be avoided. Really)" [ More explanations for people who aren't familiar with the vagaries of RCU path walking: most of it is hidden from filesystems, but if a filesystem actively participates in the low-level path walking it needs to make sure the fields involved in that walk are RCU-safe. That "actively participate in low-level path walking" includes things like having its own ->d_hash()/->d_compare() routines, or by having its own directory permission function that doesn't just use the common helpers. Having a ->d_revalidate() function will also have this issue. Note that instead of making everything RCU safe you can also choose to abort the RCU pathwalk if your operation cannot be done safely under RCU, but that obviously comes with a performance penalty. One common pattern is to allow the simple cases under RCU, and abort only if you need to do something more complicated. So not everything needs to be RCU-safe, and things like the inode etc that the VFS itself maintains obviously already are. But these fixes tend to be about properly RCU-delaying things like ->s_fs_info that are maintained by the filesystem and that got potentially released too early. - Linus ] * tag 'pull-fixes.pathwalk-rcu-2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: ext4_get_link(): fix breakage in RCU mode cifs_get_link(): bail out in unsafe case fuse: fix UAF in rcu pathwalks procfs: make freeing proc_fs_info rcu-delayed procfs: move dropping pde and pid from ->evict_inode() to ->free_inode() nfs: fix UAF on pathwalk running into umount nfs: make nfs_set_verifier() safe for use in RCU pathwalk afs: fix __afs_break_callback() / afs_drop_open_mmap() race hfsplus: switch to rcu-delayed unloading of nls and freeing ->s_fs_info exfat: move freeing sbi, upcase table and dropping nls into rcu-delayed helper affs: free affs_sb_info with kfree_rcu() rcu pathwalk: prevent bogus hard errors from may_lookup() fs/super.c: don't drop ->s_user_ns until we free struct super_block itself
2 parents 9b24349 + 9fa8e28 commit 66a97c2

File tree

22 files changed

+88
-63
lines changed

22 files changed

+88
-63
lines changed

fs/affs/affs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ struct affs_sb_info {
105105
int work_queued; /* non-zero delayed work is queued */
106106
struct delayed_work sb_work; /* superblock flush delayed work */
107107
spinlock_t work_lock; /* protects sb_work and work_queued */
108+
struct rcu_head rcu;
108109
};
109110

110111
#define AFFS_MOUNT_SF_INTL 0x0001 /* International filesystem. */

fs/affs/super.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ static void affs_kill_sb(struct super_block *sb)
640640
affs_brelse(sbi->s_root_bh);
641641
kfree(sbi->s_prefix);
642642
mutex_destroy(&sbi->s_bmlock);
643-
kfree(sbi);
643+
kfree_rcu(sbi, rcu);
644644
}
645645
}
646646

fs/afs/file.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,13 +417,17 @@ static void afs_add_open_mmap(struct afs_vnode *vnode)
417417

418418
static void afs_drop_open_mmap(struct afs_vnode *vnode)
419419
{
420-
if (!atomic_dec_and_test(&vnode->cb_nr_mmap))
420+
if (atomic_add_unless(&vnode->cb_nr_mmap, -1, 1))
421421
return;
422422

423423
down_write(&vnode->volume->open_mmaps_lock);
424424

425-
if (atomic_read(&vnode->cb_nr_mmap) == 0)
425+
read_seqlock_excl(&vnode->cb_lock);
426+
// the only place where ->cb_nr_mmap may hit 0
427+
// see __afs_break_callback() for the other side...
428+
if (atomic_dec_and_test(&vnode->cb_nr_mmap))
426429
list_del_init(&vnode->cb_mmap_link);
430+
read_sequnlock_excl(&vnode->cb_lock);
427431

428432
up_write(&vnode->volume->open_mmaps_lock);
429433
flush_work(&vnode->cb_work);

fs/exfat/exfat_fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ struct exfat_sb_info {
275275

276276
spinlock_t inode_hash_lock;
277277
struct hlist_head inode_hashtable[EXFAT_HASH_SIZE];
278+
struct rcu_head rcu;
278279
};
279280

280281
#define EXFAT_CACHE_VALID 0

fs/exfat/nls.c

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,6 @@ static int exfat_load_upcase_table(struct super_block *sb,
655655
unsigned int sect_size = sb->s_blocksize;
656656
unsigned int i, index = 0;
657657
u32 chksum = 0;
658-
int ret;
659658
unsigned char skip = false;
660659
unsigned short *upcase_table;
661660

@@ -673,8 +672,7 @@ static int exfat_load_upcase_table(struct super_block *sb,
673672
if (!bh) {
674673
exfat_err(sb, "failed to read sector(0x%llx)",
675674
(unsigned long long)sector);
676-
ret = -EIO;
677-
goto free_table;
675+
return -EIO;
678676
}
679677
sector++;
680678
for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) {
@@ -701,15 +699,12 @@ static int exfat_load_upcase_table(struct super_block *sb,
701699

702700
exfat_err(sb, "failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)",
703701
index, chksum, utbl_checksum);
704-
ret = -EINVAL;
705-
free_table:
706-
exfat_free_upcase_table(sbi);
707-
return ret;
702+
return -EINVAL;
708703
}
709704

710705
static int exfat_load_default_upcase_table(struct super_block *sb)
711706
{
712-
int i, ret = -EIO;
707+
int i;
713708
struct exfat_sb_info *sbi = EXFAT_SB(sb);
714709
unsigned char skip = false;
715710
unsigned short uni = 0, *upcase_table;
@@ -740,8 +735,7 @@ static int exfat_load_default_upcase_table(struct super_block *sb)
740735
return 0;
741736

742737
/* FATAL error: default upcase table has error */
743-
exfat_free_upcase_table(sbi);
744-
return ret;
738+
return -EIO;
745739
}
746740

747741
int exfat_create_upcase_table(struct super_block *sb)

fs/exfat/super.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ static void exfat_put_super(struct super_block *sb)
3939
exfat_free_bitmap(sbi);
4040
brelse(sbi->boot_bh);
4141
mutex_unlock(&sbi->s_lock);
42-
43-
unload_nls(sbi->nls_io);
44-
exfat_free_upcase_table(sbi);
4542
}
4643

4744
static int exfat_sync_fs(struct super_block *sb, int wait)
@@ -600,7 +597,7 @@ static int __exfat_fill_super(struct super_block *sb)
600597
ret = exfat_load_bitmap(sb);
601598
if (ret) {
602599
exfat_err(sb, "failed to load alloc-bitmap");
603-
goto free_upcase_table;
600+
goto free_bh;
604601
}
605602

606603
ret = exfat_count_used_clusters(sb, &sbi->used_clusters);
@@ -613,8 +610,6 @@ static int __exfat_fill_super(struct super_block *sb)
613610

614611
free_alloc_bitmap:
615612
exfat_free_bitmap(sbi);
616-
free_upcase_table:
617-
exfat_free_upcase_table(sbi);
618613
free_bh:
619614
brelse(sbi->boot_bh);
620615
return ret;
@@ -701,12 +696,10 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
701696
sb->s_root = NULL;
702697

703698
free_table:
704-
exfat_free_upcase_table(sbi);
705699
exfat_free_bitmap(sbi);
706700
brelse(sbi->boot_bh);
707701

708702
check_nls_io:
709-
unload_nls(sbi->nls_io);
710703
return err;
711704
}
712705

@@ -771,13 +764,22 @@ static int exfat_init_fs_context(struct fs_context *fc)
771764
return 0;
772765
}
773766

767+
static void delayed_free(struct rcu_head *p)
768+
{
769+
struct exfat_sb_info *sbi = container_of(p, struct exfat_sb_info, rcu);
770+
771+
unload_nls(sbi->nls_io);
772+
exfat_free_upcase_table(sbi);
773+
exfat_free_sbi(sbi);
774+
}
775+
774776
static void exfat_kill_sb(struct super_block *sb)
775777
{
776778
struct exfat_sb_info *sbi = sb->s_fs_info;
777779

778780
kill_block_super(sb);
779781
if (sbi)
780-
exfat_free_sbi(sbi);
782+
call_rcu(&sbi->rcu, delayed_free);
781783
}
782784

783785
static struct file_system_type exfat_fs_type = {

fs/ext4/symlink.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,12 @@ static const char *ext4_get_link(struct dentry *dentry, struct inode *inode,
9292

9393
if (!dentry) {
9494
bh = ext4_getblk(NULL, inode, 0, EXT4_GET_BLOCKS_CACHED_NOWAIT);
95-
if (IS_ERR(bh))
96-
return ERR_CAST(bh);
97-
if (!bh || !ext4_buffer_uptodate(bh))
95+
if (IS_ERR(bh) || !bh)
9896
return ERR_PTR(-ECHILD);
97+
if (!ext4_buffer_uptodate(bh)) {
98+
brelse(bh);
99+
return ERR_PTR(-ECHILD);
100+
}
99101
} else {
100102
bh = ext4_bread(NULL, inode, 0, 0);
101103
if (IS_ERR(bh))

fs/fuse/cuse.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,7 @@ static int cuse_send_init(struct cuse_conn *cc)
474474

475475
static void cuse_fc_release(struct fuse_conn *fc)
476476
{
477-
struct cuse_conn *cc = fc_to_cc(fc);
478-
kfree_rcu(cc, fc.rcu);
477+
kfree(fc_to_cc(fc));
479478
}
480479

481480
/**

fs/fuse/fuse_i.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,7 @@ struct fuse_mount {
888888

889889
/* Entry on fc->mounts */
890890
struct list_head fc_entry;
891+
struct rcu_head rcu;
891892
};
892893

893894
static inline struct fuse_mount *get_fuse_mount_super(struct super_block *sb)

fs/fuse/inode.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,14 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
930930
}
931931
EXPORT_SYMBOL_GPL(fuse_conn_init);
932932

933+
static void delayed_release(struct rcu_head *p)
934+
{
935+
struct fuse_conn *fc = container_of(p, struct fuse_conn, rcu);
936+
937+
put_user_ns(fc->user_ns);
938+
fc->release(fc);
939+
}
940+
933941
void fuse_conn_put(struct fuse_conn *fc)
934942
{
935943
if (refcount_dec_and_test(&fc->count)) {
@@ -941,13 +949,12 @@ void fuse_conn_put(struct fuse_conn *fc)
941949
if (fiq->ops->release)
942950
fiq->ops->release(fiq);
943951
put_pid_ns(fc->pid_ns);
944-
put_user_ns(fc->user_ns);
945952
bucket = rcu_dereference_protected(fc->curr_bucket, 1);
946953
if (bucket) {
947954
WARN_ON(atomic_read(&bucket->count) != 1);
948955
kfree(bucket);
949956
}
950-
fc->release(fc);
957+
call_rcu(&fc->rcu, delayed_release);
951958
}
952959
}
953960
EXPORT_SYMBOL_GPL(fuse_conn_put);
@@ -1366,7 +1373,7 @@ EXPORT_SYMBOL_GPL(fuse_send_init);
13661373
void fuse_free_conn(struct fuse_conn *fc)
13671374
{
13681375
WARN_ON(!list_empty(&fc->devices));
1369-
kfree_rcu(fc, rcu);
1376+
kfree(fc);
13701377
}
13711378
EXPORT_SYMBOL_GPL(fuse_free_conn);
13721379

@@ -1902,7 +1909,7 @@ static void fuse_sb_destroy(struct super_block *sb)
19021909
void fuse_mount_destroy(struct fuse_mount *fm)
19031910
{
19041911
fuse_conn_put(fm->fc);
1905-
kfree(fm);
1912+
kfree_rcu(fm, rcu);
19061913
}
19071914
EXPORT_SYMBOL(fuse_mount_destroy);
19081915

0 commit comments

Comments
 (0)