Skip to content

Commit

Permalink
dm btree: fix order of block initialization in btree_split_beneath
Browse files Browse the repository at this point in the history
When btree_split_beneath() splits a node to two new children, it will
allocate two blocks: left and right.  If right block's allocation
failed, the left block will be unlocked and marked dirty.  If this
happened, the left block'ss content is zero, because it wasn't
initialized with the btree struct before the attempot to allocate the
right block.  Upon return, when flushing the left block to disk, the
validator will fail when check this block.  Then a BUG_ON is raised.

Fix this by completely initializing the left block before allocating and
initializing the right block.

Fixes: 4dcb8b5 ("dm btree: fix leak of bufio-backed block in btree_split_beneath error path")
Cc: stable@vger.kernel.org
Signed-off-by: ZhangXiaoxu <zhangxiaoxu5@huawei.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
  • Loading branch information
ZhangXiaoxu authored and Mike Snitzer committed Aug 22, 2019
1 parent dc1a3e8 commit e4f9d60
Showing 1 changed file with 16 additions and 15 deletions.
31 changes: 16 additions & 15 deletions drivers/md/persistent-data/dm-btree.c
Original file line number Diff line number Diff line change
Expand Up @@ -628,39 +628,40 @@ static int btree_split_beneath(struct shadow_spine *s, uint64_t key)

new_parent = shadow_current(s);

pn = dm_block_data(new_parent);
size = le32_to_cpu(pn->header.flags) & INTERNAL_NODE ?
sizeof(__le64) : s->info->value_type.size;

/* create & init the left block */
r = new_block(s->info, &left);
if (r < 0)
return r;

ln = dm_block_data(left);
nr_left = le32_to_cpu(pn->header.nr_entries) / 2;

ln->header.flags = pn->header.flags;
ln->header.nr_entries = cpu_to_le32(nr_left);
ln->header.max_entries = pn->header.max_entries;
ln->header.value_size = pn->header.value_size;
memcpy(ln->keys, pn->keys, nr_left * sizeof(pn->keys[0]));
memcpy(value_ptr(ln, 0), value_ptr(pn, 0), nr_left * size);

/* create & init the right block */
r = new_block(s->info, &right);
if (r < 0) {
unlock_block(s->info, left);
return r;
}

pn = dm_block_data(new_parent);
ln = dm_block_data(left);
rn = dm_block_data(right);

nr_left = le32_to_cpu(pn->header.nr_entries) / 2;
nr_right = le32_to_cpu(pn->header.nr_entries) - nr_left;

ln->header.flags = pn->header.flags;
ln->header.nr_entries = cpu_to_le32(nr_left);
ln->header.max_entries = pn->header.max_entries;
ln->header.value_size = pn->header.value_size;

rn->header.flags = pn->header.flags;
rn->header.nr_entries = cpu_to_le32(nr_right);
rn->header.max_entries = pn->header.max_entries;
rn->header.value_size = pn->header.value_size;

memcpy(ln->keys, pn->keys, nr_left * sizeof(pn->keys[0]));
memcpy(rn->keys, pn->keys + nr_left, nr_right * sizeof(pn->keys[0]));

size = le32_to_cpu(pn->header.flags) & INTERNAL_NODE ?
sizeof(__le64) : s->info->value_type.size;
memcpy(value_ptr(ln, 0), value_ptr(pn, 0), nr_left * size);
memcpy(value_ptr(rn, 0), value_ptr(pn, nr_left),
nr_right * size);

Expand Down

0 comments on commit e4f9d60

Please sign in to comment.