Commit 13a09ade authored by Zhiqiang Liu's avatar Zhiqiang Liu Committed by Yang Yingliang
Browse files

bcache: fix potential deadlock problem in btree_gc_coalesce



hulk inclusion
category: bugfix
bugzilla: 13690
CVE: CVE-2020-12771

---------------------------

coccicheck reports:
  drivers/md//bcache/btree.c:1538:1-7: preceding lock on line 1417

btree_gc_coalesce func is designed to coalesce two adjacent nodes in
new_nodes[GC_MERGE_NODES] and finally release one node. All nodes`write_lock,
new_nodes[i]->write_lock, are holded before coalescing adjacent nodes,
and them will be released after coalescing successfully.

However, if the coalescing process fails, such as no enough space of new_nodes[1]
to fit all of the remaining keys in new_nodes[0] and realloc keylist failed, we
will goto to out_nocoalesce tag directly without releasing new_nodes[i]->write_lock.
Then, a deadlock will occur after calling btree_node_free to free new_nodes[i],
which also try to acquire new_nodes[i]->write_lock.

Here, we add a new tag 'out_unlock_nocoalesce' before out_nocoalesce tag to release
new_nodes[i]->write_lock when coalescing process fails.

Fixes: 2a285686 ("bcache: btree locking rework")
Signed-off-by: default avatarZhiqiang Liu <liuzhiqiang26@huawei.com>
Signed-off-by: default avatarYang Yingliang <yangyingliang@huawei.com>
Reviewed-by: default avatarJason Yan <yanaijie@huawei.com>
Signed-off-by: default avatarYang Yingliang <yangyingliang@huawei.com>
parent 97fd0dd5
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -1432,7 +1432,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op,
			if (__set_blocks(n1, n1->keys + n2->keys,
					 block_bytes(b->c)) >
			    btree_blocks(new_nodes[i]))
				goto out_nocoalesce;
				goto out_unlock_nocoalesce;

			keys = n2->keys;
			/* Take the key of the node we're getting rid of */
@@ -1461,7 +1461,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op,

		if (__bch_keylist_realloc(&keylist,
					  bkey_u64s(&new_nodes[i]->key)))
			goto out_nocoalesce;
			goto out_unlock_nocoalesce;

		bch_btree_node_write(new_nodes[i], &cl);
		bch_keylist_add(&keylist, &new_nodes[i]->key);
@@ -1507,6 +1507,10 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op,
	/* Invalidated our iterator */
	return -EINTR;

out_unlock_nocoalesce:
	for (i = 0; i < nodes; i++)
		mutex_unlock(&new_nodes[i]->write_lock);

out_nocoalesce:
	closure_sync(&cl);
	bch_keylist_free(&keylist);