Skip to content

Commit c07a4da

Browse files
Zhihao Chengrichardweinberger
authored andcommitted
ubifs: Check @c->dirty_[n|p]n_cnt and @c->nroot state under @c->lp_mutex
The checking of @c->nroot->flags and @c->dirty_[n|p]n_cnt in function nothing_to_commit() is not atomic, which could be raced with modifying of lpt, for example: P1 P2 P3 run_gc ubifs_garbage_collect do_commit ubifs_return_leb ubifs_lpt_lookup_dirty dirty_cow_nnode do_commit nothing_to_commit if (test_bit(DIRTY_CNODE, &c->nroot->flags) // false test_and_set_bit(DIRTY_CNODE, &nnode->flags) c->dirty_nn_cnt += 1 ubifs_assert(c, c->dirty_nn_cnt == 0) // false ! Fetch a reproducer in Link: UBIFS error (ubi0:0 pid 2747): ubifs_assert_failed UBIFS assert failed: c->dirty_pn_cnt == 0, in fs/ubifs/commit.c Call Trace: ubifs_ro_mode+0x58/0x70 [ubifs] ubifs_assert_failed+0x6a/0x90 [ubifs] do_commit+0x5b7/0x930 [ubifs] ubifs_run_commit+0xc6/0x1a0 [ubifs] ubifs_sync_fs+0xd8/0x110 [ubifs] sync_filesystem+0xb4/0x120 do_syscall_64+0x6f/0x140 Fix it by checking @c->dirty_[n|p]n_cnt and @c->nroot state with @c->lp_mutex locked. Fixes: 944fdef ("UBIFS: do not start the commit if there is nothing to commit") Link: https://bugzilla.kernel.org/show_bug.cgi?id=218162 Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com> Signed-off-by: Richard Weinberger <richard@nod.at>
1 parent 2ba5b48 commit c07a4da

1 file changed

Lines changed: 12 additions & 1 deletion

File tree

fs/ubifs/commit.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,30 @@ static int nothing_to_commit(struct ubifs_info *c)
6969
if (c->zroot.znode && ubifs_zn_dirty(c->zroot.znode))
7070
return 0;
7171

72+
/*
73+
* Increasing @c->dirty_pn_cnt/@c->dirty_nn_cnt and marking
74+
* nnodes/pnodes as dirty in run_gc() could race with following
75+
* checking, which leads inconsistent states between @c->nroot
76+
* and @c->dirty_pn_cnt/@c->dirty_nn_cnt, holding @c->lp_mutex
77+
* to avoid that.
78+
*/
79+
mutex_lock(&c->lp_mutex);
7280
/*
7381
* Even though the TNC is clean, the LPT tree may have dirty nodes. For
7482
* example, this may happen if the budgeting subsystem invoked GC to
7583
* make some free space, and the GC found an LEB with only dirty and
7684
* free space. In this case GC would just change the lprops of this
7785
* LEB (by turning all space into free space) and unmap it.
7886
*/
79-
if (c->nroot && test_bit(DIRTY_CNODE, &c->nroot->flags))
87+
if (c->nroot && test_bit(DIRTY_CNODE, &c->nroot->flags)) {
88+
mutex_unlock(&c->lp_mutex);
8089
return 0;
90+
}
8191

8292
ubifs_assert(c, atomic_long_read(&c->dirty_zn_cnt) == 0);
8393
ubifs_assert(c, c->dirty_pn_cnt == 0);
8494
ubifs_assert(c, c->dirty_nn_cnt == 0);
95+
mutex_unlock(&c->lp_mutex);
8596

8697
return 1;
8798
}

0 commit comments

Comments
 (0)