Linux-mtd Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "James Hilliard" <james.hilliard1@gmail.com>,
	"Miquel Raynal" <miquel.raynal@bootlin.com>,
	"Richard Weinberger" <richard@nod.at>,
	"Vignesh Raghavendra" <vigneshr@ti.com>
Cc: linux-mtd@lists.infradead.org,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	"Srinivas Kandagatla" <srini@kernel.org>,
	"Tze Yee Ng" <tze.yee.ng@altera.com>
Subject: Re: [PATCH v4 03/58] mtd: separate class devices from retained backing objects
Date: Fri, 13 Nov 2026 19:16:47 +0000	[thread overview]
Message-ID: <sashiko-outbox-213533@kernel.org> (raw)
In-Reply-To: <20261113-submit-rawnand-retained-handle-lifetime-v1-v4-3-556b1d7c6221@gmail.com>

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [High] mtd: out-of-bounds access in gluebi_get_device() via
  add_mtd_partitions()
- [Medium] mtd: concurrent child removal in add_mtd_partitions() deletes
  all partitions
- [Medium] mtd: mtd_device_parse_register() and unregister check
  device_is_registered()
--

commit 6ac4170246a3c7e1422d462f2c77f494c0bb8489
Author: James Hilliard <james.hilliard1@gmail.com>

mtd: separate class devices from retained backing objects

This commit allocates MTD class devices separately from struct mtd_info,
moves the MTD class and device release handling into a built-in helper,
and adds reference counting for backing master and partition metadata.

> diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
> index 620326279957f..dd404d36156ea 100644
> --- a/drivers/mtd/mtdcore.c
> +++ b/drivers/mtd/mtdcore.c
[ ... ]
> @@ -1150,7 +1200,7 @@ 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 (!mtd->dev || !device_is_registered(mtd->dev))
>  		ret = add_mtd_device(mtd);
>  	else
>  		ret = 0;
> @@ -1184,7 +1234,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 && mtd->dev && device_is_registered(mtd->dev)) {
>  		err = del_mtd_device(mtd);
>  		if (err)
>  			pr_err("Error when deleting MTD device (%d)\n", err);
> @@ -1225,7 +1275,7 @@ int mtd_device_unregister(struct mtd_info *master)
>  	if (err)
>  		return err;
>  
> -	if (!device_is_registered(&master->dev))
> +	if (!master->dev || !device_is_registered(master->dev))
>  		return 0;
>  
>  	return del_mtd_device(master);

[Severity: Medium]
Can checking device_is_registered(master->dev) instead of
master->registered cause mtd_device_unregister() to call del_mtd_device()
a second time and fail with -ENODEV?

With CONFIG_MTD_PARTITIONED_MASTER=y, mtd_device_parse_register() calls
add_mtd_device(mtd) before parsing and registering partitions.  If
userspace (such as udevd on KOBJ_ADD) or an in-kernel consumer acquires a
handle on the master during that window and partition registration then
fails (for example with -EPROBE_DEFER), del_mtd_device(mtd) clears
mtd->registered and drops the initial refcnt without running
mtd_device_release().  Because mtd->dev remains registered until the
handle is put, a driver error path that calls mtd_device_unregister()
(such as physmap_flash_probe() -> physmap_flash_remove()) sees
device_is_registered(master->dev) == true and calls del_mtd_device(master)
again, returning -ENODEV.  Wait-free unmanaged callers also return from
mtd_device_parse_register() with mtd->dev drvdata and mtd->nvmem still
attached to the mtd_info they are about to free, and a deferred probe
retry hits WARN_ONCE(mtd->dev) in add_mtd_device().

[ ... ]

> diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
> index c316cfe92c7e4..58d1343476912 100644
> --- a/drivers/mtd/mtdpart.c
> +++ b/drivers/mtd/mtdpart.c
[ ... ]
> @@ -419,35 +420,47 @@ int add_mtd_partitions(struct mtd_info *parent,
>  	       nbparts, parent->name);
>  
>  	for (i = 0; i < nbparts; i++) {
> +		mutex_lock(&master->master.partitions_lock);
> +		if (master->master.unregistering ||
> +		    (mtd_is_partition(parent) && !parent->registered)) {
> +			ret = -ENODEV;
> +			goto err_unlock;
> +		}
> +
>  		child = allocate_partition(parent, parts + i, i, cur_offset);
>  		if (IS_ERR(child)) {
>  			ret = PTR_ERR(child);
> -			goto err_del_partitions;
> +			goto err_unlock;
>  		}
>  
>  		if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) {
> -			if (mtd_virt_concat_add(child))
> +			if (mtd_virt_concat_add(child)) {
> +				mutex_unlock(&master->master.partitions_lock);
>  				continue;
> +			}
>  		}
>  
> -		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) {
> -			mutex_lock(&master->master.partitions_lock);
>  			list_del(&child->part.node);
> -			mutex_unlock(&master->master.partitions_lock);
> -
>  			free_partition(child);
> -			goto err_del_partitions;
> +			goto err_unlock;
>  		}
>  
>  		mtd_add_partition_attrs(child);
> +		cur_offset = child->part.offset + child->part.size;
> +
> +		/* Parsing drops the partition lock and may race with deletion. */
> +		ret = PTR_ERR_OR_ZERO(get_mtd_device(child, -1));
> +		if (ret)
> +			goto err_unlock;
> +		mutex_unlock(&master->master.partitions_lock);
>  
>  		/* Look for subpartitions (skip if no maching parser found) */
>  		ret = parse_mtd_partitions(child, parts[i].types, NULL);
> +		put_mtd_device(child);

[Severity: High]
Does calling get_mtd_device(child, -1) on every new partition cause an
out-of-bounds access in gluebi_get_device() when a gluebi MTD device is
partitioned?

__get_mtd_device(child) passes child to master->_get_device(mtd), whereas
__put_mtd_device(child) passes master to master->_put_device(master).
gluebi_get_device() applies container_of() directly to mtd without calling
mtd_get_master(mtd):

drivers/mtd/ubi/gluebi.c:gluebi_get_device() {
    ...
	gluebi = container_of(mtd, struct gluebi_device, mtd);
	mutex_lock(&devices_mutex);
	if (gluebi->refcnt > 0) {
		gluebi->refcnt += 1;
    ...
}

Because child is allocated by allocate_partition() as a standalone struct
mtd_info rather than embedded in struct gluebi_device, gluebi_get_device()
reads and writes past the end of child, and the paired put_mtd_device(child)
then decrements the master gluebi->refcnt in gluebi_put_device(master).

>  		if (ret < 0 && ret == -ENOENT) {
>  			pr_debug("Skip parsing subpartitions: %d\n", ret);
>  			continue;
> @@ -455,12 +468,12 @@ int add_mtd_partitions(struct mtd_info *parent,
>  			pr_err("Failed to parse subpartitions: %d\n", ret);
>  			goto err_del_partitions;
>  		}
> -
> -		cur_offset = child->part.offset + child->part.size;
>  	}
>  
>  	return 0;
>  
> +err_unlock:
> +	mutex_unlock(&master->master.partitions_lock);
>  err_del_partitions:
>  	del_mtd_partitions(master);

[Severity: Medium]
If child is removed via BLKPG_DEL_PARTITION while partitions_lock is
dropped for parse_mtd_partitions(child, ...), does jumping to err_unlock
destroy all partitions on the root master device?

When a subpartition parser calls add_mtd_partitions(child, ...) after
child->registered has been cleared by mtd_del_partition(), the
!parent->registered check sets ret = -ENODEV and jumps to err_unlock.
That falls through to del_mtd_partitions(master) on the top-level master
instead of just skipping subpartitions for the removed child, and returns
-ENODEV to the outer add_mtd_partitions() loop (which only ignores
-ENOENT), failing the entire master registration.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-submit-rawnand-retained-handle-lifetime-v1-v4-0-556b1d7c6221@gmail.com?part=3

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

  reply	other threads:[~2026-11-13 19:16 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-13 18:14 [PATCH v4 00/58] mtd: rawnand: retain chip and controller state across removal James Hilliard
2026-11-13 18:14 ` [PATCH v4 01/58] nvmem: disconnect provider callbacks before unregister returns James Hilliard
2026-11-13 18:14 ` [PATCH v4 02/58] mtd: acquire an MTD handle from a referenced class device James Hilliard
2026-11-13 18:14 ` [PATCH v4 03/58] mtd: separate class devices from retained backing objects James Hilliard
2026-11-13 19:16   ` sashiko-bot [this message]
2026-11-13 18:14 ` [PATCH v4 04/58] mtd: retain virtual concatenations across component removal James Hilliard
2026-11-13 18:14 ` [PATCH v4 05/58] mtd: rawnand: separate hardware disconnection from cleanup James Hilliard
2026-11-13 18:14 ` [PATCH v4 06/58] mtd: rawnand: share retained chip and controller ownership James Hilliard
2026-11-13 18:14 ` [PATCH v4 07/58] mtd: rawnand: sunxi: retain chip state until the final MTD put James Hilliard
2026-11-13 18:14 ` [PATCH v4 08/58] mtd: rawnand: plat_nand: retain open-handle state on removal James Hilliard
2026-11-13 18:14 ` [PATCH v4 09/58] mtd: rawnand: gpio: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 10/58] mtd: rawnand: stm32_fmc2: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 11/58] mtd: rawnand: denali: retain chips across controller removal James Hilliard
2026-11-13 18:14 ` [PATCH v4 12/58] mtd: rawnand: arasan: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 13/58] mtd: rawnand: mxic: retain open-handle state on removal James Hilliard
2026-11-13 18:14 ` [PATCH v4 14/58] mtd: rawnand: intel: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 15/58] mtd: rawnand: tegra: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 16/58] mtd: rawnand: mtk: retain chips across controller removal James Hilliard
2026-11-13 18:14 ` [PATCH v4 17/58] mtd: rawnand: rockchip: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 18/58] mtd: rawnand: atmel: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 19/58] mtd: rawnand: orion: retain open-handle state on removal James Hilliard
2026-11-13 18:14 ` [PATCH v4 20/58] mtd: rawnand: socrates: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 21/58] mtd: rawnand: fsl_upm: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 22/58] mtd: rawnand: pasemi: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 23/58] mtd: rawnand: ams-delta: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 24/58] mtd: rawnand: xway: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 25/58] mtd: rawnand: hisi504: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 26/58] mtd: rawnand: vf610: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 27/58] mtd: rawnand: technologic: " James Hilliard
2026-11-13 18:39   ` sashiko-bot
2026-11-13 18:14 ` [PATCH v4 28/58] mtd: rawnand: loongson: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 29/58] mtd: rawnand: ma35d1: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 30/58] mtd: rawnand: pl35x: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 31/58] mtd: rawnand: mxc: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 32/58] mtd: rawnand: fsmc: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 33/58] mtd: rawnand: gpmi: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 34/58] mtd: rawnand: omap2: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 35/58] mtd: rawnand: marvell: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 36/58] mtd: rawnand: renesas: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 37/58] mtd: rawnand: meson: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 38/58] mtd: rawnand: cadence: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 39/58] mtd: rawnand: qcom: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 40/58] mtd: rawnand: cafe: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 41/58] mtd: rawnand: brcmnand: " James Hilliard
2026-11-13 18:14 ` [PATCH v4 42/58] mtd: rawnand: au1550nd: retain chip state across removal James Hilliard
2026-11-13 18:14 ` [PATCH v4 43/58] mtd: rawnand: sharpsl: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 44/58] mtd: rawnand: davinci: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 45/58] mtd: rawnand: sh_flctl: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 46/58] mtd: rawnand: lpc32xx_slc: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 47/58] mtd: rawnand: lpc32xx_mlc: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 48/58] mtd: rawnand: ndfc: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 49/58] mtd: rawnand: txx9ndfmc: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 50/58] mtd: rawnand: ingenic: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 51/58] mtd: rawnand: mpc5121: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 52/58] mtd: rawnand: bcm47xxnflash: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 53/58] mtd: rawnand: fsl_elbc: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 54/58] mtd: rawnand: fsl_ifc: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 55/58] mtd: rawnand: r852: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 56/58] mtd: rawnand: nandsim: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 57/58] mtd: rawnand: cs553x: " James Hilliard
2026-11-13 18:15 ` [PATCH v4 58/58] mtd: rawnand: diskonchip: " James Hilliard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-213533@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=james.hilliard1@gmail.com \
    --cc=linux-mtd@lists.infradead.org \
    --cc=miquel.raynal@bootlin.com \
    --cc=richard@nod.at \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=srini@kernel.org \
    --cc=tze.yee.ng@altera.com \
    --cc=vigneshr@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox