mtd_add_partition() drops partitions_lock after linking the new child but before registering its MTD device and adding the partition sysfs attributes. Concurrent removal can unlink that child before it has been registered, or release it after registration but before the attributes are added. It can also finish deleting partitions before a new child is linked, leaving a partition published after master removal. Keep partitions_lock held across allocation, publication and attribute creation. Record that master unregistration has started under the same lock and reject new dynamic partitions from that point. Check before allocation, which can scan the parent's bad-block markers, so it cannot start hardware access after removal has passed this synchronization point. Initialize the state when registering the master, allowing drivers that reuse their MTD object to register it again. Fixes: 5daa7b21496a ("mtd: prepare partition add and del functions for ioctl requests") Cc: stable@vger.kernel.org Signed-off-by: James Hilliard --- drivers/mtd/mtdcore.c | 5 +++++ drivers/mtd/mtdpart.c | 20 +++++++++++++------- include/linux/mtd/mtd.h | 2 ++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 491a27d0315f..7a5205810bf0 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -918,6 +918,7 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd) INIT_LIST_HEAD(&mtd->partitions); mutex_init(&mtd->master.partitions_lock); mutex_init(&mtd->master.chrdev_lock); + mtd->master.unregistering = false; } static ssize_t mtd_otp_size(struct mtd_info *mtd, bool is_user) @@ -1206,6 +1207,10 @@ int mtd_device_unregister(struct mtd_info *master) { int err; + mutex_lock(&master->master.partitions_lock); + master->master.unregistering = true; + mutex_unlock(&master->master.partitions_lock); + if (master->_reboot) { unregister_reboot_notifier(&master->reboot_notifier); memset(&master->reboot_notifier, 0, sizeof(master->reboot_notifier)); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index ddded0dbe77e..c316cfe92c7e 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -278,28 +278,34 @@ int mtd_add_partition(struct mtd_info *parent, const char *name, part.size = length; part.offset = offset; + mutex_lock(&master->master.partitions_lock); + if (master->master.unregistering) { + ret = -ENODEV; + goto err_unlock; + } + child = allocate_partition(parent, &part, -1, offset); - if (IS_ERR(child)) - return PTR_ERR(child); + if (IS_ERR(child)) { + ret = PTR_ERR(child); + goto err_unlock; + } - mutex_lock(&master->master.partitions_lock); list_add_tail(&child->part.node, &parent->partitions); - mutex_unlock(&master->master.partitions_lock); ret = add_mtd_device(child); if (ret) goto err_remove_part; mtd_add_partition_attrs(child); + mutex_unlock(&master->master.partitions_lock); return 0; err_remove_part: - mutex_lock(&master->master.partitions_lock); list_del(&child->part.node); - mutex_unlock(&master->master.partitions_lock); - free_partition(child); +err_unlock: + mutex_unlock(&master->master.partitions_lock); return ret; } diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 8d10d9d2e830..cd7b4671c456 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -224,6 +224,7 @@ struct mtd_part { * not only the master partition list, but also all * sub-partitions. * @suspended: set to 1 when the device is suspended, 0 otherwise + * @unregistering: removal has started; protected by partitions_lock * * This struct is embedded in mtd_info and contains master-specific * properties/fields. The master is the root MTD device from the MTD partition @@ -233,6 +234,7 @@ struct mtd_master { struct mutex partitions_lock; struct mutex chrdev_lock; unsigned int suspended : 1; + bool unregistering; }; struct mtd_info { --- base-commit: 1c1a342aceec79528b3ba51f376eca2be5928fe7 change-id: 20261108-submit-mtd-partition-publication-v1-4629be94d7e6 Best regards, -- James Hilliard