mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 02:21:52 +09:00
fuse: fix UAF in rcu pathwalks
[ Upstream commit 053fc4f755ad43cf35210677bcba798ccdc48d0c ] ->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> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
fb8b3b44e0
commit
a8f650b93e
@@ -474,8 +474,7 @@ err:
|
|||||||
|
|
||||||
static void cuse_fc_release(struct fuse_conn *fc)
|
static void cuse_fc_release(struct fuse_conn *fc)
|
||||||
{
|
{
|
||||||
struct cuse_conn *cc = fc_to_cc(fc);
|
kfree(fc_to_cc(fc));
|
||||||
kfree_rcu(cc, fc.rcu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -872,6 +872,7 @@ struct fuse_mount {
|
|||||||
|
|
||||||
/* Entry on fc->mounts */
|
/* Entry on fc->mounts */
|
||||||
struct list_head fc_entry;
|
struct list_head fc_entry;
|
||||||
|
struct rcu_head rcu;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct fuse_mount *get_fuse_mount_super(struct super_block *sb)
|
static inline struct fuse_mount *get_fuse_mount_super(struct super_block *sb)
|
||||||
|
|||||||
@@ -925,6 +925,14 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fuse_conn_init);
|
EXPORT_SYMBOL_GPL(fuse_conn_init);
|
||||||
|
|
||||||
|
static void delayed_release(struct rcu_head *p)
|
||||||
|
{
|
||||||
|
struct fuse_conn *fc = container_of(p, struct fuse_conn, rcu);
|
||||||
|
|
||||||
|
put_user_ns(fc->user_ns);
|
||||||
|
fc->release(fc);
|
||||||
|
}
|
||||||
|
|
||||||
void fuse_conn_put(struct fuse_conn *fc)
|
void fuse_conn_put(struct fuse_conn *fc)
|
||||||
{
|
{
|
||||||
if (refcount_dec_and_test(&fc->count)) {
|
if (refcount_dec_and_test(&fc->count)) {
|
||||||
@@ -936,13 +944,12 @@ void fuse_conn_put(struct fuse_conn *fc)
|
|||||||
if (fiq->ops->release)
|
if (fiq->ops->release)
|
||||||
fiq->ops->release(fiq);
|
fiq->ops->release(fiq);
|
||||||
put_pid_ns(fc->pid_ns);
|
put_pid_ns(fc->pid_ns);
|
||||||
put_user_ns(fc->user_ns);
|
|
||||||
bucket = rcu_dereference_protected(fc->curr_bucket, 1);
|
bucket = rcu_dereference_protected(fc->curr_bucket, 1);
|
||||||
if (bucket) {
|
if (bucket) {
|
||||||
WARN_ON(atomic_read(&bucket->count) != 1);
|
WARN_ON(atomic_read(&bucket->count) != 1);
|
||||||
kfree(bucket);
|
kfree(bucket);
|
||||||
}
|
}
|
||||||
fc->release(fc);
|
call_rcu(&fc->rcu, delayed_release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fuse_conn_put);
|
EXPORT_SYMBOL_GPL(fuse_conn_put);
|
||||||
@@ -1356,7 +1363,7 @@ EXPORT_SYMBOL_GPL(fuse_send_init);
|
|||||||
void fuse_free_conn(struct fuse_conn *fc)
|
void fuse_free_conn(struct fuse_conn *fc)
|
||||||
{
|
{
|
||||||
WARN_ON(!list_empty(&fc->devices));
|
WARN_ON(!list_empty(&fc->devices));
|
||||||
kfree_rcu(fc, rcu);
|
kfree(fc);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fuse_free_conn);
|
EXPORT_SYMBOL_GPL(fuse_free_conn);
|
||||||
|
|
||||||
@@ -1895,7 +1902,7 @@ static void fuse_sb_destroy(struct super_block *sb)
|
|||||||
void fuse_mount_destroy(struct fuse_mount *fm)
|
void fuse_mount_destroy(struct fuse_mount *fm)
|
||||||
{
|
{
|
||||||
fuse_conn_put(fm->fc);
|
fuse_conn_put(fm->fc);
|
||||||
kfree(fm);
|
kfree_rcu(fm, rcu);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(fuse_mount_destroy);
|
EXPORT_SYMBOL(fuse_mount_destroy);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user