Among the most important fixes that we have here, there are:

- the revert of the uclinux map driver which was presumably no longer
   used but in fact was
 - the use of SPI match data to get chip capabilities in the mchp23k256
   driver
 - several fixes addressing the newly introduced virt-concat support
 - a missing build dependency on ndfc
 
 There is as well the usual load (if not actually bigger than ususal) of
 uninitialized variables, leaks, double free, and AI fuzzed issues being
 fixed.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmpaTeQACgkQJWrqGEe9
 VoQ0/Af7BpZ9gxFZrgiXBdQ/XcVDfsrP7YP/3D3SLlB7ynwPHrq0YvMdBs8CCp+b
 YbTDqBrt0+T1YeZ4igWu50bGSjAz0mLztE2r09/uwrG9YO+B0s98jXRFEqnaOMMf
 2ghhrIKr2RB9OPJaigmtdzQij1UAjCIFpWxEz4XLytXQ5oxnzP2j50JyeVxifb1R
 3KDw0KBibngn3I5MUQ4OHCg4+6Krf0rtjo8yZWB6LKgVlCcy3eEbYGZMn3YI3T6T
 xsOa8KxkBLf2BeAFu2nDp0e/5vEfaVk9fCnRElBDz3kEdJ6LR6k45mLpqfB+U9HN
 DhWZswONu0y54xHAmyauVWyYBK/DHg==
 =SD+E
 -----END PGP SIGNATURE-----

Merge tag 'mtd/fixes-for-7.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux

Pull mtd fixes from Miquel Raynal:
 "Among the most important fixes that we have here, there are:

   - the revert of the uclinux map driver which was presumed to
     be no longer used but in fact was

   - the use of SPI match data to get chip capabilities in the
     mchp23k256 driver

   - several fixes addressing the newly introduced virt-concat
     support

   - a missing build dependency on ndfc

  as well as the usual load (if not actually bigger than usual) of
  uninitialized variables, leaks, double free, and AI fuzzed issues
  being fixed"

* tag 'mtd/fixes-for-7.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux:
  Revert "mtd: maps: remove uclinux map driver"
  mtd: onenand: samsung: report DMA completion timeouts
  mtd: rawnand: fsl_ifc: return errors for failed page reads
  mtd: mchp23k256: use SPI match data for chip caps
  mtd: rawnand: lpc32xx_slc: fail DMA transfer on completion timeout
  mtd: rawnand: lpc32xx_mlc: fail DMA transfers on timeout
  mtd: fix double free and WARN_ON in add_mtd_device() error paths
  mtd: virt-concat: free duplicate generated name
  mtd: nand: mtk-ecc: stop on ECC idle timeouts
  mtd: mtdswap: remove debugfs stats file on teardown
  mtd: mtdpart: validate partition bounds in mtd_add_partition()
  mtd: mtdpart: fix uninitialized erasesize on MTDPART_OFS_RETAIN error path
  mtd: rawnand: ndfc: add CONFIG_OF dependency
  mtd: spinand: initialize ret in regular page reads
  mtd: virt_concat: fix use-after-free in mtd_virt_concat_destroy()
  mtd: rawnand: ingenic: handle ECC clock enable failures
  mtd: nand: ecc-mtk: handle ECC clock enable failures
  mtd: virt_concat: fix use-after-free in mtd_virt_concat_destroy_joins()
  mtd: rawnand: ndfc: fix gcc uninitialized var
This commit is contained in:
Linus Torvalds 2026-07-17 09:30:17 -07:00
commit af5e34a41c
17 changed files with 224 additions and 23 deletions

View File

@ -188,7 +188,7 @@ static int mchp23k256_probe(struct spi_device *spi)
data = dev_get_platdata(&spi->dev);
flash->caps = of_device_get_match_data(&spi->dev);
flash->caps = spi_get_device_match_data(spi);
if (!flash->caps)
flash->caps = &mchp23k256_caps;

View File

@ -277,6 +277,12 @@ config MTD_PCMCIA_ANONYMOUS
If unsure, say N.
config MTD_UCLINUX
bool "Generic uClinux RAM/ROM filesystem support"
depends on (MTD_RAM=y || MTD_ROM=y) && (!MMU || COLDFIRE)
help
Map driver to support image based filesystems for uClinux.
config MTD_PLATRAM
tristate "Map driver for platform device RAM (mtd-ram)"
select MTD_RAM

View File

@ -30,6 +30,7 @@ obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
obj-$(CONFIG_MTD_PCI) += pci.o
obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o

118
drivers/mtd/maps/uclinux.c Normal file
View File

@ -0,0 +1,118 @@
/****************************************************************************/
/*
* uclinux.c -- generic memory mapped MTD driver for uclinux
*
* (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
*
* License: GPL
*/
/****************************************************************************/
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/major.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/sections.h>
/****************************************************************************/
#ifdef CONFIG_MTD_ROM
#define MAP_NAME "rom"
#else
#define MAP_NAME "ram"
#endif
static struct map_info uclinux_ram_map = {
.name = MAP_NAME,
.size = 0,
};
static unsigned long physaddr = -1;
module_param(physaddr, ulong, S_IRUGO);
static struct mtd_info *uclinux_ram_mtdinfo;
/****************************************************************************/
static const struct mtd_partition uclinux_romfs[] = {
{ .name = "ROMfs" }
};
#define NUM_PARTITIONS ARRAY_SIZE(uclinux_romfs)
/****************************************************************************/
static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys)
{
struct map_info *map = mtd->priv;
*virt = map->virt + from;
if (phys)
*phys = map->phys + from;
*retlen = len;
return(0);
}
/****************************************************************************/
static int __init uclinux_mtd_init(void)
{
struct mtd_info *mtd;
struct map_info *mapp;
mapp = &uclinux_ram_map;
if (physaddr == -1)
mapp->phys = (resource_size_t)__bss_stop;
else
mapp->phys = physaddr;
if (!mapp->size)
mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8))));
mapp->bankwidth = 4;
printk("uclinux[mtd]: probe address=0x%x size=0x%x\n",
(int) mapp->phys, (int) mapp->size);
/*
* The filesystem is guaranteed to be in direct mapped memory. It is
* directly following the kernels own bss region. Following the same
* mechanism used by architectures setting up traditional initrds we
* use phys_to_virt to get the virtual address of its start.
*/
mapp->virt = phys_to_virt(mapp->phys);
if (mapp->virt == 0) {
printk("uclinux[mtd]: no virtual mapping?\n");
return(-EIO);
}
simple_map_init(mapp);
mtd = do_map_probe("map_" MAP_NAME, mapp);
if (!mtd) {
printk("uclinux[mtd]: failed to find a mapping?\n");
return(-ENXIO);
}
mtd->owner = THIS_MODULE;
mtd->_point = uclinux_point;
mtd->priv = mapp;
uclinux_ram_mtdinfo = mtd;
mtd_device_register(mtd, uclinux_romfs, NUM_PARTITIONS);
return(0);
}
device_initcall(uclinux_mtd_init);
/****************************************************************************/

View File

@ -75,8 +75,8 @@ void mtd_virt_concat_destroy_joins(void)
if (item->concat) {
mtd_device_unregister(mtd);
kfree(mtd->name);
mtd_concat_destroy(mtd);
mtd_virt_concat_put_mtd_devices(item->concat);
mtd_concat_destroy(mtd);
}
}
}
@ -126,8 +126,8 @@ int mtd_virt_concat_destroy(struct mtd_info *mtd)
if (concat->mtd.name) {
del_mtd_device(&concat->mtd);
kfree(concat->mtd.name);
mtd_concat_destroy(&concat->mtd);
mtd_virt_concat_put_mtd_devices(item->concat);
mtd_concat_destroy(&concat->mtd);
}
for (idx = 0; idx < item->count; idx++)
@ -321,8 +321,10 @@ int mtd_virt_concat_create_join(void)
if (concat->mtd.name) {
ret = memcmp(concat->mtd.name, name, name_sz);
if (ret == 0)
if (ret == 0) {
kfree(name);
continue;
}
}
mtd = mtd_concat_create(concat->subdev, concat->num_subdev, name);
if (!mtd) {

View File

@ -105,6 +105,15 @@ static void mtd_release(struct device *dev)
device_destroy(&mtd_class, index + 1);
}
/*
* No-op device release used in add_mtd_device() error paths.
* Prevents mtd_release() from being called via device_release(),
* which would free the mtd_info that the caller still manages.
*/
static void mtd_dev_release_nop(struct device *dev)
{
}
static void mtd_device_release(struct kref *kref)
{
struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt);
@ -799,10 +808,8 @@ int add_mtd_device(struct mtd_info *mtd)
mtd_check_of_node(mtd);
of_node_get(mtd_get_of_node(mtd));
error = device_register(&mtd->dev);
if (error) {
put_device(&mtd->dev);
if (error)
goto fail_added;
}
/* Add the nvmem provider */
error = mtd_nvmem_add(mtd);
@ -840,8 +847,16 @@ int add_mtd_device(struct mtd_info *mtd)
return 0;
fail_nvmem_add:
device_unregister(&mtd->dev);
device_del(&mtd->dev);
fail_added:
/*
* Clear type and set nop release to prevent mtd_release() ->
* release_mtd_partition() -> free_partition() from freeing mtd.
* The caller handles cleanup on failure.
*/
mtd->dev.type = NULL;
mtd->dev.release = mtd_dev_release_nop;
put_device(&mtd->dev);
of_node_put(mtd_get_of_node(mtd));
fail_devname:
idr_remove(&mtd_idr, i);

View File

@ -118,6 +118,9 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent,
part->name, parent_size - child->part.offset,
child->part.size);
/* register to preserve ordering */
child->part.offset = 0;
child->part.size = 0;
child->erasesize = parent->erasesize;
goto out_register;
}
}
@ -264,6 +267,11 @@ int mtd_add_partition(struct mtd_info *parent, const char *name,
if (length <= 0)
return -EINVAL;
if (offset < 0 || offset >= (long long)parent_size)
return -EINVAL;
if ((u64)offset + (u64)length > parent_size)
return -EINVAL;
memset(&part, 0, sizeof(part));
part.name = name;
part.size = length;

View File

@ -125,6 +125,7 @@ struct mtdswap_dev {
char *page_buf;
char *oob_buf;
struct dentry *debugfs_stats;
};
struct mtdswap_oobdata {
@ -1262,7 +1263,8 @@ static int mtdswap_add_debugfs(struct mtdswap_dev *d)
if (IS_ERR_OR_NULL(root))
return -1;
debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, &mtdswap_fops);
d->debugfs_stats = debugfs_create_file("mtdswap_stats", 0400, root,
d, &mtdswap_fops);
return 0;
}
@ -1463,6 +1465,7 @@ static void mtdswap_remove_dev(struct mtd_blktrans_dev *dev)
{
struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev);
debugfs_remove(d->debugfs_stats);
del_mtd_blktrans_dev(dev);
mtdswap_cleanup(d);
kfree(d);

View File

@ -123,8 +123,8 @@ static int mt7622_ecc_regs[] = {
[ECC_DECIRQ_STA] = 0x144,
};
static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
enum mtk_ecc_operation op)
static inline int mtk_ecc_wait_idle(struct mtk_ecc *ecc,
enum mtk_ecc_operation op)
{
struct device *dev = ecc->dev;
u32 val;
@ -136,6 +136,8 @@ static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
if (ret)
dev_warn(dev, "%s NOT idle\n",
op == ECC_ENCODE ? "encoder" : "decoder");
return ret;
}
static irqreturn_t mtk_ecc_irq(int irq, void *id)
@ -265,6 +267,7 @@ static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
{
struct platform_device *pdev;
struct mtk_ecc *ecc;
int ret;
pdev = of_find_device_by_node(np);
if (!pdev)
@ -276,7 +279,12 @@ static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
return ERR_PTR(-EPROBE_DEFER);
}
clk_prepare_enable(ecc->clk);
ret = clk_prepare_enable(ecc->clk);
if (ret) {
put_device(&pdev->dev);
return ERR_PTR(ret);
}
mtk_ecc_hw_init(ecc);
return ecc;
@ -312,7 +320,11 @@ int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
return ret;
}
mtk_ecc_wait_idle(ecc, op);
ret = mtk_ecc_wait_idle(ecc, op);
if (ret) {
mutex_unlock(&ecc->lock);
return ret;
}
ret = mtk_ecc_config(ecc, config);
if (ret) {
@ -412,7 +424,9 @@ int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
if (ret)
goto timeout;
mtk_ecc_wait_idle(ecc, ECC_ENCODE);
ret = mtk_ecc_wait_idle(ecc, ECC_ENCODE);
if (ret)
goto timeout;
/* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
len = (config->strength * ecc->caps->parity_bits + 7) >> 3;

View File

@ -554,6 +554,9 @@ static int s5pc110_dma_poll(dma_addr_t dst, dma_addr_t src, size_t count, int di
} while (!(status & S5PC110_DMA_TRANS_STATUS_TD) &&
time_before(jiffies, timeout));
if (!(status & S5PC110_DMA_TRANS_STATUS_TD))
return -ETIMEDOUT;
writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD);
return 0;
@ -608,7 +611,9 @@ static int s5pc110_dma_irq(dma_addr_t dst, dma_addr_t src, size_t count, int dir
writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20));
if (!wait_for_completion_timeout(&onenand->complete,
msecs_to_jiffies(20)))
return -ETIMEDOUT;
return 0;
}

View File

@ -72,6 +72,7 @@ config MTD_NAND_AU1550
config MTD_NAND_NDFC
tristate "IBM/MCC 4xx NAND controller"
depends on 44x || COMPILE_TEST
depends on OF
select MTD_NAND_ECC_SW_HAMMING
select MTD_NAND_ECC_SW_HAMMING_SMC
help

View File

@ -684,8 +684,15 @@ static int fsl_ifc_read_page(struct nand_chip *chip, uint8_t *buf,
return check_erased_page(chip, buf);
}
if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
if (!ctrl->nand_stat) {
mtd->ecc_stats.failed++;
return -ETIMEDOUT;
}
if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) {
mtd->ecc_stats.failed++;
return -EIO;
}
return nctrl->max_bitflips;
}

View File

@ -67,6 +67,7 @@ static struct ingenic_ecc *ingenic_ecc_get(struct device_node *np)
{
struct platform_device *pdev;
struct ingenic_ecc *ecc;
int ret;
pdev = of_find_device_by_node(np);
if (!pdev)
@ -78,7 +79,11 @@ static struct ingenic_ecc *ingenic_ecc_get(struct device_node *np)
}
ecc = platform_get_drvdata(pdev);
clk_prepare_enable(ecc->clk);
ret = clk_prepare_enable(ecc->clk);
if (ret) {
put_device(&pdev->dev);
return ERR_PTR(ret);
}
return ecc;
}

View File

@ -396,6 +396,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
unsigned long time_left;
int res;
sg_init_one(&host->sgl, mem, len);
@ -410,6 +411,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
flags);
if (!desc) {
dev_err(mtd->dev.parent, "Failed to prepare slave sg\n");
res = -ENXIO;
goto out1;
}
@ -420,7 +422,13 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
dmaengine_submit(desc);
dma_async_issue_pending(host->dma_chan);
wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000));
time_left = wait_for_completion_timeout(&host->comp_dma,
msecs_to_jiffies(1000));
if (!time_left) {
dmaengine_terminate_sync(host->dma_chan);
res = -ETIMEDOUT;
goto out1;
}
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
@ -428,7 +436,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
out1:
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
return -ENXIO;
return res;
}
static int lpc32xx_read_page(struct nand_chip *chip, uint8_t *buf,

View File

@ -430,6 +430,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
int res;
unsigned long time_left;
host->dma_slave_config.direction = dir;
host->dma_slave_config.src_addr = dma;
@ -467,12 +468,19 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
dmaengine_submit(desc);
dma_async_issue_pending(host->dma_chan);
wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000));
time_left = wait_for_completion_timeout(&host->comp,
msecs_to_jiffies(1000));
if (!time_left) {
dmaengine_terminate_sync(host->dma_chan);
res = -ETIMEDOUT;
} else {
res = 0;
}
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
return 0;
return res;
out1:
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);

View File

@ -188,7 +188,7 @@ static int ndfc_probe(struct platform_device *ofdev)
const __be32 *reg;
u32 ccr;
u32 cs;
int err, len;
int err, len = 0;
/* Read the reg property to get the chip select */
reg = of_get_property(ofdev->dev.of_node, "reg", &len);

View File

@ -822,7 +822,7 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from,
bool disable_ecc = false;
bool ecc_failed = false;
unsigned int retry_mode = 0;
int ret;
int ret = 0;
old_stats = mtd->ecc_stats;