[PATCH OLK-5.10] dm-thin: fix metadata refcount underflow
From: Mikulas Patocka <mpatocka@redhat.com> stable inclusion from stable-v6.6.140 commit 12161e03d33afce781f68fa11cc6060538862fad category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15360 CVE: CVE-2026-46107 Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/... -------------------------------- commit 09a65adc7d8bbfce06392cb6d375468e2728ead5 upstream. There's a bug in dm-thin in the function rebalance_children. If the internal btree node has one entry, the code tries to copy all btree entries from the node's child to the node itself and then decrement the child's reference count. If the child node is shared (it has reference count > 1), we won't free it, so there would be two pointers to each of the grandchildren nodes. But the reference counts of the grandchildren is not increased, thus the reference count doesn't match the number of pointers that point to the grandchildren. This results in "device mapper: space map common: unable to decrement block" errors. Fix this bug by incrementing reference counts on the grandchildren if the btree node is shared. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Fixes: 3241b1d3e0aa ("dm: add persistent data library") Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Conflicts: drivers/md/persistent-data/dm-transaction-manager.c drivers/md/persistent-data/dm-transaction-manager.h [This kernel version does not have dm_tm_block_is_shared() function as commit 4eafdb1515a7 ("dm btree: improve btree residency") not merged. So the functionality of this function was implemented manually.] Signed-off-by: Zizhi Wo <wozizhi@huawei.com> --- drivers/md/persistent-data/dm-btree-remove.c | 8 ++++++++ drivers/md/persistent-data/dm-transaction-manager.c | 9 +++++++++ drivers/md/persistent-data/dm-transaction-manager.h | 7 +++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c index 63f2baed3c8a..1cf31861020b 100644 --- a/drivers/md/persistent-data/dm-btree-remove.c +++ b/drivers/md/persistent-data/dm-btree-remove.c @@ -413,16 +413,24 @@ static int rebalance_children(struct shadow_spine *s, n = dm_block_data(shadow_current(s)); if (le32_to_cpu(n->header.nr_entries) == 1) { struct dm_block *child; + int is_shared; dm_block_t b = value64(n, 0); + r = dm_tm_block_is_shared(info->tm, b, &is_shared); + if (r) + return r; + r = dm_tm_read_lock(info->tm, b, &btree_node_validator, &child); if (r) return r; + if (is_shared) + inc_children(info->tm, dm_block_data(child), vt); + memcpy(n, dm_block_data(child), dm_bm_block_size(dm_tm_get_bm(info->tm))); dm_tm_dec(info->tm, dm_block_location(child)); dm_tm_unlock(info->tm, child); diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c index 73f57977b2e3..eda9d7a02008 100644 --- a/drivers/md/persistent-data/dm-transaction-manager.c +++ b/drivers/md/persistent-data/dm-transaction-manager.c @@ -380,10 +380,19 @@ int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b, return -EWOULDBLOCK; return dm_sm_get_count(tm->sm, b, result); } +int dm_tm_block_is_shared(struct dm_transaction_manager *tm, dm_block_t b, + int *result) +{ + if (tm->is_clone) + return -EWOULDBLOCK; + + return dm_sm_count_is_more_than_one(tm->sm, b, result); +} + struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm) { return tm->bm; } diff --git a/drivers/md/persistent-data/dm-transaction-manager.h b/drivers/md/persistent-data/dm-transaction-manager.h index f3a18be68f30..dbc6e4b13b06 100644 --- a/drivers/md/persistent-data/dm-transaction-manager.h +++ b/drivers/md/persistent-data/dm-transaction-manager.h @@ -104,10 +104,17 @@ void dm_tm_inc(struct dm_transaction_manager *tm, dm_block_t b); void dm_tm_dec(struct dm_transaction_manager *tm, dm_block_t b); int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b, uint32_t *result); +/* + * Finds out if a given block is shared (ie. has a reference count higher + * than one). + */ +int dm_tm_block_is_shared(struct dm_transaction_manager *tm, dm_block_t b, + int *result); + struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm); /* * If you're using a non-blocking clone the tm will build up a list of * requested blocks that weren't in core. This call will request those -- 2.52.0
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,转换为PR失败! 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/DO7... 失败原因:调用atomgit api创建PR失败, 失败原因如下: Backend timeout 建议解决方法:请稍等,机器人会在下一次任务重新执行 FeedBack: The patch(es) which you have sent to kernel@openeuler.org has been converted to PR failed! Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/DO7... Failed Reason: create PR failed when call atomgit's api, failed reason is as follows: Backend timeout Suggest Solution: please wait, the bot will retry in the next interval
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/23534 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/DO7... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/23534 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/DO7...
participants (2)
-
patchwork bot -
Zizhi Wo