* [PATCHv2] dmaengine: ti: omap-dma: turn lch_map into a flexible array
@ 2026-05-26 20:13 Rosen Penev
2026-05-26 20:51 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Rosen Penev @ 2026-05-26 20:13 UTC (permalink / raw)
To: dmaengine
Cc: Peter Ujfalusi, linusw, Vinod Koul, Frank Li, Kees Cook,
Gustavo A. R. Silva, open list,
open list:KERNEL HARDENING (not covered by other areas):Keyword:b__counted_by(_le|_be|_ptr)?b
Convert the separately-allocated lch_map pointer array to a C99
flexible array member at the end of struct omap_dmadev and annotate it
with __counted_by(lch_count). The probe is reordered so platform_data
lookup and the lch_count determination happen before the parent
allocation, letting struct_size() size the FAM and the dedicated
devm_kcalloc() for lch_map go away.
Two allocations collapse into one and the runtime bounds checks from
__counted_by now apply to every lch_map[] access.
Assisted-by: Claude:Opus-4.7
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
v2: fix sashiko warnings
drivers/dma/ti/omap-dma.c | 73 ++++++++++++++++++++-------------------
1 file changed, 37 insertions(+), 36 deletions(-)
diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c
index 55ece7fd0d99..0cccd6663628 100644
--- a/drivers/dma/ti/omap-dma.c
+++ b/drivers/dma/ti/omap-dma.c
@@ -57,7 +57,7 @@ struct omap_dmadev {
unsigned dma_requests;
spinlock_t irq_lock;
uint32_t irq_enable_mask;
- struct omap_chan **lch_map;
+ struct omap_chan *lch_map[] __counted_by(lch_count);
};
struct omap_chan {
@@ -1656,36 +1656,55 @@ static const struct omap_dma_config default_cfg;
static int omap_dma_probe(struct platform_device *pdev)
{
const struct omap_dma_config *conf;
+ struct omap_system_dma_plat_info *plat;
struct omap_dmadev *od;
+ int lch_count;
int rc, i, irq;
u32 val;
- od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
- if (!od)
- return -ENOMEM;
-
- od->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(od->base))
- return PTR_ERR(od->base);
-
conf = of_device_get_match_data(&pdev->dev);
if (conf) {
- od->cfg = conf;
- od->plat = dev_get_platdata(&pdev->dev);
- if (!od->plat) {
+ plat = dev_get_platdata(&pdev->dev);
+ if (!plat) {
dev_err(&pdev->dev, "omap_system_dma_plat_info is missing");
return -ENODEV;
}
} else if (IS_ENABLED(CONFIG_ARCH_OMAP1)) {
- od->cfg = &default_cfg;
-
- od->plat = omap_get_plat_info();
- if (!od->plat)
+ plat = omap_get_plat_info();
+ if (!plat)
return -EPROBE_DEFER;
} else {
return -ENODEV;
}
+ /* Number of available logical channels */
+ if (!pdev->dev.of_node) {
+ lch_count = plat->dma_attr->lch_count;
+ if (unlikely(!lch_count))
+ lch_count = OMAP_SDMA_CHANNELS;
+ } else if (of_property_read_u32(pdev->dev.of_node, "dma-channels", &lch_count)) {
+ dev_info(&pdev->dev, "Missing dma-channels property, using %u.\n",
+ OMAP_SDMA_CHANNELS);
+ lch_count = OMAP_SDMA_CHANNELS;
+ }
+
+ if (lch_count > OMAP_SDMA_CHANNELS) {
+ dev_err(&pdev->dev, "invalid dma-channels value %u\n", lch_count);
+ return -EINVAL;
+ }
+
+ od = devm_kzalloc(&pdev->dev, struct_size(od, lch_map, lch_count), GFP_KERNEL);
+ if (!od)
+ return -ENOMEM;
+
+ od->lch_count = lch_count;
+ od->plat = plat;
+ od->cfg = conf ? conf : &default_cfg;
+
+ od->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(od->base))
+ return PTR_ERR(od->base);
+
od->reg_map = od->plat->reg_map;
dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
@@ -1730,19 +1749,6 @@ static int omap_dma_probe(struct platform_device *pdev)
OMAP_SDMA_REQUESTS);
}
- /* Number of available logical channels */
- if (!pdev->dev.of_node) {
- od->lch_count = od->plat->dma_attr->lch_count;
- if (unlikely(!od->lch_count))
- od->lch_count = OMAP_SDMA_CHANNELS;
- } else if (of_property_read_u32(pdev->dev.of_node, "dma-channels",
- &od->lch_count)) {
- dev_info(&pdev->dev,
- "Missing dma-channels property, using %u.\n",
- OMAP_SDMA_CHANNELS);
- od->lch_count = OMAP_SDMA_CHANNELS;
- }
-
/* Mask of allowed logical channels */
if (pdev->dev.of_node && !of_property_read_u32(pdev->dev.of_node,
"dma-channel-mask",
@@ -1754,12 +1760,6 @@ static int omap_dma_probe(struct platform_device *pdev)
if (od->plat->dma_attr->dev_caps & HS_CHANNELS_RESERVED)
bitmap_set(od->lch_bitmap, 0, 2);
- od->lch_map = devm_kcalloc(&pdev->dev, od->lch_count,
- sizeof(*od->lch_map),
- GFP_KERNEL);
- if (!od->lch_map)
- return -ENOMEM;
-
for (i = 0; i < od->dma_requests; i++) {
rc = omap_dma_chan_init(od);
if (rc) {
@@ -1828,6 +1828,7 @@ static int omap_dma_probe(struct platform_device *pdev)
if (od->ll123_supported)
dma_pool_destroy(od->desc_pool);
omap_dma_free(od);
+ return rc;
}
}
@@ -1852,7 +1853,7 @@ static void omap_dma_remove(struct platform_device *pdev)
struct omap_dmadev *od = platform_get_drvdata(pdev);
int irq;
- if (od->cfg->may_lose_context)
+ if (od->cfg->needs_busy_check || od->cfg->may_lose_context)
cpu_pm_unregister_notifier(&od->nb);
if (pdev->dev.of_node)
--
2.54.0
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCHv2] dmaengine: ti: omap-dma: turn lch_map into a flexible array
2026-05-26 20:13 [PATCHv2] dmaengine: ti: omap-dma: turn lch_map into a flexible array Rosen Penev
@ 2026-05-26 20:51 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-05-26 20:51 UTC (permalink / raw)
To: Rosen Penev; +Cc: dmaengine, Frank.Li, vkoul
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Medium] Type mismatch and bypassed bounds check for `lch_count`.
- [Medium] Undocumented behavioral changes and bug fixes silently included in the patch.
Pre-existing issues:
- [High] Teardown race condition and unconditional devres WARN_ON in remove().
--
commit 274cec0b6701807c53603cef31b387fd271e1ddd
Author: Rosen Penev <rosenp@gmail.com>
dmaengine: ti: omap-dma: turn lch_map into a flexible array
Convert the separately-allocated lch_map pointer array to a C99 flexible
array member at the end of struct omap_dmadev and annotate it with
__counted_by(lch_count). The probe is reordered so platform_data lookup
and the lch_count determination happen before the parent allocation.
> diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c
> index 55ece7fd0d99a..0cccd66636284 100644
> --- a/drivers/dma/ti/omap-dma.c
> +++ b/drivers/dma/ti/omap-dma.c
[ ... ]
> @@ -1656,36 +1656,55 @@ static int omap_dma_probe(struct platform_device *pdev)
> {
> const struct omap_dma_config *conf;
> + struct omap_system_dma_plat_info *plat;
> struct omap_dmadev *od;
> + int lch_count;
[ ... ]
> + } else if (of_property_read_u32(pdev->dev.of_node, "dma-channels", &lch_count)) {
> + dev_info(&pdev->dev, "Missing dma-channels property, using %u.\n",
> + OMAP_SDMA_CHANNELS);
> + lch_count = OMAP_SDMA_CHANNELS;
> + }
> +
> + if (lch_count > OMAP_SDMA_CHANNELS) {
> + dev_err(&pdev->dev, "invalid dma-channels value %u\n", lch_count);
> + return -EINVAL;
> + }
> +
> + od = devm_kzalloc(&pdev->dev, struct_size(od, lch_map, lch_count), GFP_KERNEL);
[Severity: Medium]
Is it possible for a malformed Device Tree to bypass this bounds check?
The variable lch_count is declared as a signed int but its address is passed
to of_property_read_u32().
If a Device Tree provides a dma-channels value of 0x80000000 or greater,
of_property_read_u32() writes it as a negative number. This would bypass the
bounds check above since it performs a signed comparison.
Consequently, struct_size() would implicitly cast the negative value to a
massive size_t value, resulting in an integer overflow and an unintended
-ENOMEM allocation failure rather than gracefully rejecting the invalid DT
property with -EINVAL.
Could lch_count be declared as a u32 to avoid this and the -Wpointer-sign
warning?
[ ... ]
> @@ -1828,6 +1828,7 @@ static int omap_dma_probe(struct platform_device *pdev)
> if (od->ll123_supported)
> dma_pool_destroy(od->desc_pool);
> omap_dma_free(od);
> + return rc;
> }
> }
[Severity: Medium]
This isn't a bug, but should this fix be documented in the commit message or
separated into a standalone commit?
This silently fixes a missing return statement on the error path, which is
unrelated to the flexible array conversion described in the commit message.
[ ... ]
> @@ -1852,7 +1853,7 @@ static void omap_dma_remove(struct platform_device *pdev)
> struct omap_dmadev *od = platform_get_drvdata(pdev);
> int irq;
>
> - if (od->cfg->may_lose_context)
> + if (od->cfg->needs_busy_check || od->cfg->may_lose_context)
> cpu_pm_unregister_notifier(&od->nb);
[Severity: Medium]
This isn't a bug, but should this fix also be split out or documented?
Checking od->cfg->needs_busy_check here patches a PM notifier resource leak,
which seems unrelated to the flexible array conversion.
[Severity: High]
This is a pre-existing issue, but does omap_dma_remove() have a teardown race
condition?
Looking at the rest of omap_dma_remove():
drivers/dma/ti/omap-dma.c:omap_dma_remove() {
...
irq = platform_get_irq(pdev, 1);
devm_free_irq(&pdev->dev, irq, od);
dma_async_device_unregister(&od->ddev);
...
}
The function explicitly frees the IRQ handler via devm_free_irq() before
calling dma_async_device_unregister().
If active DMA transfers complete after the IRQ is freed, they will trigger an
unhandled interrupt. Since the IRQ is shared, repeatedly raising unhandled
interrupts will cause the core IRQ subsystem to permanently disable the
shared IRQ line.
Also, for legacy devices where irq <= 0, the driver skips requesting the IRQ
during probe but still unconditionally passes the invalid/negative IRQ to
devm_free_irq() during remove. Does this cause devres to fail its resource
lookup and trigger a warning stack trace?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260526201327.13225-1-rosenp@gmail.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-05-26 20:51 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-26 20:13 [PATCHv2] dmaengine: ti: omap-dma: turn lch_map into a flexible array Rosen Penev
2026-05-26 20:51 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox