Skip to content

Commit 053fc4f

Browse files
author
Al Viro
committed
fuse: fix UAF in rcu pathwalks
->permission(), ->get_link() and ->inode_get_acl() might dereference ->s_fs_info (and, in case of ->permission(), ->s_fs_info->fc->user_ns as well) when called from rcu pathwalk. Freeing ->s_fs_info->fc is rcu-delayed; we need to make freeing ->s_fs_info and dropping ->user_ns rcu-delayed too. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent e31f0a5 commit 053fc4f

3 files changed

Lines changed: 13 additions & 6 deletions

File tree

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)