dmaengine: Fix teardown races, resource leaks, and error handling
Subsystems (8)
cover dmaengine: Fix teardown races, resource leaks, and error handling
These issues were reported by Sashiko.
Lee Jones (10):
dmaengine: pl08x: Defer OF DMA controller registration until slave
setup
dmaengine: ae4dma: Unregister DMA engine in ae4_pci_remove()
dmaengine: ptdma: Unmap descriptor in pt_do_cleanup()
dmaengine: ptdma: Remove completed descriptor under vc.lock in
pt_cmd_callback_work()
dmaengine: amlogic: Destroy free_wq after devres DMA device
unregistration
dmaengine: arm-dma350: Query dma_cookie_status() under vc.lock in
d350_tx_status()
dmaengine: bestcomm: Defer publishing bcom_eng until initialisation
completes
dmaengine: bestcomm: Check rh_create() return value in
bcom_sram_init()
dmaengine: jz4780: Fix descriptor cleanup on hwdesc setup failure
dmaengine: dmatest: Avoid unsigned underflow in min_odd()
drivers/dma/amba-pl08x.c | 23 +++++++++ drivers/dma/amd/ae4dma/ae4dma-pci.c | 1 + drivers/dma/amd/ptdma/ptdma-dmaengine.c | 4 +- drivers/dma/amlogic-dma.c | 3 +- drivers/dma/arm-dma350.c | 3 +- drivers/dma/bestcomm/bestcomm.c | 69 +++++++++++++------------ drivers/dma/bestcomm/sram.c | 6 +++ drivers/dma/dma-jz4780.c | 5 +- drivers/dma/dmatest.c | 6 +++ 9 files changed, 80 insertions(+), 40 deletions(-)
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog
PATCH 1/10 dmaengine: pl08x: Defer OF DMA controller registration until slave setup
pl08x_of_probe() registers the controller with of_dma_controller_register() before pl08x_dma_init_virtual_channels() initialises the slave channels list and before dma_async_device_register() registers the slave DMA device, and pl08x_probe() does not unregister the OF DMA controller if subsequent initialisation fails.
Move of_dma_controller_register() after dma_async_device_register() for the slave device and unwind slave device registration if OF controller registration fails.
--- drivers/dma/amba-pl08x.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
drivers/dma/amba-pl08x.c +23 -0
@@ -2678,6 +2678,12 @@ static int pl08x_of_probe(struct amba_device *adev, pl08x->pd = pd; +return 0 ; +} + +static int pl08x_of_register (struct amba_device *adev, +struct pl08x_driver_data *pl08x) +{return of_dma_controller_register (adev->dev.of_node, pl08x_of_xlate, pl08x); }
@@ -2688,6 +2694,12 @@ static inline int pl08x_of_probe(struct amba_device *adev, {return -EINVAL ; } + +static inline int pl08x_of_register (struct amba_device *adev, +struct pl08x_driver_data *pl08x) +{ +return 0 ; +}#endif static int pl08x_probe (struct amba_device *adev,const struct amba_id *id)
@@ -2944,6 +2956,15 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) __func__, ret); goto out_no_slave_reg; } +if (np) { + ret =pl08x_of_register (adev, pl08x); +if (ret) { +dev_warn (&pl08x->adev->dev, +"%s failed to register OF DMA controller - %d\n" , + __func__, ret); + goto out_no_of_dma; + } + } }amba_set_drvdata (adev, pl08x);
@@ -2954,6 +2975,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)return 0 ; +out_no_of_dma: +dma_async_device_unregister (&pl08x->slave); out_no_slave_reg:dma_async_device_unregister (&pl08x->memcpy); out_no_memcpy_reg:
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog
PATCH 2/10 dmaengine: ae4dma: Unregister DMA engine in ae4_pci_remove()
In reply to Lee Jonesae4_core_init() registers the DMA device and creates the descriptor slab cache via pt_dmaengine_register(), but ae4_pci_remove() never calls pt_dmaengine_unregister(), leaking the descriptor cache and leaving a dangling DMA device registered after device removal.
Export pt_dmaengine_unregister() and call it from ae4_pci_remove().
--- drivers/dma/amd/ae4dma/ae4dma-pci.c | 1 + drivers/dma/amd/ptdma/ptdma-dmaengine.c | 1 + 2 files changed, 2 insertions(+)
drivers/dma/amd/ae4dma/ae4dma-pci.c +1 -0
@@ -132,6 +132,7 @@ static void ae4_pci_remove(struct pci_dev *pdev) {struct ae4_device *ae4 =dev_get_drvdata (&pdev->dev); +pt_dmaengine_unregister (&ae4->pt);ae4_destroy_work (ae4);ae4_deinit (ae4); }
drivers/dma/amd/ptdma/ptdma-dmaengine.c +1 -0
@@ -657,3 +657,4 @@ void pt_dmaengine_unregister(struct pt_device *pt)kmem_cache_destroy (pt->dma_desc_cache); } +EXPORT_SYMBOL_GPL (pt_dmaengine_unregister);
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog
[email protected]
AI reviewRe: [PATCH 02/10] dmaengine: ae4dma: Unregister DMA engine in ae4_pci_remove()
Thank you for your contribution! Sashiko AI review found 1 potential issue to consider: - [Medium] dmaengine: ae4dma: slab use-after-free in ae4_pci_remove() teardown order --
Thank you for your contribution! Sashiko AI review found 1 potential issue to consider:
- [Medium] dmaengine: ae4dma: slab use-after-free in ae4_pci_remove() teardown order
--
commit d190c73fccb945a036c5abd1d18c37ce271e68d8
Author: Lee Jones <[email protected]>
dmaengine: ae4dma: Unregister DMA engine in ae4_pci_remove()
This commit exports pt_dmaengine_unregister() and calls it in ae4_pci_remove() to unregister the DMA device and destroy its descriptor slab cache on removal.
drivers/dma/amd/ae4dma/ae4dma-pci.c static void ae4_pci_remove(struct pci_dev *pdev) · line 138
Quoted 12 lines
drivers/dma/amd/ae4dma/ae4dma-pci.c +1
-0@@ -132,6 +132,7 @@ static void ae4_pci_remove(struct pci_dev *pdev) {struct ae4_device *ae4 =dev_get_drvdata (&pdev->dev); +pt_dmaengine_unregister (&ae4->pt);ae4_destroy_work (ae4);ae4_deinit (ae4); }
[Severity: Medium]
Can calling pt_dmaengine_unregister() before ae4_destroy_work() cause a use-after-free on pt->dma_desc_cache?
pt_dmaengine_unregister() calls dma_async_device_unregister() and then immediately destroys pt->dma_desc_cache, while the per-queue delayed work items on ae4cmd_q->pws are only stopped afterwards by ae4_destroy_work(). dma_async_device_unregister() does not drain ae4cmd_q->pws, and pt_synchronize() only synchronizes the vchan tasklet. If ae4_pending_work() is still running when the device is unbound, it can free a completed descriptor into pt->dma_desc_cache after the cache is destroyed:
CPU0 kworkerae4_pci_remove() ae4_pending_work()
pt_dmaengine_unregister() cmd->pt_cmd_callback()
dma_async_device_unregister() pt_cmd_callback_work()
kmem_cache_destroy( ...
pt->dma_desc_cache) vchan_vdesc_fini()
pt_do_cleanup()
kmem_cache_free(
pt->dma_desc_cache, desc)
<-- use-after-free ae4_destroy_work()
Stopping the workqueues before pt_dmaengine_unregister(), or destroying pt->dma_desc_cache after ae4_destroy_work(), would avoid this race.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
PATCH 3/10 dmaengine: ptdma: Unmap descriptor in pt_do_cleanup()
In reply to Lee JonesWhen descriptors are freed on abort via vchan_dma_desc_free_list(), pt_do_cleanup() frees the descriptor without calling dma_descriptor_unmap(), leaking any attached dmaengine_unmap_data and DMA mappings.
Call dma_descriptor_unmap(&vd->tx) in pt_do_cleanup() before freeing the descriptor.
--- drivers/dma/amd/ptdma/ptdma-dmaengine.c | 1 + 1 file changed, 1 insertion(+)
drivers/dma/amd/ptdma/ptdma-dmaengine.c +1 -0
@@ -91,6 +91,7 @@ static void pt_do_cleanup(struct virt_dma_desc *vd)struct pt_dma_desc *desc =to_pt_desc (vd);struct pt_device *pt = desc->pt; +dma_descriptor_unmap (&vd->tx);kmem_cache_free (pt->dma_desc_cache, desc); }
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog
PATCH 4/10 dmaengine: ptdma: Remove completed descriptor under vc.lock in pt_cmd_callback_work()
In reply to Lee Jonespt_cmd_callback_work() drops chan->vc.lock before calling list_del(&desc->vd.node), racing with concurrent virt-dma list operations such as pt_terminate_all().
Move list_del(&desc->vd.node) inside the chan->vc.lock critical section, matching pt_handle_active_desc().
--- drivers/dma/amd/ptdma/ptdma-dmaengine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/dma/amd/ptdma/ptdma-dmaengine.c +1 -1
@@ -365,6 +365,7 @@ static void pt_cmd_callback_work(void *data, int err)dma_cookie_complete (tx_desc);dma_descriptor_unmap (tx_desc); +list_del (&desc->vd.node); }else { tx_desc =NULL ; }
@@ -373,7 +374,6 @@ static void pt_cmd_callback_work(void *data, int err)if (tx_desc) {dmaengine_desc_get_callback_invoke (tx_desc,NULL );dma_run_dependencies (tx_desc); -list_del (&desc->vd.node);vchan_vdesc_fini (vd); } }
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog
PATCH 5/10 dmaengine: amlogic: Destroy free_wq after devres DMA device unregistration
In reply to Lee Jonesaml_dma_probe() registers the DMA device with dmaenginem_async_device_register(), so dma_async_device_unregister() is deferred until devres release after aml_dma_remove() returns. However, aml_dma_remove() destroys aml_dma->free_wq directly, causing aml_dma_free_desc() to queue work onto a destroyed workqueue when channels and descriptors are released during devres teardown, and early error returns in aml_dma_probe() also leak aml_dma->free_wq.
Allocate aml_dma->free_wq with devm_alloc_ordered_workqueue() so it is destroyed on probe failure and after dmaenginem_async_device_register() unwinds on removal.
--- drivers/dma/amlogic-dma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/dma/amlogic-dma.c +1 -2
@@ -620,7 +620,7 @@ static int aml_dma_probe(struct platform_device *pdev)if (!aml_dma)return -ENOMEM ; - aml_dma->free_wq =alloc_ordered_workqueue ("aml_dma_free" ,0 ); + aml_dma->free_wq =devm_alloc_ordered_workqueue (&pdev->dev,"aml_dma_free" ,0 );if (!aml_dma->free_wq)return -ENOMEM ;
@@ -712,7 +712,6 @@ static void aml_dma_remove(struct platform_device *pdev)if (aml_chan)tasklet_kill (&aml_chan->vchan.task); } -destroy_workqueue (aml_dma->free_wq); }static const struct of_device_id aml_dma_ids[] = {
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog
[email protected]
AI reviewRe: [PATCH 05/10] dmaengine: amlogic: Destroy free_wq after devres DMA device unregistration
Thank you for your contribution! Sashiko AI review found no new issues in this patch, but noticed 1 related pre-existing issue it does not introduce: - [Medium] dmaengine: amlogic: missing…
Thank you for your contribution! Sashiko AI review found no new issues in this patch, but noticed 1 related pre-existing issue it does not introduce:
- [Medium] dmaengine: amlogic: missing platform_get_irq() error check in aml_dma_probe() [1]
--
commit 2bde83d0cee966b150690899ee8af15d0fb91330
Author: Lee Jones <[email protected]>
dmaengine: amlogic: Destroy free_wq after devres DMA device unregistration
This commit switches aml_dma->free_wq to devm_alloc_ordered_workqueue() so that the workqueue is cleaned up on probe failure and destroyed after devres unregisters the DMA device on removal.
drivers/dma/amlogic-dma.c static int aml_dma_probe(struct platform_device *pdev) · line 626
Quoted 13 lines
drivers/dma/amlogic-dma.c +1
-1@@ -620,7 +620,7 @@ static int aml_dma_probe(struct platform_device *pdev)if (!aml_dma)return -ENOMEM ; - aml_dma->free_wq =alloc_ordered_workqueue ("aml_dma_free" ,0 ); + aml_dma->free_wq =devm_alloc_ordered_workqueue (&pdev->dev,"aml_dma_free" ,0 );if (!aml_dma->free_wq)return -ENOMEM ;
[Severity: Medium]
This problem wasn't introduced by this patch, but should aml_dma_probe() check aml_dma->irq for errors after calling platform_get_irq() [1]?
drivers/dma/amlogic-dma.c:aml_dma_probe() {
...
aml_dma->irq = platform_get_irq(pdev, 0);
...
ret = devm_request_irq(&pdev->dev, aml_dma->irq, aml_dma_interrupt_handler,
0, dev_name(&pdev->dev), aml_dma);
}If platform_get_irq() returns a negative error code such as -EPROBE_DEFER, passing aml_dma->irq to devm_request_irq() converts it to a large unsigned IRQ number that fails with -EINVAL, turning probe deferral into a permanent probe failure.
[1] https://sashiko.dev/#/bug/linux-bec1576a-66f2-4328-8c01-0836c0f63d2a
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
PATCH 6/10 dmaengine: arm-dma350: Query dma_cookie_status() under vc.lock in d350_tx_status()
In reply to Lee JonesCalling dma_cookie_status() before acquiring dch->vc.lock in d350_tx_status() races with d350_irq() completing the active descriptor and starting the next queued transfer, causing d350_tx_status() to observe a stale DMA_IN_PROGRESS status and falsely return DMA_ERROR.
Move the dma_cookie_status() call inside the dch->vc.lock critical section.
--- drivers/dma/arm-dma350.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/dma/arm-dma350.c +1 -2
@@ -381,9 +381,8 @@ static enum dma_status d350_tx_status(struct dma_chan *chan, dma_cookie_t cookieunsigned long flags;u32 residue =0 ; +spin_lock_irqsave (&dch->vc.lock, flags); status =dma_cookie_status (chan, cookie, state); - -spin_lock_irqsave (&dch->vc.lock, flags);if (cookie == dch->cookie) { status = dch->status;if (status ==DMA_IN_PROGRESS || status ==DMA_PAUSED )
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog
PATCH 7/10 dmaengine: bestcomm: Defer publishing bcom_eng until initialisation completes
In reply to Lee Jonesmpc52xx_bcom_probe() assigns the newly allocated struct bcom_engine to the global bcom_eng pointer before calling bcom_engine_init(). A concurrent call to bcom_task_alloc() can observe a non-NULL bcom_eng pointer before its spinlock and task descriptor table are initialised.
Initialise the engine via a local pointer and only publish bcom_eng once initialisation has succeeded.
--- drivers/dma/bestcomm/bestcomm.c | 69 +++++++++++++++++---------------- 1 file changed, 36 insertions(+), 33 deletions(-)
drivers/dma/bestcomm/bestcomm.c +36 -33
@@ -270,7 +270,7 @@ static u32 fdt_ops[] = { }; -static int bcom_engine_init (void ) +static int bcom_engine_init (struct bcom_engine *eng) {int task;phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa;
@@ -282,56 +282,59 @@ static int bcom_engine_init(void) var_size =BCOM_MAX_TASKS * (BCOM_VAR_SIZE +BCOM_INC_SIZE ); fdt_size =BCOM_FDT_SIZE ; - bcom_eng->tdt =bcom_sram_alloc (tdt_size,sizeof (u32), &tdt_pa); - bcom_eng->ctx =bcom_sram_alloc (ctx_size,BCOM_CTX_ALIGN , &ctx_pa); - bcom_eng->var =bcom_sram_alloc (var_size,BCOM_VAR_ALIGN , &var_pa); - bcom_eng->fdt =bcom_sram_alloc (fdt_size,BCOM_FDT_ALIGN , &fdt_pa); + eng->tdt =bcom_sram_alloc (tdt_size,sizeof (u32), &tdt_pa); + eng->ctx =bcom_sram_alloc (ctx_size,BCOM_CTX_ALIGN , &ctx_pa); + eng->var =bcom_sram_alloc (var_size,BCOM_VAR_ALIGN , &var_pa); + eng->fdt =bcom_sram_alloc (fdt_size,BCOM_FDT_ALIGN , &fdt_pa); -if (!bcom_eng->tdt || !bcom_eng->ctx || !bcom_eng->var || !bcom_eng->fdt) { +if (!eng->tdt || !eng->ctx || !eng->var || !eng->fdt) {printk (KERN_ERR "DMA: SRAM alloc failed in engine init !\n" ); -bcom_sram_free (bcom_eng->tdt); -bcom_sram_free (bcom_eng->ctx); -bcom_sram_free (bcom_eng->var); -bcom_sram_free (bcom_eng->fdt); +bcom_sram_free (eng->tdt); +bcom_sram_free (eng->ctx); +bcom_sram_free (eng->var); +bcom_sram_free (eng->fdt);return -ENOMEM ; } -memset_io (bcom_eng->tdt,0x00 , tdt_size); -memset_io (bcom_eng->ctx,0x00 , ctx_size); -memset_io (bcom_eng->var,0x00 , var_size); -memset_io (bcom_eng->fdt,0x00 , fdt_size); +memset_io (eng->tdt,0x00 , tdt_size); +memset_io (eng->ctx,0x00 , ctx_size); +memset_io (eng->var,0x00 , var_size); +memset_io (eng->fdt,0x00 , fdt_size);/* Copy the FDT for the EU#3 */ -memcpy_toio (&bcom_eng->fdt[48 ], fdt_ops,sizeof (fdt_ops)); +memcpy_toio (&eng->fdt[48 ], fdt_ops,sizeof (fdt_ops));/* Initialize Task base structure */ for (task=0 ; task<BCOM_MAX_TASKS ; task++) { -out_be16 (&bcom_eng->regs->tcr[task],0 ); -out_8 (&bcom_eng->regs->ipr[task],0 ); +out_be16 (&eng->regs->tcr[task],0 ); +out_8 (&eng->regs->ipr[task],0 ); - bcom_eng->tdt[task].context = ctx_pa; - bcom_eng->tdt[task].var = var_pa; - bcom_eng->tdt[task].fdt = fdt_pa; + eng->tdt[task].context = ctx_pa; + eng->tdt[task].var = var_pa; + eng->tdt[task].fdt = fdt_pa; var_pa +=BCOM_VAR_SIZE +BCOM_INC_SIZE ; ctx_pa +=BCOM_CTX_SIZE ; } -out_be32 (&bcom_eng->regs->taskBar, tdt_pa); +out_be32 (&eng->regs->taskBar, tdt_pa);/* Init 'always' initiator */ -out_8 (&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS ],BCOM_IPR_ALWAYS ); +out_8 (&eng->regs->ipr[BCOM_INITIATOR_ALWAYS ],BCOM_IPR_ALWAYS ); + +/* Init lock */ +spin_lock_init (&eng->lock); + +/* Ensure all engine fields are visible before publishing bcom_eng */ +smp_store_release (&bcom_eng, eng);/* Disable COMM Bus Prefetch on the original 5200; it's broken */ if ((mfspr (SPRN_SVR ) &MPC5200_SVR_MASK ) ==MPC5200_SVR )bcom_disable_prefetch (); -/* Init lock */ -spin_lock_init (&bcom_eng->lock); -return 0 ; }
@@ -363,6 +366,7 @@ bcom_engine_cleanup(void)static int mpc52xx_bcom_probe (struct platform_device *op) { +struct bcom_engine *eng;struct device_node *ofn_sram;struct resource *res_bcom;void __iomem *regs;
@@ -398,20 +402,20 @@ static int mpc52xx_bcom_probe(struct platform_device *op) }/* Get a clean struct */ - bcom_eng = kzalloc_obj(struct bcom_engine); -if (!bcom_eng) { + eng = kzalloc_obj(struct bcom_engine); +if (!eng) { rv = -ENOMEM ; goto error_sramclean; }/* Save the node */ - bcom_eng->ofnode = op->dev.of_node; + eng->ofnode = op->dev.of_node; - bcom_eng->regs = regs; - bcom_eng->regs_base = res_bcom->start; + eng->regs = regs; + eng->regs_base = res_bcom->start;/* Now, do the real init */ - rv =bcom_engine_init (); + rv =bcom_engine_init (eng);if (rv) goto error_sramclean;
@@ -423,8 +427,7 @@ static int mpc52xx_bcom_probe(struct platform_device *op)/* Error path */ error_sramclean: -kfree (bcom_eng); - bcom_eng =NULL ; +kfree (eng);bcom_sram_cleanup (); error_ofput:of_node_put (op->dev.of_node);
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog
[email protected]
AI reviewRe: [PATCH 07/10] dmaengine: bestcomm: Defer publishing bcom_eng until initialisation completes
Thank you for your contribution! Sashiko AI review found no new issues in this patch, but noticed 1 related pre-existing issue it does not introduce: - [Critical] dmaengine: bestcomm: use-after-free…
Thank you for your contribution! Sashiko AI review found no new issues in this patch, but noticed 1 related pre-existing issue it does not introduce:
- [Critical] dmaengine: bestcomm: use-after-free in bcom_task_alloc()
[1]
--
commit 0c7452c3ef4427c8251608e31eaba52df094162d
Author: Lee Jones <[email protected]>
dmaengine: bestcomm: Defer publishing bcom_eng until initialisation completes
This commit initializes struct bcom_engine via a local pointer and defers publishing it to the global bcom_eng pointer until initialization in bcom_engine_init() has completed.
Quoted 4 lines
drivers/dma/bestcomm/bestcomm.c +0
-0
[ ... ]
Quoted 28 lines
drivers/dma/bestcomm/bestcomm.c +6
-6@@ -398,20 +402,20 @@ static int mpc52xx_bcom_probe(struct platform_device *op) }/* Get a clean struct */ - bcom_eng = kzalloc_obj(struct bcom_engine); -if (!bcom_eng) { + eng = kzalloc_obj(struct bcom_engine); +if (!eng) { rv = -ENOMEM ; goto error_sramclean; }/* Save the node */ - bcom_eng->ofnode = op->dev.of_node; + eng->ofnode = op->dev.of_node; - bcom_eng->regs = regs; - bcom_eng->regs_base = res_bcom->start; + eng->regs = regs; + eng->regs_base = res_bcom->start;/* Now, do the real init */ - rv =bcom_engine_init (); + rv =bcom_engine_init (eng);if (rv) goto error_sramclean;
drivers/dma/bestcomm/bestcomm.c static int mpc52xx_bcom_probe(struct platform_device *op) · line 433
Quoted 9 lines
drivers/dma/bestcomm/bestcomm.c +1
-2@@ -423,8 +427,7 @@ static int mpc52xx_bcom_probe(struct platform_device *op)/* Error path */ error_sramclean: -kfree (bcom_eng); - bcom_eng =NULL ; +kfree (eng);bcom_sram_cleanup (); error_ofput:of_node_put (op->dev.of_node);
[Severity: Critical]
This problem wasn't introduced by this patch, but can mpc52xx_bcom_remove() trigger a use-after-free in bcom_task_alloc() [1]?
While this patch fixes premature publication of bcom_eng during probe, mpc52xx_bcom_remove() frees bcom_eng before clearing the global pointer and without synchronizing against concurrent bcom_task_alloc() callers:
CPU0 CPU1bcom_task_alloc()
if (!bcom_eng)
mpc52xx_bcom_remove()
kfree(bcom_eng);
bcom_eng = NULL; spin_lock(&bcom_eng->lock); <-- use-after-free
[1] https://sashiko.dev/#/bug/linux-149609ff-866f-4c27-8adf-ef935bca5f89
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
PATCH 8/10 dmaengine: bestcomm: Check rh_create() return value in bcom_sram_init()
In reply to Lee Jonesrh_create() returns ERR_PTR(-ENOMEM) on allocation failure. bcom_sram_init() assigns this return value directly to bcom_sram->rh without checking IS_ERR() and passes it to rh_attach_region(), which dereferences the error pointer.
Check IS_ERR(bcom_sram->rh) after rh_create() and unmap the SRAM region on failure.
--- drivers/dma/bestcomm/sram.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/dma/bestcomm/sram.c +6 -0
@@ -88,6 +88,10 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)/* Create an rheap (defaults to 32 bits word alignment) */ bcom_sram->rh =rh_create (4 ); +if (IS_ERR (bcom_sram->rh)) { + rv =PTR_ERR (bcom_sram->rh); + goto error_unmap; + }/* Attach the free zones */ regaddr_p =NULL ;
@@ -111,6 +115,8 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)return 0 ; +error_unmap: +iounmap ((void __iomem *)bcom_sram->base_virt); error_release:release_mem_region (res.start,resource_size (&res)); error_free:
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog
PATCH 9/10 dmaengine: jz4780: Fix descriptor cleanup on hwdesc setup failure
In reply to Lee JonesWhen jz4780_dma_setup_hwdesc() fails in jz4780_dma_prep_slave_sg() or jz4780_dma_prep_dma_cyclic(), the error paths pass &jzchan->desc->vdesc to jz4780_dma_desc_free() instead of the newly allocated &desc->vdesc. This leaks the newly allocated descriptor and either dereferences NULL when jzchan->desc is NULL or frees the currently active descriptor while it is still in flight. Furthermore, jz4780_dma_desc_free() derives jzchan from vdesc->tx.chan, which is not yet initialised before vchan_tx_prep() is called.
Initialise desc->vdesc.tx.chan in jz4780_dma_desc_alloc() and pass &desc->vdesc to jz4780_dma_desc_free() in the preparation error paths.
--- drivers/dma/dma-jz4780.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/dma/dma-jz4780.c +3 -2
@@ -248,6 +248,7 @@ jz4780_dma_desc_alloc(struct jz4780_dma_chan *jzchan, unsigned int count,return NULL ; } + desc->vdesc.tx.chan = &jzchan->vchan.chan; desc->count = count; desc->type = type;
@@ -379,7 +380,7 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_slave_sg(sg_dma_len (&sgl[i]), direction);if (err <0 ) { -jz4780_dma_desc_free (&jzchan->desc->vdesc); +jz4780_dma_desc_free (&desc->vdesc);return NULL ; }
@@ -426,7 +427,7 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_dma_cyclic( err =jz4780_dma_setup_hwdesc (jzchan, &desc->desc[i], buf_addr, period_len, direction);if (err <0 ) { -jz4780_dma_desc_free (&jzchan->desc->vdesc); +jz4780_dma_desc_free (&desc->vdesc);return NULL ; }
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog
[email protected]
AI reviewRe: [PATCH 09/10] dmaengine: jz4780: Fix descriptor cleanup on hwdesc setup failure
Thank you for your contribution! Sashiko AI review found 1 potential issue to consider: - [Low] dmaengine: jz4780: Fixes trailer cites wrong introducing commit --
Thank you for your contribution! Sashiko AI review found 1 potential issue to consider:
- [Low] dmaengine: jz4780: Fixes trailer cites wrong introducing commit --
commit 689fd02184422d0b30aad68612dbfd11c23b8306
Author: Lee Jones <[email protected]>
Quoted 11 lines
dmaengine: jz4780: Fix descriptor cleanup on hwdesc setup failure
When
jz4780_dma_setup_hwdesc()fails injz4780_dma_prep_slave_sg()orjz4780_dma_prep_dma_cyclic(), the error paths pass&jzchan->desc->vdesctojz4780_dma_desc_free()instead of the newly allocated&desc->vdesc. This leaks the newly allocated descriptor and either dereferences NULL whenjzchan->descis NULL or frees the currently active descriptor while it is still in flight. Furthermore,jz4780_dma_desc_free()derives jzchan fromvdesc->tx.chan, which is not yet initialised beforevchan_tx_prep()is called.
Quoted 7 lines
Initialise
desc->vdesc.tx.chaninjz4780_dma_desc_alloc()and pass&desc->vdesctojz4780_dma_desc_free()in the preparation error paths.Fixes d894fc6046fe ("dmaengine: jz4780: add driver for the Ingenic JZ4780 DMA controller")Signed-off-by Lee Jones <[email protected]>
[Severity: Low]
In d894fc6046fe ("dmaengine: jz4780: add driver for the Ingenic JZ4780 DMA controller"), the preparation error paths did not call jz4780_dma_desc_free() at all. The broken jz4780_dma_desc_free(&jzchan->desc->vdesc) calls before vchan_tx_prep() were added later by fc878efe84c3 ("dmaengine: jz4780: fix resource leaks on error exit return"), so the Fixes tag should be:
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
PATCH 10/10 dmaengine: dmatest: Avoid unsigned underflow in min_odd()
In reply to Lee JonesWhen either argument to min_odd() is zero (for example when a DMA device reports max_xor or max_pq as 0), val evaluates to 0 and val - 1 underflows to UINT_MAX. In dmatest_func(), this sets src->cnt to UINT_MAX, which wraps around the (src->cnt + dst->cnt) >= 255 check and causes dmatest_alloc_test_data() to pass 0 to kcalloc() and dereference ZERO_SIZE_PTR.
Return 0 from min_odd() when the minimum value is 0, and abort dmatest_func() if src->cnt is 0.
--- drivers/dma/dmatest.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/dma/dmatest.c +6 -0
@@ -457,6 +457,9 @@ static unsigned int min_odd(unsigned int x, unsigned int y) {unsigned int val =min (x, y); +if (!val) +return 0 ; +return val %2 ? val : val -1 ; }
@@ -650,6 +653,9 @@ static int dmatest_func(void *data) }else goto err_thread_type; +if (!src->cnt) + goto err_free_coefs; +/* Check if buffer count fits into map count variable (u8) */ if ((src->cnt + dst->cnt) >=255 ) { pr_err("too many buffers (%d of 255 supported)\n",
Signature · 1 line
2.56.0.567.gdb80ae8cd5-goog