DMA Engine development
 help / color / mirror / Atom feed
* [PATCH] dmaengine: ti: omap-dma: turn lch_map into a flexible array
@ 2026-05-24  2:46 Rosen Penev
  2026-05-24  3:20 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Rosen Penev @ 2026-05-24  2:46 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>
---
 drivers/dma/ti/omap-dma.c | 68 +++++++++++++++++++--------------------
 1 file changed, 33 insertions(+), 35 deletions(-)

diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c
index 55ece7fd0d99..901e38b08962 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,53 @@ 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;
+	}
+
+	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 +1747,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 +1758,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) {
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] dmaengine: ti: omap-dma: turn lch_map into a flexible array
  2026-05-24  2:46 [PATCH] dmaengine: ti: omap-dma: turn lch_map into a flexible array Rosen Penev
@ 2026-05-24  3:20 ` sashiko-bot
  0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-05-24  3:20 UTC (permalink / raw)
  To: Rosen Penev; +Cc: vkoul, Frank.Li, dmaengine

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Unbounded `dma-channels` device tree property causes out-of-bounds stack read and heap buffer overflow.
- [High] Missing return on error path and improper unregistration leaves a dangling CPU PM notifier, leading to Use-After-Free.
--

commit 91ef33462c6154050830fe926f0bdab84e94c15b
Author: Rosen Penev <rosenp@gmail.com>
Subject: 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. This lets
struct_size() size the FAM, collapsing two allocations into one.

> diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c
> index 55ece7fd0d99a..901e38b089624 100644
> --- a/drivers/dma/ti/omap-dma.c
> +++ b/drivers/dma/ti/omap-dma.c

[ ... ]

> @@ -1656,36 +1656,53 @@ 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)) {

[Severity: High]
This is a pre-existing issue, but does this code properly bound the value
read from the dma-channels device tree property?

If a malformed device tree provides a value greater than OMAP_SDMA_CHANNELS
(which is 32), the subsequent call to bitmap_from_arr32(od->lch_bitmap,
&val, od->lch_count) will attempt to read more than 32 bits from the single
32-bit stack variable 'val'.

Since od->lch_bitmap is strictly sized to 32 bits via:
    DECLARE_BITMAP(lch_bitmap, OMAP_SDMA_CHANNELS);

Could this out-of-bounds stack read silently corrupt subsequent members of
the omap_dmadev structure on the heap (such as the lch_lock mutex)?

> +		dev_info(&pdev->dev,
> +			 "Missing dma-channels property, using %u.\n",
> +			 OMAP_SDMA_CHANNELS);
> +		lch_count = OMAP_SDMA_CHANNELS;
> +	}

[ ... ]

>  	if (pdev->dev.of_node) {
>  		omap_dma_info.dma_cap = od->ddev.cap_mask;
>  
>  		/* Device-tree DMA controller registration */
>  		rc = of_dma_controller_register(pdev->dev.of_node,
>  				of_dma_simple_xlate, &omap_dma_info);
>  		if (rc) {
>  			pr_warn("OMAP-DMA: failed to register DMA controller\n");
>  			dma_async_device_unregister(&od->ddev);
>  			if (od->ll123_supported)
>  				dma_pool_destroy(od->desc_pool);
>  			omap_dma_free(od);
>  		}

[Severity: High]
This is a pre-existing issue, but is there a missing return statement in
the of_dma_controller_register() error path above?

If of_dma_controller_register() fails, the code handles the error by logging
a warning and calling omap_dma_free(od) but fails to immediately return 'rc'.

Execution falls through, eventually calling cpu_pm_register_notifier(&od->nb)
and then returning the error code 'rc'.

Because the probe function returns an error, the driver core will free 'od'
via devres. However, the CPU PM notifier remains globally registered. When a
CPU PM event occurs, could the kernel invoke the notifier callback with the
freed 'od' pointer, resulting in a use-after-free?

Additionally, does the unregistration logic in omap_dma_remove() properly
clean this up? It appears to only unregister if 'may_lose_context' is true,
but probe registers it if 'needs_busy_check' is true as well.

>  	}
>  
>  	omap_dma_init_gcr(od, DMA_DEFAULT_ARB_RATE, DMA_DEFAULT_FIFO_DEPTH, 0);
>  
>  	if (od->cfg->needs_busy_check) {
>  		od->nb.notifier_call = omap_dma_busy_notifier;
>  		cpu_pm_register_notifier(&od->nb);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260524024614.182126-1-rosenp@gmail.com?part=1

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-05-24  3:20 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-24  2:46 [PATCH] dmaengine: ti: omap-dma: turn lch_map into a flexible array Rosen Penev
2026-05-24  3:20 ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox