Skip to content

Commit 0a69b6b

Browse files
cmaiolinoakpm00
authored andcommitted
tmpfs: fix race on handling dquot rbtree
A syzkaller reproducer found a race while attempting to remove dquot information from the rb tree. Fetching the rb_tree root node must also be protected by the dqopt->dqio_sem, otherwise, giving the right timing, shmem_release_dquot() will trigger a warning because it couldn't find a node in the tree, when the real reason was the root node changing before the search starts: Thread 1 Thread 2 - shmem_release_dquot() - shmem_{acquire,release}_dquot() - fetch ROOT - Fetch ROOT - acquire dqio_sem - wait dqio_sem - do something, triger a tree rebalance - release dqio_sem - acquire dqio_sem - start searching for the node, but from the wrong location, missing the node, and triggering a warning. Link: https://lkml.kernel.org/r/[email protected] Fixes: eafc474 ("shmem: prepare shmem quota infrastructure") Signed-off-by: Carlos Maiolino <[email protected]> Reported-by: Ubisectech Sirius <[email protected]> Reviewed-by: Jan Kara <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 105840e commit 0a69b6b

File tree

1 file changed

+7
-3
lines changed

1 file changed

+7
-3
lines changed

mm/shmem_quota.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ static int shmem_free_file_info(struct super_block *sb, int type)
116116
static int shmem_get_next_id(struct super_block *sb, struct kqid *qid)
117117
{
118118
struct mem_dqinfo *info = sb_dqinfo(sb, qid->type);
119-
struct rb_node *node = ((struct rb_root *)info->dqi_priv)->rb_node;
119+
struct rb_node *node;
120120
qid_t id = from_kqid(&init_user_ns, *qid);
121121
struct quota_info *dqopt = sb_dqopt(sb);
122122
struct quota_id *entry = NULL;
@@ -126,6 +126,7 @@ static int shmem_get_next_id(struct super_block *sb, struct kqid *qid)
126126
return -ESRCH;
127127

128128
down_read(&dqopt->dqio_sem);
129+
node = ((struct rb_root *)info->dqi_priv)->rb_node;
129130
while (node) {
130131
entry = rb_entry(node, struct quota_id, node);
131132

@@ -165,7 +166,7 @@ static int shmem_get_next_id(struct super_block *sb, struct kqid *qid)
165166
static int shmem_acquire_dquot(struct dquot *dquot)
166167
{
167168
struct mem_dqinfo *info = sb_dqinfo(dquot->dq_sb, dquot->dq_id.type);
168-
struct rb_node **n = &((struct rb_root *)info->dqi_priv)->rb_node;
169+
struct rb_node **n;
169170
struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;
170171
struct rb_node *parent = NULL, *new_node = NULL;
171172
struct quota_id *new_entry, *entry;
@@ -176,6 +177,8 @@ static int shmem_acquire_dquot(struct dquot *dquot)
176177
mutex_lock(&dquot->dq_lock);
177178

178179
down_write(&dqopt->dqio_sem);
180+
n = &((struct rb_root *)info->dqi_priv)->rb_node;
181+
179182
while (*n) {
180183
parent = *n;
181184
entry = rb_entry(parent, struct quota_id, node);
@@ -264,7 +267,7 @@ static bool shmem_is_empty_dquot(struct dquot *dquot)
264267
static int shmem_release_dquot(struct dquot *dquot)
265268
{
266269
struct mem_dqinfo *info = sb_dqinfo(dquot->dq_sb, dquot->dq_id.type);
267-
struct rb_node *node = ((struct rb_root *)info->dqi_priv)->rb_node;
270+
struct rb_node *node;
268271
qid_t id = from_kqid(&init_user_ns, dquot->dq_id);
269272
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
270273
struct quota_id *entry = NULL;
@@ -275,6 +278,7 @@ static int shmem_release_dquot(struct dquot *dquot)
275278
goto out_dqlock;
276279

277280
down_write(&dqopt->dqio_sem);
281+
node = ((struct rb_root *)info->dqi_priv)->rb_node;
278282
while (node) {
279283
entry = rb_entry(node, struct quota_id, node);
280284

0 commit comments

Comments
 (0)