[PATCH] fix: mtd: mtd_device_parse_register: fix refcount imbalance on add_mtd_device failure and multi-call scenarios

From: WenTao Liang

Date: Fri Jun 26 2026 - 11:26:48 EST


add_mtd_device initializes mtd->refcnt via kref_init unconditionally. The
cleanup in the out label only calls del_mtd_device when
device_is_registered is true. When add_mtd_device fails before
device_register, the refcnt is left at 1 (leak). Conversely, when
CONFIG_MTD_PARTITIONED_MASTER is disabled and the device was already
registered by a prior call, the error path incorrectly calls
del_mtd_device without a matching add_mtd_device in this invocation,
causing a refcount underflow.

Track whether add_mtd_device succeeded in this invocation with a
registered flag, and only call del_mtd_device on error when registered is
true.

Cc: stable@xxxxxxxxxxxxxxx
Fixes: 1c4c215cbdcb ("mtd: add new API for handling MTD registration")
Signed-off-by: WenTao Liang <vulab@xxxxxxxxxxx>
---
drivers/mtd/mtdcore.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 576537774628..59d8a6c61f55 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -1108,6 +1108,7 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
int nr_parts)
{
int ret, err;
+ bool registered = false;

mtd_set_dev_defaults(mtd);

@@ -1119,6 +1120,7 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
ret = add_mtd_device(mtd);
if (ret)
goto out;
+ registered = true;
}

if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) {
@@ -1136,9 +1138,11 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
ret = 0;
else if (nr_parts)
ret = add_mtd_partitions(mtd, parts, nr_parts);
- else if (!device_is_registered(&mtd->dev))
+ else if (!device_is_registered(&mtd->dev)) {
ret = add_mtd_device(mtd);
- else
+ if (!ret)
+ registered = true;
+ } else
ret = 0;

if (ret)
@@ -1170,7 +1174,7 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
nvmem_unregister(mtd->otp_factory_nvmem);
}

- if (ret && device_is_registered(&mtd->dev)) {
+ if (ret && registered) {
err = del_mtd_device(mtd);
if (err)
pr_err("Error when deleting MTD device (%d)\n", err);
--
2.39.5 (Apple Git-154)