| From b6e58b5466b2959f83034bead2e2e1395cca8aeb Mon Sep 17 00:00:00 2001 |
| From: Hou Tao <houtao1@huawei.com> |
| Date: Thu, 17 Jun 2021 15:45:47 +0800 |
| Subject: dm btree remove: assign new_root only when removal succeeds |
| |
| From: Hou Tao <houtao1@huawei.com> |
| |
| commit b6e58b5466b2959f83034bead2e2e1395cca8aeb upstream. |
| |
| remove_raw() in dm_btree_remove() may fail due to IO read error |
| (e.g. read the content of origin block fails during shadowing), |
| and the value of shadow_spine::root is uninitialized, but |
| the uninitialized value is still assign to new_root in the |
| end of dm_btree_remove(). |
| |
| For dm-thin, the value of pmd->details_root or pmd->root will become |
| an uninitialized value, so if trying to read details_info tree again |
| out-of-bound memory may occur as showed below: |
| |
| general protection fault, probably for non-canonical address 0x3fdcb14c8d7520 |
| CPU: 4 PID: 515 Comm: dmsetup Not tainted 5.13.0-rc6 |
| Hardware name: QEMU Standard PC |
| RIP: 0010:metadata_ll_load_ie+0x14/0x30 |
| Call Trace: |
| sm_metadata_count_is_more_than_one+0xb9/0xe0 |
| dm_tm_shadow_block+0x52/0x1c0 |
| shadow_step+0x59/0xf0 |
| remove_raw+0xb2/0x170 |
| dm_btree_remove+0xf4/0x1c0 |
| dm_pool_delete_thin_device+0xc3/0x140 |
| pool_message+0x218/0x2b0 |
| target_message+0x251/0x290 |
| ctl_ioctl+0x1c4/0x4d0 |
| dm_ctl_ioctl+0xe/0x20 |
| __x64_sys_ioctl+0x7b/0xb0 |
| do_syscall_64+0x40/0xb0 |
| entry_SYSCALL_64_after_hwframe+0x44/0xae |
| |
| Fixing it by only assign new_root when removal succeeds |
| |
| Signed-off-by: Hou Tao <houtao1@huawei.com> |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Mike Snitzer <snitzer@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/md/persistent-data/dm-btree-remove.c | 3 ++- |
| 1 file changed, 2 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/md/persistent-data/dm-btree-remove.c |
| +++ b/drivers/md/persistent-data/dm-btree-remove.c |
| @@ -549,7 +549,8 @@ int dm_btree_remove(struct dm_btree_info |
| delete_at(n, index); |
| } |
| |
| - *new_root = shadow_root(&spine); |
| + if (!r) |
| + *new_root = shadow_root(&spine); |
| exit_shadow_spine(&spine); |
| |
| return r; |