* [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
2026-03-27 16:58 [PATCH v2 0/4] dmaengine: dma-axi-dmac: Some memory related fixes Nuno Sá
@ 2026-03-27 16:58 ` Nuno Sá via B4 Relay
0 siblings, 0 replies; 8+ messages in thread
From: Nuno Sá @ 2026-03-27 16:58 UTC (permalink / raw)
To: dmaengine, linux-kernel; +Cc: Lars-Peter Clausen, Vinod Koul, Frank Li
The DMA device lifetime can extend beyond the platform driver unbind if
DMA channels are still referenced by client drivers. This leads to
use-after-free when the devm-managed memory is freed on unbind but the
DMA device callbacks still access it.
Fix this by:
- Allocating axi_dmac with kzalloc_obj() instead of devm_kzalloc() so
its lifetime is not tied to the platform device.
- Implementing the device_release callback that so that we can free
the object when reference count gets to 0 (no users).
- Adding an 'unbound' flag protected by the vchan lock that is set
during driver removal, preventing MMIO accesses after the device has been
unbound.
While at it, explicitly include spinlock.h given it was missing.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/dma/dma-axi-dmac.c | 70 +++++++++++++++++++++++++++++++++++++++-------
1 file changed, 60 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index 127c3cf80a0e..70d3ad7e7d37 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <dt-bindings/dma/axi-dmac.h>
@@ -174,6 +175,8 @@ struct axi_dmac {
struct dma_device dma_dev;
struct axi_dmac_chan chan;
+
+ bool unbound;
};
static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
@@ -182,6 +185,11 @@ static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
dma_dev);
}
+static struct axi_dmac *dev_to_axi_dmac(struct dma_device *dev)
+{
+ return container_of(dev, struct axi_dmac, dma_dev);
+}
+
static struct axi_dmac_chan *to_axi_dmac_chan(struct dma_chan *c)
{
return container_of(c, struct axi_dmac_chan, vchan.chan);
@@ -614,7 +622,12 @@ static int axi_dmac_terminate_all(struct dma_chan *c)
LIST_HEAD(head);
spin_lock_irqsave(&chan->vchan.lock, flags);
- axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
+ /*
+ * Only allow the MMIO access if the device is live. Otherwise still
+ * go for freeing the descriptors.
+ */
+ if (!dmac->unbound)
+ axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
chan->next_desc = NULL;
vchan_get_all_descriptors(&chan->vchan, &head);
list_splice_tail_init(&chan->active_descs, &head);
@@ -642,9 +655,12 @@ static void axi_dmac_issue_pending(struct dma_chan *c)
if (chan->hw_sg)
ctrl |= AXI_DMAC_CTRL_ENABLE_SG;
- axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl);
-
spin_lock_irqsave(&chan->vchan.lock, flags);
+ if (dmac->unbound) {
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+ return;
+ }
+ axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl);
if (vchan_issue_pending(&chan->vchan))
axi_dmac_start_transfer(chan);
spin_unlock_irqrestore(&chan->vchan.lock, flags);
@@ -1184,6 +1200,14 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
return 0;
}
+static void axi_dmac_release(struct dma_device *dma_dev)
+{
+ struct axi_dmac *dmac = dev_to_axi_dmac(dma_dev);
+
+ put_device(dma_dev->dev);
+ kfree(dmac);
+}
+
static void axi_dmac_tasklet_kill(void *task)
{
tasklet_kill(task);
@@ -1194,16 +1218,27 @@ static void axi_dmac_free_dma_controller(void *of_node)
of_dma_controller_free(of_node);
}
+static void axi_dmac_disable(void *__dmac)
+{
+ struct axi_dmac *dmac = __dmac;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dmac->chan.vchan.lock, flags);
+ dmac->unbound = true;
+ spin_unlock_irqrestore(&dmac->chan.vchan.lock, flags);
+ axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
+}
+
static int axi_dmac_probe(struct platform_device *pdev)
{
struct dma_device *dma_dev;
- struct axi_dmac *dmac;
+ struct axi_dmac *__dmac;
struct regmap *regmap;
unsigned int version;
u32 irq_mask = 0;
int ret;
- dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL);
+ struct axi_dmac *dmac __free(kfree) = kzalloc_obj(struct axi_dmac);
if (!dmac)
return -ENOMEM;
@@ -1251,6 +1286,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
dma_dev->dev = &pdev->dev;
dma_dev->src_addr_widths = BIT(dmac->chan.src_width);
dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width);
+ dma_dev->device_release = axi_dmac_release;
dma_dev->directions = BIT(dmac->chan.direction);
dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
dma_dev->max_sg_burst = 31; /* 31 SGs maximum in one burst */
@@ -1285,12 +1321,21 @@ static int axi_dmac_probe(struct platform_device *pdev)
if (ret)
return ret;
+ /*
+ * From this point on, our dmac object has it's lifetime bounded with
+ * dma_dev. Will be freed when dma_dev refcount goes to 0. That means,
+ * no more automatic kfree(). Also note that dmac is now NULL so we
+ * need __dmac.
+ */
+ __dmac = no_free_ptr(dmac);
+ get_device(&pdev->dev);
+
/*
* Put the action in here so it get's done before unregistering the DMA
* device.
*/
ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_tasklet_kill,
- &dmac->chan.vchan.task);
+ &__dmac->chan.vchan.task);
if (ret)
return ret;
@@ -1304,13 +1349,18 @@ static int axi_dmac_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = devm_request_irq(&pdev->dev, dmac->irq, axi_dmac_interrupt_handler,
- IRQF_SHARED, dev_name(&pdev->dev), dmac);
+ /* So that we can mark the device as unbound and disable it */
+ ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_disable, __dmac);
if (ret)
return ret;
- regmap = devm_regmap_init_mmio(&pdev->dev, dmac->base,
- &axi_dmac_regmap_config);
+ ret = devm_request_irq(&pdev->dev, __dmac->irq, axi_dmac_interrupt_handler,
+ IRQF_SHARED, dev_name(&pdev->dev), __dmac);
+ if (ret)
+ return ret;
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, __dmac->base,
+ &axi_dmac_regmap_config);
return PTR_ERR_OR_ZERO(regmap);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
@ 2026-03-27 16:58 ` Nuno Sá via B4 Relay
0 siblings, 0 replies; 8+ messages in thread
From: Nuno Sá via B4 Relay @ 2026-03-27 16:58 UTC (permalink / raw)
To: dmaengine, linux-kernel; +Cc: Lars-Peter Clausen, Vinod Koul, Frank Li
From: Nuno Sá <nuno.sa@analog.com>
The DMA device lifetime can extend beyond the platform driver unbind if
DMA channels are still referenced by client drivers. This leads to
use-after-free when the devm-managed memory is freed on unbind but the
DMA device callbacks still access it.
Fix this by:
- Allocating axi_dmac with kzalloc_obj() instead of devm_kzalloc() so
its lifetime is not tied to the platform device.
- Implementing the device_release callback that so that we can free
the object when reference count gets to 0 (no users).
- Adding an 'unbound' flag protected by the vchan lock that is set
during driver removal, preventing MMIO accesses after the device has been
unbound.
While at it, explicitly include spinlock.h given it was missing.
Signed-off-by: Nuno Sá <nuno.sa@analog.com>
---
drivers/dma/dma-axi-dmac.c | 70 +++++++++++++++++++++++++++++++++++++++-------
1 file changed, 60 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index 127c3cf80a0e..70d3ad7e7d37 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <dt-bindings/dma/axi-dmac.h>
@@ -174,6 +175,8 @@ struct axi_dmac {
struct dma_device dma_dev;
struct axi_dmac_chan chan;
+
+ bool unbound;
};
static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
@@ -182,6 +185,11 @@ static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
dma_dev);
}
+static struct axi_dmac *dev_to_axi_dmac(struct dma_device *dev)
+{
+ return container_of(dev, struct axi_dmac, dma_dev);
+}
+
static struct axi_dmac_chan *to_axi_dmac_chan(struct dma_chan *c)
{
return container_of(c, struct axi_dmac_chan, vchan.chan);
@@ -614,7 +622,12 @@ static int axi_dmac_terminate_all(struct dma_chan *c)
LIST_HEAD(head);
spin_lock_irqsave(&chan->vchan.lock, flags);
- axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
+ /*
+ * Only allow the MMIO access if the device is live. Otherwise still
+ * go for freeing the descriptors.
+ */
+ if (!dmac->unbound)
+ axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
chan->next_desc = NULL;
vchan_get_all_descriptors(&chan->vchan, &head);
list_splice_tail_init(&chan->active_descs, &head);
@@ -642,9 +655,12 @@ static void axi_dmac_issue_pending(struct dma_chan *c)
if (chan->hw_sg)
ctrl |= AXI_DMAC_CTRL_ENABLE_SG;
- axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl);
-
spin_lock_irqsave(&chan->vchan.lock, flags);
+ if (dmac->unbound) {
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+ return;
+ }
+ axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl);
if (vchan_issue_pending(&chan->vchan))
axi_dmac_start_transfer(chan);
spin_unlock_irqrestore(&chan->vchan.lock, flags);
@@ -1184,6 +1200,14 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
return 0;
}
+static void axi_dmac_release(struct dma_device *dma_dev)
+{
+ struct axi_dmac *dmac = dev_to_axi_dmac(dma_dev);
+
+ put_device(dma_dev->dev);
+ kfree(dmac);
+}
+
static void axi_dmac_tasklet_kill(void *task)
{
tasklet_kill(task);
@@ -1194,16 +1218,27 @@ static void axi_dmac_free_dma_controller(void *of_node)
of_dma_controller_free(of_node);
}
+static void axi_dmac_disable(void *__dmac)
+{
+ struct axi_dmac *dmac = __dmac;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dmac->chan.vchan.lock, flags);
+ dmac->unbound = true;
+ spin_unlock_irqrestore(&dmac->chan.vchan.lock, flags);
+ axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
+}
+
static int axi_dmac_probe(struct platform_device *pdev)
{
struct dma_device *dma_dev;
- struct axi_dmac *dmac;
+ struct axi_dmac *__dmac;
struct regmap *regmap;
unsigned int version;
u32 irq_mask = 0;
int ret;
- dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL);
+ struct axi_dmac *dmac __free(kfree) = kzalloc_obj(struct axi_dmac);
if (!dmac)
return -ENOMEM;
@@ -1251,6 +1286,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
dma_dev->dev = &pdev->dev;
dma_dev->src_addr_widths = BIT(dmac->chan.src_width);
dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width);
+ dma_dev->device_release = axi_dmac_release;
dma_dev->directions = BIT(dmac->chan.direction);
dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
dma_dev->max_sg_burst = 31; /* 31 SGs maximum in one burst */
@@ -1285,12 +1321,21 @@ static int axi_dmac_probe(struct platform_device *pdev)
if (ret)
return ret;
+ /*
+ * From this point on, our dmac object has it's lifetime bounded with
+ * dma_dev. Will be freed when dma_dev refcount goes to 0. That means,
+ * no more automatic kfree(). Also note that dmac is now NULL so we
+ * need __dmac.
+ */
+ __dmac = no_free_ptr(dmac);
+ get_device(&pdev->dev);
+
/*
* Put the action in here so it get's done before unregistering the DMA
* device.
*/
ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_tasklet_kill,
- &dmac->chan.vchan.task);
+ &__dmac->chan.vchan.task);
if (ret)
return ret;
@@ -1304,13 +1349,18 @@ static int axi_dmac_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = devm_request_irq(&pdev->dev, dmac->irq, axi_dmac_interrupt_handler,
- IRQF_SHARED, dev_name(&pdev->dev), dmac);
+ /* So that we can mark the device as unbound and disable it */
+ ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_disable, __dmac);
if (ret)
return ret;
- regmap = devm_regmap_init_mmio(&pdev->dev, dmac->base,
- &axi_dmac_regmap_config);
+ ret = devm_request_irq(&pdev->dev, __dmac->irq, axi_dmac_interrupt_handler,
+ IRQF_SHARED, dev_name(&pdev->dev), __dmac);
+ if (ret)
+ return ret;
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, __dmac->base,
+ &axi_dmac_regmap_config);
return PTR_ERR_OR_ZERO(regmap);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
@ 2026-03-29 23:40 kernel test robot
2026-03-30 14:32 ` Nuno Sá
0 siblings, 1 reply; 8+ messages in thread
From: kernel test robot @ 2026-03-29 23:40 UTC (permalink / raw)
To: oe-kbuild; +Cc: lkp, Dan Carpenter
BCC: lkp@intel.com
CC: oe-kbuild-all@lists.linux.dev
In-Reply-To: <20260327-dma-dmac-handle-vunmap-v2-3-021f95f0e87b@analog.com>
References: <20260327-dma-dmac-handle-vunmap-v2-3-021f95f0e87b@analog.com>
TO: "Nuno Sá via B4 Relay" <devnull+nuno.sa.analog.com@kernel.org>
TO: dmaengine@vger.kernel.org
TO: linux-kernel@vger.kernel.org
CC: "Lars-Peter Clausen" <lars@metafoo.de>
CC: Vinod Koul <vkoul@kernel.org>
CC: Frank Li <Frank.Li@kernel.org>
Hi Nuno,
kernel test robot noticed the following build warnings:
[auto build test WARNING on b7560798466a07d9c3fb011698e92c335ab28baf]
url: https://github.com/intel-lab-lkp/linux/commits/Nuno-S-via-B4-Relay/dmaengine-Fix-possuible-use-after-free/20260329-160000
base: b7560798466a07d9c3fb011698e92c335ab28baf
patch link: https://lore.kernel.org/r/20260327-dma-dmac-handle-vunmap-v2-3-021f95f0e87b%40analog.com
patch subject: [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
:::::: branch date: 16 hours ago
:::::: commit date: 16 hours ago
config: loongarch-randconfig-r071-20260329 (https://download.01.org/0day-ci/archive/20260330/202603300705.jhDfInJk-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 15.2.0
smatch: v0.5.0-9004-gb810ac53
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <error27@gmail.com>
| Closes: https://lore.kernel.org/r/202603300705.jhDfInJk-lkp@intel.com/
smatch warnings:
drivers/dma/dma-axi-dmac.c:1342 axi_dmac_probe() warn: address of NULL pointer 'dmac'
vim +/dmac +1342 drivers/dma/dma-axi-dmac.c
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1231
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1232 static int axi_dmac_probe(struct platform_device *pdev)
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1233 {
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1234 struct dma_device *dma_dev;
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1235 struct axi_dmac *__dmac;
a5b982af953bcc8 Chuhong Yuan 2019-12-09 1236 struct regmap *regmap;
b377e670bac558a Alexandru Ardelean 2020-08-25 1237 unsigned int version;
238f68a08e19a61 Paul Cercueil 2023-12-15 1238 u32 irq_mask = 0;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1239 int ret;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1240
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1241 struct axi_dmac *dmac __free(kfree) = kzalloc_obj(struct axi_dmac);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1242 if (!dmac)
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1243 return -ENOMEM;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1244
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1245 dmac->irq = platform_get_irq(pdev, 0);
50dc60a25597e10 Lars-Peter Clausen 2016-07-01 1246 if (dmac->irq < 0)
50dc60a25597e10 Lars-Peter Clausen 2016-07-01 1247 return dmac->irq;
50dc60a25597e10 Lars-Peter Clausen 2016-07-01 1248 if (dmac->irq == 0)
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1249 return -EINVAL;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1250
4b23603a251d240 Tudor Ambarus 2022-11-10 1251 dmac->base = devm_platform_ioremap_resource(pdev, 0);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1252 if (IS_ERR(dmac->base))
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1253 return PTR_ERR(dmac->base);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1254
779a44831a4f646 Nuno Sa 2024-03-28 1255 dmac->clk = devm_clk_get_enabled(&pdev->dev, NULL);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1256 if (IS_ERR(dmac->clk))
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1257 return PTR_ERR(dmac->clk);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1258
78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1259 version = axi_dmac_read(dmac, ADI_AXI_REG_VERSION);
78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1260
78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1261 if (version >= ADI_AXI_PCORE_VER(4, 3, 'a'))
78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1262 ret = axi_dmac_read_chan_config(&pdev->dev, dmac);
78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1263 else
06b6e88c7ecf48e Alexandru Ardelean 2020-08-25 1264 ret = axi_dmac_parse_dt(&pdev->dev, dmac);
78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1265
06b6e88c7ecf48e Alexandru Ardelean 2020-08-25 1266 if (ret < 0)
779a44831a4f646 Nuno Sa 2024-03-28 1267 return ret;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1268
a88fdece44d40cd Alexandru Ardelean 2020-08-25 1269 INIT_LIST_HEAD(&dmac->chan.active_descs);
a88fdece44d40cd Alexandru Ardelean 2020-08-25 1270
921234e0c5d77b5 Lars-Peter Clausen 2019-03-08 1271 dma_set_max_seg_size(&pdev->dev, UINT_MAX);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1272
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1273 dma_dev = &dmac->dma_dev;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1274 dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1275 dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask);
9a05045d2a681d3 Dragos Bogdan 2019-03-26 1276 dma_cap_set(DMA_INTERLEAVE, dma_dev->cap_mask);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1277 dma_dev->device_free_chan_resources = axi_dmac_free_chan_resources;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1278 dma_dev->device_tx_status = dma_cookie_status;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1279 dma_dev->device_issue_pending = axi_dmac_issue_pending;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1280 dma_dev->device_prep_slave_sg = axi_dmac_prep_slave_sg;
74609e5686701ed Paul Cercueil 2024-06-20 1281 dma_dev->device_prep_peripheral_dma_vec = axi_dmac_prep_peripheral_dma_vec;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1282 dma_dev->device_prep_dma_cyclic = axi_dmac_prep_dma_cyclic;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1283 dma_dev->device_prep_interleaved_dma = axi_dmac_prep_interleaved;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1284 dma_dev->device_terminate_all = axi_dmac_terminate_all;
860dd64c4382709 Lars-Peter Clausen 2015-10-20 1285 dma_dev->device_synchronize = axi_dmac_synchronize;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1286 dma_dev->dev = &pdev->dev;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1287 dma_dev->src_addr_widths = BIT(dmac->chan.src_width);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1288 dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width);
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1289 dma_dev->device_release = axi_dmac_release;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1290 dma_dev->directions = BIT(dmac->chan.direction);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1291 dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
e97dc7435972d28 Paul Cercueil 2023-12-15 1292 dma_dev->max_sg_burst = 31; /* 31 SGs maximum in one burst */
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1293 INIT_LIST_HEAD(&dma_dev->channels);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1294
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1295 dmac->chan.vchan.desc_free = axi_dmac_desc_free;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1296 vchan_init(&dmac->chan.vchan, dma_dev);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1297
b377e670bac558a Alexandru Ardelean 2020-08-25 1298 ret = axi_dmac_detect_caps(dmac, version);
b5d89905d0391de Lars-Peter Clausen 2019-05-16 1299 if (ret)
779a44831a4f646 Nuno Sa 2024-03-28 1300 return ret;
56009f0d2f54e4c Lars-Peter Clausen 2019-03-26 1301
5b969bd1d9cdfc8 Alexandru Ardelean 2019-05-27 1302 dma_dev->copy_align = (dmac->chan.address_align_mask + 1);
56009f0d2f54e4c Lars-Peter Clausen 2019-03-26 1303
238f68a08e19a61 Paul Cercueil 2023-12-15 1304 if (dmac->chan.hw_sg)
238f68a08e19a61 Paul Cercueil 2023-12-15 1305 irq_mask |= AXI_DMAC_IRQ_SOT;
238f68a08e19a61 Paul Cercueil 2023-12-15 1306
238f68a08e19a61 Paul Cercueil 2023-12-15 1307 axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_MASK, irq_mask);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1308
9327c7e7539371c Mathias Tausen 2022-07-26 1309 if (of_dma_is_coherent(pdev->dev.of_node)) {
9327c7e7539371c Mathias Tausen 2022-07-26 1310 ret = axi_dmac_read(dmac, AXI_DMAC_REG_COHERENCY_DESC);
9327c7e7539371c Mathias Tausen 2022-07-26 1311
9327c7e7539371c Mathias Tausen 2022-07-26 1312 if (version < ADI_AXI_PCORE_VER(4, 4, 'a') ||
9327c7e7539371c Mathias Tausen 2022-07-26 1313 !AXI_DMAC_DST_COHERENT_GET(ret)) {
9327c7e7539371c Mathias Tausen 2022-07-26 1314 dev_err(dmac->dma_dev.dev,
9327c7e7539371c Mathias Tausen 2022-07-26 1315 "Coherent DMA not supported in hardware");
779a44831a4f646 Nuno Sa 2024-03-28 1316 return -EINVAL;
9327c7e7539371c Mathias Tausen 2022-07-26 1317 }
9327c7e7539371c Mathias Tausen 2022-07-26 1318 }
9327c7e7539371c Mathias Tausen 2022-07-26 1319
779a44831a4f646 Nuno Sa 2024-03-28 1320 ret = dmaenginem_async_device_register(dma_dev);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1321 if (ret)
779a44831a4f646 Nuno Sa 2024-03-28 1322 return ret;
779a44831a4f646 Nuno Sa 2024-03-28 1323
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1324 /*
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1325 * From this point on, our dmac object has it's lifetime bounded with
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1326 * dma_dev. Will be freed when dma_dev refcount goes to 0. That means,
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1327 * no more automatic kfree(). Also note that dmac is now NULL so we
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1328 * need __dmac.
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1329 */
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1330 __dmac = no_free_ptr(dmac);
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1331 get_device(&pdev->dev);
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1332
779a44831a4f646 Nuno Sa 2024-03-28 1333 /*
779a44831a4f646 Nuno Sa 2024-03-28 1334 * Put the action in here so it get's done before unregistering the DMA
779a44831a4f646 Nuno Sa 2024-03-28 1335 * device.
779a44831a4f646 Nuno Sa 2024-03-28 1336 */
779a44831a4f646 Nuno Sa 2024-03-28 1337 ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_tasklet_kill,
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1338 &__dmac->chan.vchan.task);
779a44831a4f646 Nuno Sa 2024-03-28 1339 if (ret)
779a44831a4f646 Nuno Sa 2024-03-28 1340 return ret;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1341
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 @1342 ret = of_dma_controller_register(pdev->dev.of_node,
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1343 of_dma_xlate_by_chan_id, dma_dev);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1344 if (ret)
779a44831a4f646 Nuno Sa 2024-03-28 1345 return ret;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1346
779a44831a4f646 Nuno Sa 2024-03-28 1347 ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_free_dma_controller,
779a44831a4f646 Nuno Sa 2024-03-28 1348 pdev->dev.of_node);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1349 if (ret)
779a44831a4f646 Nuno Sa 2024-03-28 1350 return ret;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1351
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1352 /* So that we can mark the device as unbound and disable it */
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1353 ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_disable, __dmac);
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1354 if (ret)
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1355 return ret;
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1356
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1357 ret = devm_request_irq(&pdev->dev, __dmac->irq, axi_dmac_interrupt_handler,
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1358 IRQF_SHARED, dev_name(&pdev->dev), __dmac);
779a44831a4f646 Nuno Sa 2024-03-28 1359 if (ret)
779a44831a4f646 Nuno Sa 2024-03-28 1360 return ret;
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1361
e6cff0d5c22ce16 Nuno Sá 2026-03-27 1362 regmap = devm_regmap_init_mmio(&pdev->dev, __dmac->base,
a5b982af953bcc8 Chuhong Yuan 2019-12-09 1363 &axi_dmac_regmap_config);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1364
779a44831a4f646 Nuno Sa 2024-03-28 1365 return PTR_ERR_OR_ZERO(regmap);
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1366 }
0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1367
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
2026-03-29 23:40 [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind kernel test robot
@ 2026-03-30 14:32 ` Nuno Sá
2026-03-30 14:37 ` Dan Carpenter
0 siblings, 1 reply; 8+ messages in thread
From: Nuno Sá @ 2026-03-30 14:32 UTC (permalink / raw)
To: kernel test robot; +Cc: oe-kbuild, Dan Carpenter
On Mon, Mar 30, 2026 at 07:40:55AM +0800, kernel test robot wrote:
> BCC: lkp@intel.com
> CC: oe-kbuild-all@lists.linux.dev
> In-Reply-To: <20260327-dma-dmac-handle-vunmap-v2-3-021f95f0e87b@analog.com>
> References: <20260327-dma-dmac-handle-vunmap-v2-3-021f95f0e87b@analog.com>
> TO: "Nuno Sá via B4 Relay" <devnull+nuno.sa.analog.com@kernel.org>
> TO: dmaengine@vger.kernel.org
> TO: linux-kernel@vger.kernel.org
> CC: "Lars-Peter Clausen" <lars@metafoo.de>
> CC: Vinod Koul <vkoul@kernel.org>
> CC: Frank Li <Frank.Li@kernel.org>
>
> Hi Nuno,
>
> kernel test robot noticed the following build warnings:
>
> [auto build test WARNING on b7560798466a07d9c3fb011698e92c335ab28baf]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Nuno-S-via-B4-Relay/dmaengine-Fix-possuible-use-after-free/20260329-160000
> base: b7560798466a07d9c3fb011698e92c335ab28baf
> patch link: https://lore.kernel.org/r/20260327-dma-dmac-handle-vunmap-v2-3-021f95f0e87b%40analog.com
> patch subject: [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
> :::::: branch date: 16 hours ago
> :::::: commit date: 16 hours ago
> config: loongarch-randconfig-r071-20260329 (https://download.01.org/0day-ci/archive/20260330/202603300705.jhDfInJk-lkp@intel.com/config)
> compiler: loongarch64-linux-gcc (GCC) 15.2.0
> smatch: v0.5.0-9004-gb810ac53
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Reported-by: Dan Carpenter <error27@gmail.com>
> | Closes: https://lore.kernel.org/r/202603300705.jhDfInJk-lkp@intel.com/
>
> smatch warnings:
> drivers/dma/dma-axi-dmac.c:1342 axi_dmac_probe() warn: address of NULL pointer 'dmac'
Am I missing something or is this a false positive?
- Nuno Sá
>
> vim +/dmac +1342 drivers/dma/dma-axi-dmac.c
>
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1231
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1232 static int axi_dmac_probe(struct platform_device *pdev)
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1233 {
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1234 struct dma_device *dma_dev;
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1235 struct axi_dmac *__dmac;
> a5b982af953bcc8 Chuhong Yuan 2019-12-09 1236 struct regmap *regmap;
> b377e670bac558a Alexandru Ardelean 2020-08-25 1237 unsigned int version;
> 238f68a08e19a61 Paul Cercueil 2023-12-15 1238 u32 irq_mask = 0;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1239 int ret;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1240
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1241 struct axi_dmac *dmac __free(kfree) = kzalloc_obj(struct axi_dmac);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1242 if (!dmac)
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1243 return -ENOMEM;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1244
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1245 dmac->irq = platform_get_irq(pdev, 0);
> 50dc60a25597e10 Lars-Peter Clausen 2016-07-01 1246 if (dmac->irq < 0)
> 50dc60a25597e10 Lars-Peter Clausen 2016-07-01 1247 return dmac->irq;
> 50dc60a25597e10 Lars-Peter Clausen 2016-07-01 1248 if (dmac->irq == 0)
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1249 return -EINVAL;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1250
> 4b23603a251d240 Tudor Ambarus 2022-11-10 1251 dmac->base = devm_platform_ioremap_resource(pdev, 0);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1252 if (IS_ERR(dmac->base))
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1253 return PTR_ERR(dmac->base);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1254
> 779a44831a4f646 Nuno Sa 2024-03-28 1255 dmac->clk = devm_clk_get_enabled(&pdev->dev, NULL);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1256 if (IS_ERR(dmac->clk))
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1257 return PTR_ERR(dmac->clk);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1258
> 78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1259 version = axi_dmac_read(dmac, ADI_AXI_REG_VERSION);
> 78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1260
> 78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1261 if (version >= ADI_AXI_PCORE_VER(4, 3, 'a'))
> 78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1262 ret = axi_dmac_read_chan_config(&pdev->dev, dmac);
> 78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1263 else
> 06b6e88c7ecf48e Alexandru Ardelean 2020-08-25 1264 ret = axi_dmac_parse_dt(&pdev->dev, dmac);
> 78a2f92e4c4a3b7 Alexandru Ardelean 2020-08-25 1265
> 06b6e88c7ecf48e Alexandru Ardelean 2020-08-25 1266 if (ret < 0)
> 779a44831a4f646 Nuno Sa 2024-03-28 1267 return ret;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1268
> a88fdece44d40cd Alexandru Ardelean 2020-08-25 1269 INIT_LIST_HEAD(&dmac->chan.active_descs);
> a88fdece44d40cd Alexandru Ardelean 2020-08-25 1270
> 921234e0c5d77b5 Lars-Peter Clausen 2019-03-08 1271 dma_set_max_seg_size(&pdev->dev, UINT_MAX);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1272
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1273 dma_dev = &dmac->dma_dev;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1274 dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1275 dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask);
> 9a05045d2a681d3 Dragos Bogdan 2019-03-26 1276 dma_cap_set(DMA_INTERLEAVE, dma_dev->cap_mask);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1277 dma_dev->device_free_chan_resources = axi_dmac_free_chan_resources;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1278 dma_dev->device_tx_status = dma_cookie_status;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1279 dma_dev->device_issue_pending = axi_dmac_issue_pending;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1280 dma_dev->device_prep_slave_sg = axi_dmac_prep_slave_sg;
> 74609e5686701ed Paul Cercueil 2024-06-20 1281 dma_dev->device_prep_peripheral_dma_vec = axi_dmac_prep_peripheral_dma_vec;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1282 dma_dev->device_prep_dma_cyclic = axi_dmac_prep_dma_cyclic;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1283 dma_dev->device_prep_interleaved_dma = axi_dmac_prep_interleaved;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1284 dma_dev->device_terminate_all = axi_dmac_terminate_all;
> 860dd64c4382709 Lars-Peter Clausen 2015-10-20 1285 dma_dev->device_synchronize = axi_dmac_synchronize;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1286 dma_dev->dev = &pdev->dev;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1287 dma_dev->src_addr_widths = BIT(dmac->chan.src_width);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1288 dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width);
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1289 dma_dev->device_release = axi_dmac_release;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1290 dma_dev->directions = BIT(dmac->chan.direction);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1291 dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
> e97dc7435972d28 Paul Cercueil 2023-12-15 1292 dma_dev->max_sg_burst = 31; /* 31 SGs maximum in one burst */
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1293 INIT_LIST_HEAD(&dma_dev->channels);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1294
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1295 dmac->chan.vchan.desc_free = axi_dmac_desc_free;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1296 vchan_init(&dmac->chan.vchan, dma_dev);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1297
> b377e670bac558a Alexandru Ardelean 2020-08-25 1298 ret = axi_dmac_detect_caps(dmac, version);
> b5d89905d0391de Lars-Peter Clausen 2019-05-16 1299 if (ret)
> 779a44831a4f646 Nuno Sa 2024-03-28 1300 return ret;
> 56009f0d2f54e4c Lars-Peter Clausen 2019-03-26 1301
> 5b969bd1d9cdfc8 Alexandru Ardelean 2019-05-27 1302 dma_dev->copy_align = (dmac->chan.address_align_mask + 1);
> 56009f0d2f54e4c Lars-Peter Clausen 2019-03-26 1303
> 238f68a08e19a61 Paul Cercueil 2023-12-15 1304 if (dmac->chan.hw_sg)
> 238f68a08e19a61 Paul Cercueil 2023-12-15 1305 irq_mask |= AXI_DMAC_IRQ_SOT;
> 238f68a08e19a61 Paul Cercueil 2023-12-15 1306
> 238f68a08e19a61 Paul Cercueil 2023-12-15 1307 axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_MASK, irq_mask);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1308
> 9327c7e7539371c Mathias Tausen 2022-07-26 1309 if (of_dma_is_coherent(pdev->dev.of_node)) {
> 9327c7e7539371c Mathias Tausen 2022-07-26 1310 ret = axi_dmac_read(dmac, AXI_DMAC_REG_COHERENCY_DESC);
> 9327c7e7539371c Mathias Tausen 2022-07-26 1311
> 9327c7e7539371c Mathias Tausen 2022-07-26 1312 if (version < ADI_AXI_PCORE_VER(4, 4, 'a') ||
> 9327c7e7539371c Mathias Tausen 2022-07-26 1313 !AXI_DMAC_DST_COHERENT_GET(ret)) {
> 9327c7e7539371c Mathias Tausen 2022-07-26 1314 dev_err(dmac->dma_dev.dev,
> 9327c7e7539371c Mathias Tausen 2022-07-26 1315 "Coherent DMA not supported in hardware");
> 779a44831a4f646 Nuno Sa 2024-03-28 1316 return -EINVAL;
> 9327c7e7539371c Mathias Tausen 2022-07-26 1317 }
> 9327c7e7539371c Mathias Tausen 2022-07-26 1318 }
> 9327c7e7539371c Mathias Tausen 2022-07-26 1319
> 779a44831a4f646 Nuno Sa 2024-03-28 1320 ret = dmaenginem_async_device_register(dma_dev);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1321 if (ret)
> 779a44831a4f646 Nuno Sa 2024-03-28 1322 return ret;
> 779a44831a4f646 Nuno Sa 2024-03-28 1323
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1324 /*
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1325 * From this point on, our dmac object has it's lifetime bounded with
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1326 * dma_dev. Will be freed when dma_dev refcount goes to 0. That means,
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1327 * no more automatic kfree(). Also note that dmac is now NULL so we
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1328 * need __dmac.
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1329 */
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1330 __dmac = no_free_ptr(dmac);
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1331 get_device(&pdev->dev);
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1332
> 779a44831a4f646 Nuno Sa 2024-03-28 1333 /*
> 779a44831a4f646 Nuno Sa 2024-03-28 1334 * Put the action in here so it get's done before unregistering the DMA
> 779a44831a4f646 Nuno Sa 2024-03-28 1335 * device.
> 779a44831a4f646 Nuno Sa 2024-03-28 1336 */
> 779a44831a4f646 Nuno Sa 2024-03-28 1337 ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_tasklet_kill,
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1338 &__dmac->chan.vchan.task);
> 779a44831a4f646 Nuno Sa 2024-03-28 1339 if (ret)
> 779a44831a4f646 Nuno Sa 2024-03-28 1340 return ret;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1341
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 @1342 ret = of_dma_controller_register(pdev->dev.of_node,
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1343 of_dma_xlate_by_chan_id, dma_dev);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1344 if (ret)
> 779a44831a4f646 Nuno Sa 2024-03-28 1345 return ret;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1346
> 779a44831a4f646 Nuno Sa 2024-03-28 1347 ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_free_dma_controller,
> 779a44831a4f646 Nuno Sa 2024-03-28 1348 pdev->dev.of_node);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1349 if (ret)
> 779a44831a4f646 Nuno Sa 2024-03-28 1350 return ret;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1351
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1352 /* So that we can mark the device as unbound and disable it */
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1353 ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_disable, __dmac);
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1354 if (ret)
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1355 return ret;
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1356
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1357 ret = devm_request_irq(&pdev->dev, __dmac->irq, axi_dmac_interrupt_handler,
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1358 IRQF_SHARED, dev_name(&pdev->dev), __dmac);
> 779a44831a4f646 Nuno Sa 2024-03-28 1359 if (ret)
> 779a44831a4f646 Nuno Sa 2024-03-28 1360 return ret;
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1361
> e6cff0d5c22ce16 Nuno Sá 2026-03-27 1362 regmap = devm_regmap_init_mmio(&pdev->dev, __dmac->base,
> a5b982af953bcc8 Chuhong Yuan 2019-12-09 1363 &axi_dmac_regmap_config);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1364
> 779a44831a4f646 Nuno Sa 2024-03-28 1365 return PTR_ERR_OR_ZERO(regmap);
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1366 }
> 0e3b67b348b838d Lars-Peter Clausen 2015-08-20 1367
>
> --
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
2026-03-30 14:32 ` Nuno Sá
@ 2026-03-30 14:37 ` Dan Carpenter
0 siblings, 0 replies; 8+ messages in thread
From: Dan Carpenter @ 2026-03-30 14:37 UTC (permalink / raw)
To: Nuno Sá; +Cc: kernel test robot, oe-kbuild
On Mon, Mar 30, 2026 at 03:32:25PM +0100, Nuno Sá wrote:
> On Mon, Mar 30, 2026 at 07:40:55AM +0800, kernel test robot wrote:
> > BCC: lkp@intel.com
> > CC: oe-kbuild-all@lists.linux.dev
> > In-Reply-To: <20260327-dma-dmac-handle-vunmap-v2-3-021f95f0e87b@analog.com>
> > References: <20260327-dma-dmac-handle-vunmap-v2-3-021f95f0e87b@analog.com>
> > TO: "Nuno Sá via B4 Relay" <devnull+nuno.sa.analog.com@kernel.org>
> > TO: dmaengine@vger.kernel.org
> > TO: linux-kernel@vger.kernel.org
> > CC: "Lars-Peter Clausen" <lars@metafoo.de>
> > CC: Vinod Koul <vkoul@kernel.org>
> > CC: Frank Li <Frank.Li@kernel.org>
> >
> > Hi Nuno,
> >
> > kernel test robot noticed the following build warnings:
> >
> > [auto build test WARNING on b7560798466a07d9c3fb011698e92c335ab28baf]
> >
> > url: https://github.com/intel-lab-lkp/linux/commits/Nuno-S-via-B4-Relay/dmaengine-Fix-possuible-use-after-free/20260329-160000
> > base: b7560798466a07d9c3fb011698e92c335ab28baf
> > patch link: https://lore.kernel.org/r/20260327-dma-dmac-handle-vunmap-v2-3-021f95f0e87b%40analog.com
> > patch subject: [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
> > :::::: branch date: 16 hours ago
> > :::::: commit date: 16 hours ago
> > config: loongarch-randconfig-r071-20260329 (https://download.01.org/0day-ci/archive/20260330/202603300705.jhDfInJk-lkp@intel.com/config)
> > compiler: loongarch64-linux-gcc (GCC) 15.2.0
> > smatch: v0.5.0-9004-gb810ac53
> >
> > If you fix the issue in a separate patch/commit (i.e. not just a new version of
> > the same patch/commit), kindly add following tags
> > | Reported-by: kernel test robot <lkp@intel.com>
> > | Reported-by: Dan Carpenter <error27@gmail.com>
> > | Closes: https://lore.kernel.org/r/202603300705.jhDfInJk-lkp@intel.com/
> >
> > smatch warnings:
> > drivers/dma/dma-axi-dmac.c:1342 axi_dmac_probe() warn: address of NULL pointer 'dmac'
>
> Am I missing something or is this a false positive?
>
It is a false positive, yes. You're using lei to see unfiltered
results. Normally I review the warnings and hit resend on the
warnings which might be real.
regards,
dan carpenter
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
2026-03-27 16:58 ` Nuno Sá via B4 Relay
(?)
@ 2026-03-30 15:22 ` Frank Li
2026-03-31 8:46 ` Nuno Sá
-1 siblings, 1 reply; 8+ messages in thread
From: Frank Li @ 2026-03-30 15:22 UTC (permalink / raw)
To: Nuno Sá
Cc: dmaengine, linux-kernel, Lars-Peter Clausen, Vinod Koul, Frank Li
On Fri, Mar 27, 2026 at 04:58:40PM +0000, Nuno Sá wrote:
> The DMA device lifetime can extend beyond the platform driver unbind if
> DMA channels are still referenced by client drivers. This leads to
> use-after-free when the devm-managed memory is freed on unbind but the
> DMA device callbacks still access it.
>
> Fix this by:
> - Allocating axi_dmac with kzalloc_obj() instead of devm_kzalloc() so
> its lifetime is not tied to the platform device.
> - Implementing the device_release callback that so that we can free
> the object when reference count gets to 0 (no users).
> - Adding an 'unbound' flag protected by the vchan lock that is set
> during driver removal, preventing MMIO accesses after the device has been
> unbound.
>
> While at it, explicitly include spinlock.h given it was missing.
>
> Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> ---
Not sure if it similar with
https://lore.kernel.org/dmaengine/20250903-v6-16-topic-sdma-v1-9-ac7bab629e8b@pengutronix.de/
It looks like miss device link between comsumer and provider.
Frank
> drivers/dma/dma-axi-dmac.c | 70 +++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 60 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
> index 127c3cf80a0e..70d3ad7e7d37 100644
> --- a/drivers/dma/dma-axi-dmac.c
> +++ b/drivers/dma/dma-axi-dmac.c
> @@ -24,6 +24,7 @@
> #include <linux/platform_device.h>
> #include <linux/regmap.h>
> #include <linux/slab.h>
> +#include <linux/spinlock.h>
>
> #include <dt-bindings/dma/axi-dmac.h>
>
> @@ -174,6 +175,8 @@ struct axi_dmac {
>
> struct dma_device dma_dev;
> struct axi_dmac_chan chan;
> +
> + bool unbound;
> };
>
> static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
> @@ -182,6 +185,11 @@ static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
> dma_dev);
> }
>
> +static struct axi_dmac *dev_to_axi_dmac(struct dma_device *dev)
> +{
> + return container_of(dev, struct axi_dmac, dma_dev);
> +}
> +
> static struct axi_dmac_chan *to_axi_dmac_chan(struct dma_chan *c)
> {
> return container_of(c, struct axi_dmac_chan, vchan.chan);
> @@ -614,7 +622,12 @@ static int axi_dmac_terminate_all(struct dma_chan *c)
> LIST_HEAD(head);
>
> spin_lock_irqsave(&chan->vchan.lock, flags);
> - axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
> + /*
> + * Only allow the MMIO access if the device is live. Otherwise still
> + * go for freeing the descriptors.
> + */
> + if (!dmac->unbound)
> + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
> chan->next_desc = NULL;
> vchan_get_all_descriptors(&chan->vchan, &head);
> list_splice_tail_init(&chan->active_descs, &head);
> @@ -642,9 +655,12 @@ static void axi_dmac_issue_pending(struct dma_chan *c)
> if (chan->hw_sg)
> ctrl |= AXI_DMAC_CTRL_ENABLE_SG;
>
> - axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl);
> -
> spin_lock_irqsave(&chan->vchan.lock, flags);
> + if (dmac->unbound) {
> + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> + return;
> + }
> + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl);
> if (vchan_issue_pending(&chan->vchan))
> axi_dmac_start_transfer(chan);
> spin_unlock_irqrestore(&chan->vchan.lock, flags);
> @@ -1184,6 +1200,14 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
> return 0;
> }
>
> +static void axi_dmac_release(struct dma_device *dma_dev)
> +{
> + struct axi_dmac *dmac = dev_to_axi_dmac(dma_dev);
> +
> + put_device(dma_dev->dev);
> + kfree(dmac);
> +}
> +
> static void axi_dmac_tasklet_kill(void *task)
> {
> tasklet_kill(task);
> @@ -1194,16 +1218,27 @@ static void axi_dmac_free_dma_controller(void *of_node)
> of_dma_controller_free(of_node);
> }
>
> +static void axi_dmac_disable(void *__dmac)
> +{
> + struct axi_dmac *dmac = __dmac;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dmac->chan.vchan.lock, flags);
> + dmac->unbound = true;
> + spin_unlock_irqrestore(&dmac->chan.vchan.lock, flags);
> + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
> +}
> +
> static int axi_dmac_probe(struct platform_device *pdev)
> {
> struct dma_device *dma_dev;
> - struct axi_dmac *dmac;
> + struct axi_dmac *__dmac;
> struct regmap *regmap;
> unsigned int version;
> u32 irq_mask = 0;
> int ret;
>
> - dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL);
> + struct axi_dmac *dmac __free(kfree) = kzalloc_obj(struct axi_dmac);
> if (!dmac)
> return -ENOMEM;
>
> @@ -1251,6 +1286,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
> dma_dev->dev = &pdev->dev;
> dma_dev->src_addr_widths = BIT(dmac->chan.src_width);
> dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width);
> + dma_dev->device_release = axi_dmac_release;
> dma_dev->directions = BIT(dmac->chan.direction);
> dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
> dma_dev->max_sg_burst = 31; /* 31 SGs maximum in one burst */
> @@ -1285,12 +1321,21 @@ static int axi_dmac_probe(struct platform_device *pdev)
> if (ret)
> return ret;
>
> + /*
> + * From this point on, our dmac object has it's lifetime bounded with
> + * dma_dev. Will be freed when dma_dev refcount goes to 0. That means,
> + * no more automatic kfree(). Also note that dmac is now NULL so we
> + * need __dmac.
> + */
> + __dmac = no_free_ptr(dmac);
> + get_device(&pdev->dev);
> +
> /*
> * Put the action in here so it get's done before unregistering the DMA
> * device.
> */
> ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_tasklet_kill,
> - &dmac->chan.vchan.task);
> + &__dmac->chan.vchan.task);
> if (ret)
> return ret;
>
> @@ -1304,13 +1349,18 @@ static int axi_dmac_probe(struct platform_device *pdev)
> if (ret)
> return ret;
>
> - ret = devm_request_irq(&pdev->dev, dmac->irq, axi_dmac_interrupt_handler,
> - IRQF_SHARED, dev_name(&pdev->dev), dmac);
> + /* So that we can mark the device as unbound and disable it */
> + ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_disable, __dmac);
> if (ret)
> return ret;
>
> - regmap = devm_regmap_init_mmio(&pdev->dev, dmac->base,
> - &axi_dmac_regmap_config);
> + ret = devm_request_irq(&pdev->dev, __dmac->irq, axi_dmac_interrupt_handler,
> + IRQF_SHARED, dev_name(&pdev->dev), __dmac);
> + if (ret)
> + return ret;
> +
> + regmap = devm_regmap_init_mmio(&pdev->dev, __dmac->base,
> + &axi_dmac_regmap_config);
>
> return PTR_ERR_OR_ZERO(regmap);
> }
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
2026-03-30 15:22 ` Frank Li
@ 2026-03-31 8:46 ` Nuno Sá
2026-03-31 14:20 ` Frank Li
0 siblings, 1 reply; 8+ messages in thread
From: Nuno Sá @ 2026-03-31 8:46 UTC (permalink / raw)
To: Frank Li
Cc: Nuno Sá, dmaengine, linux-kernel, Lars-Peter Clausen,
Vinod Koul, Frank Li
On Mon, Mar 30, 2026 at 11:22:10AM -0400, Frank Li wrote:
> On Fri, Mar 27, 2026 at 04:58:40PM +0000, Nuno Sá wrote:
> > The DMA device lifetime can extend beyond the platform driver unbind if
> > DMA channels are still referenced by client drivers. This leads to
> > use-after-free when the devm-managed memory is freed on unbind but the
> > DMA device callbacks still access it.
> >
> > Fix this by:
> > - Allocating axi_dmac with kzalloc_obj() instead of devm_kzalloc() so
> > its lifetime is not tied to the platform device.
> > - Implementing the device_release callback that so that we can free
> > the object when reference count gets to 0 (no users).
> > - Adding an 'unbound' flag protected by the vchan lock that is set
> > during driver removal, preventing MMIO accesses after the device has been
> > unbound.
> >
> > While at it, explicitly include spinlock.h given it was missing.
> >
> > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > ---
>
> Not sure if it similar with
> https://lore.kernel.org/dmaengine/20250903-v6-16-topic-sdma-v1-9-ac7bab629e8b@pengutronix.de/
>
> It looks like miss device link between comsumer and provider.
Well, it surely it's related. I mean, if we ensure the consumers are
gone through devlinks and nothing is left behind, then this patch is basically unneeded.
But, FWIW, my 2cents would also go into questioning if AUTOREMOVE is
really want we want in every situation? Might be to harsh to assume that
a DMA channel consumer is useless even if DMA is gone. Anyways, is there
a v2 already? I would be interested in following this one...
- Nuno Sá
>
> Frank
>
> > drivers/dma/dma-axi-dmac.c | 70 +++++++++++++++++++++++++++++++++++++++-------
> > 1 file changed, 60 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
> > index 127c3cf80a0e..70d3ad7e7d37 100644
> > --- a/drivers/dma/dma-axi-dmac.c
> > +++ b/drivers/dma/dma-axi-dmac.c
> > @@ -24,6 +24,7 @@
> > #include <linux/platform_device.h>
> > #include <linux/regmap.h>
> > #include <linux/slab.h>
> > +#include <linux/spinlock.h>
> >
> > #include <dt-bindings/dma/axi-dmac.h>
> >
> > @@ -174,6 +175,8 @@ struct axi_dmac {
> >
> > struct dma_device dma_dev;
> > struct axi_dmac_chan chan;
> > +
> > + bool unbound;
> > };
> >
> > static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
> > @@ -182,6 +185,11 @@ static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
> > dma_dev);
> > }
> >
> > +static struct axi_dmac *dev_to_axi_dmac(struct dma_device *dev)
> > +{
> > + return container_of(dev, struct axi_dmac, dma_dev);
> > +}
> > +
> > static struct axi_dmac_chan *to_axi_dmac_chan(struct dma_chan *c)
> > {
> > return container_of(c, struct axi_dmac_chan, vchan.chan);
> > @@ -614,7 +622,12 @@ static int axi_dmac_terminate_all(struct dma_chan *c)
> > LIST_HEAD(head);
> >
> > spin_lock_irqsave(&chan->vchan.lock, flags);
> > - axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
> > + /*
> > + * Only allow the MMIO access if the device is live. Otherwise still
> > + * go for freeing the descriptors.
> > + */
> > + if (!dmac->unbound)
> > + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
> > chan->next_desc = NULL;
> > vchan_get_all_descriptors(&chan->vchan, &head);
> > list_splice_tail_init(&chan->active_descs, &head);
> > @@ -642,9 +655,12 @@ static void axi_dmac_issue_pending(struct dma_chan *c)
> > if (chan->hw_sg)
> > ctrl |= AXI_DMAC_CTRL_ENABLE_SG;
> >
> > - axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl);
> > -
> > spin_lock_irqsave(&chan->vchan.lock, flags);
> > + if (dmac->unbound) {
> > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > + return;
> > + }
> > + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl);
> > if (vchan_issue_pending(&chan->vchan))
> > axi_dmac_start_transfer(chan);
> > spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > @@ -1184,6 +1200,14 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
> > return 0;
> > }
> >
> > +static void axi_dmac_release(struct dma_device *dma_dev)
> > +{
> > + struct axi_dmac *dmac = dev_to_axi_dmac(dma_dev);
> > +
> > + put_device(dma_dev->dev);
> > + kfree(dmac);
> > +}
> > +
> > static void axi_dmac_tasklet_kill(void *task)
> > {
> > tasklet_kill(task);
> > @@ -1194,16 +1218,27 @@ static void axi_dmac_free_dma_controller(void *of_node)
> > of_dma_controller_free(of_node);
> > }
> >
> > +static void axi_dmac_disable(void *__dmac)
> > +{
> > + struct axi_dmac *dmac = __dmac;
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&dmac->chan.vchan.lock, flags);
> > + dmac->unbound = true;
> > + spin_unlock_irqrestore(&dmac->chan.vchan.lock, flags);
> > + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
> > +}
> > +
> > static int axi_dmac_probe(struct platform_device *pdev)
> > {
> > struct dma_device *dma_dev;
> > - struct axi_dmac *dmac;
> > + struct axi_dmac *__dmac;
> > struct regmap *regmap;
> > unsigned int version;
> > u32 irq_mask = 0;
> > int ret;
> >
> > - dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL);
> > + struct axi_dmac *dmac __free(kfree) = kzalloc_obj(struct axi_dmac);
> > if (!dmac)
> > return -ENOMEM;
> >
> > @@ -1251,6 +1286,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
> > dma_dev->dev = &pdev->dev;
> > dma_dev->src_addr_widths = BIT(dmac->chan.src_width);
> > dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width);
> > + dma_dev->device_release = axi_dmac_release;
> > dma_dev->directions = BIT(dmac->chan.direction);
> > dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
> > dma_dev->max_sg_burst = 31; /* 31 SGs maximum in one burst */
> > @@ -1285,12 +1321,21 @@ static int axi_dmac_probe(struct platform_device *pdev)
> > if (ret)
> > return ret;
> >
> > + /*
> > + * From this point on, our dmac object has it's lifetime bounded with
> > + * dma_dev. Will be freed when dma_dev refcount goes to 0. That means,
> > + * no more automatic kfree(). Also note that dmac is now NULL so we
> > + * need __dmac.
> > + */
> > + __dmac = no_free_ptr(dmac);
> > + get_device(&pdev->dev);
> > +
> > /*
> > * Put the action in here so it get's done before unregistering the DMA
> > * device.
> > */
> > ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_tasklet_kill,
> > - &dmac->chan.vchan.task);
> > + &__dmac->chan.vchan.task);
> > if (ret)
> > return ret;
> >
> > @@ -1304,13 +1349,18 @@ static int axi_dmac_probe(struct platform_device *pdev)
> > if (ret)
> > return ret;
> >
> > - ret = devm_request_irq(&pdev->dev, dmac->irq, axi_dmac_interrupt_handler,
> > - IRQF_SHARED, dev_name(&pdev->dev), dmac);
> > + /* So that we can mark the device as unbound and disable it */
> > + ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_disable, __dmac);
> > if (ret)
> > return ret;
> >
> > - regmap = devm_regmap_init_mmio(&pdev->dev, dmac->base,
> > - &axi_dmac_regmap_config);
> > + ret = devm_request_irq(&pdev->dev, __dmac->irq, axi_dmac_interrupt_handler,
> > + IRQF_SHARED, dev_name(&pdev->dev), __dmac);
> > + if (ret)
> > + return ret;
> > +
> > + regmap = devm_regmap_init_mmio(&pdev->dev, __dmac->base,
> > + &axi_dmac_regmap_config);
> >
> > return PTR_ERR_OR_ZERO(regmap);
> > }
> >
> > --
> > 2.53.0
> >
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind
2026-03-31 8:46 ` Nuno Sá
@ 2026-03-31 14:20 ` Frank Li
0 siblings, 0 replies; 8+ messages in thread
From: Frank Li @ 2026-03-31 14:20 UTC (permalink / raw)
To: Nuno Sá
Cc: Nuno Sá, dmaengine, linux-kernel, Lars-Peter Clausen,
Vinod Koul, Frank Li
On Tue, Mar 31, 2026 at 09:46:35AM +0100, Nuno Sá wrote:
> On Mon, Mar 30, 2026 at 11:22:10AM -0400, Frank Li wrote:
> > On Fri, Mar 27, 2026 at 04:58:40PM +0000, Nuno Sá wrote:
> > > The DMA device lifetime can extend beyond the platform driver unbind if
> > > DMA channels are still referenced by client drivers. This leads to
> > > use-after-free when the devm-managed memory is freed on unbind but the
> > > DMA device callbacks still access it.
> > >
> > > Fix this by:
> > > - Allocating axi_dmac with kzalloc_obj() instead of devm_kzalloc() so
> > > its lifetime is not tied to the platform device.
> > > - Implementing the device_release callback that so that we can free
> > > the object when reference count gets to 0 (no users).
> > > - Adding an 'unbound' flag protected by the vchan lock that is set
> > > during driver removal, preventing MMIO accesses after the device has been
> > > unbound.
> > >
> > > While at it, explicitly include spinlock.h given it was missing.
> > >
> > > Signed-off-by: Nuno Sá <nuno.sa@analog.com>
> > > ---
> >
> > Not sure if it similar with
> > https://lore.kernel.org/dmaengine/20250903-v6-16-topic-sdma-v1-9-ac7bab629e8b@pengutronix.de/
> >
> > It looks like miss device link between comsumer and provider.
>
> Well, it surely it's related. I mean, if we ensure the consumers are
> gone through devlinks and nothing is left behind, then this patch is basically unneeded.
>
> But, FWIW, my 2cents would also go into questioning if AUTOREMOVE is
> really want we want in every situation? Might be to harsh to assume that
> a DMA channel consumer is useless even if DMA is gone. Anyways, is there
> a v2 already? I would be interested in following this one...
This patch may be missed by vnod, I have asked vnod to check again. The
open question link to channel device or dma enginee device. I prefer link
to channel devices, so it support more advance's runtime pm management.
Frank
>
> - Nuno Sá
>
> >
> > Frank
> >
> > > drivers/dma/dma-axi-dmac.c | 70 +++++++++++++++++++++++++++++++++++++++-------
> > > 1 file changed, 60 insertions(+), 10 deletions(-)
> > >
> > > diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
> > > index 127c3cf80a0e..70d3ad7e7d37 100644
> > > --- a/drivers/dma/dma-axi-dmac.c
> > > +++ b/drivers/dma/dma-axi-dmac.c
> > > @@ -24,6 +24,7 @@
> > > #include <linux/platform_device.h>
> > > #include <linux/regmap.h>
> > > #include <linux/slab.h>
> > > +#include <linux/spinlock.h>
> > >
> > > #include <dt-bindings/dma/axi-dmac.h>
> > >
> > > @@ -174,6 +175,8 @@ struct axi_dmac {
> > >
> > > struct dma_device dma_dev;
> > > struct axi_dmac_chan chan;
> > > +
> > > + bool unbound;
> > > };
> > >
> > > static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
> > > @@ -182,6 +185,11 @@ static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan)
> > > dma_dev);
> > > }
> > >
> > > +static struct axi_dmac *dev_to_axi_dmac(struct dma_device *dev)
> > > +{
> > > + return container_of(dev, struct axi_dmac, dma_dev);
> > > +}
> > > +
> > > static struct axi_dmac_chan *to_axi_dmac_chan(struct dma_chan *c)
> > > {
> > > return container_of(c, struct axi_dmac_chan, vchan.chan);
> > > @@ -614,7 +622,12 @@ static int axi_dmac_terminate_all(struct dma_chan *c)
> > > LIST_HEAD(head);
> > >
> > > spin_lock_irqsave(&chan->vchan.lock, flags);
> > > - axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
> > > + /*
> > > + * Only allow the MMIO access if the device is live. Otherwise still
> > > + * go for freeing the descriptors.
> > > + */
> > > + if (!dmac->unbound)
> > > + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
> > > chan->next_desc = NULL;
> > > vchan_get_all_descriptors(&chan->vchan, &head);
> > > list_splice_tail_init(&chan->active_descs, &head);
> > > @@ -642,9 +655,12 @@ static void axi_dmac_issue_pending(struct dma_chan *c)
> > > if (chan->hw_sg)
> > > ctrl |= AXI_DMAC_CTRL_ENABLE_SG;
> > >
> > > - axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl);
> > > -
> > > spin_lock_irqsave(&chan->vchan.lock, flags);
> > > + if (dmac->unbound) {
> > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > + return;
> > > + }
> > > + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, ctrl);
> > > if (vchan_issue_pending(&chan->vchan))
> > > axi_dmac_start_transfer(chan);
> > > spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > @@ -1184,6 +1200,14 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version)
> > > return 0;
> > > }
> > >
> > > +static void axi_dmac_release(struct dma_device *dma_dev)
> > > +{
> > > + struct axi_dmac *dmac = dev_to_axi_dmac(dma_dev);
> > > +
> > > + put_device(dma_dev->dev);
> > > + kfree(dmac);
> > > +}
> > > +
> > > static void axi_dmac_tasklet_kill(void *task)
> > > {
> > > tasklet_kill(task);
> > > @@ -1194,16 +1218,27 @@ static void axi_dmac_free_dma_controller(void *of_node)
> > > of_dma_controller_free(of_node);
> > > }
> > >
> > > +static void axi_dmac_disable(void *__dmac)
> > > +{
> > > + struct axi_dmac *dmac = __dmac;
> > > + unsigned long flags;
> > > +
> > > + spin_lock_irqsave(&dmac->chan.vchan.lock, flags);
> > > + dmac->unbound = true;
> > > + spin_unlock_irqrestore(&dmac->chan.vchan.lock, flags);
> > > + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0);
> > > +}
> > > +
> > > static int axi_dmac_probe(struct platform_device *pdev)
> > > {
> > > struct dma_device *dma_dev;
> > > - struct axi_dmac *dmac;
> > > + struct axi_dmac *__dmac;
> > > struct regmap *regmap;
> > > unsigned int version;
> > > u32 irq_mask = 0;
> > > int ret;
> > >
> > > - dmac = devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL);
> > > + struct axi_dmac *dmac __free(kfree) = kzalloc_obj(struct axi_dmac);
> > > if (!dmac)
> > > return -ENOMEM;
> > >
> > > @@ -1251,6 +1286,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
> > > dma_dev->dev = &pdev->dev;
> > > dma_dev->src_addr_widths = BIT(dmac->chan.src_width);
> > > dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width);
> > > + dma_dev->device_release = axi_dmac_release;
> > > dma_dev->directions = BIT(dmac->chan.direction);
> > > dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
> > > dma_dev->max_sg_burst = 31; /* 31 SGs maximum in one burst */
> > > @@ -1285,12 +1321,21 @@ static int axi_dmac_probe(struct platform_device *pdev)
> > > if (ret)
> > > return ret;
> > >
> > > + /*
> > > + * From this point on, our dmac object has it's lifetime bounded with
> > > + * dma_dev. Will be freed when dma_dev refcount goes to 0. That means,
> > > + * no more automatic kfree(). Also note that dmac is now NULL so we
> > > + * need __dmac.
> > > + */
> > > + __dmac = no_free_ptr(dmac);
> > > + get_device(&pdev->dev);
> > > +
> > > /*
> > > * Put the action in here so it get's done before unregistering the DMA
> > > * device.
> > > */
> > > ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_tasklet_kill,
> > > - &dmac->chan.vchan.task);
> > > + &__dmac->chan.vchan.task);
> > > if (ret)
> > > return ret;
> > >
> > > @@ -1304,13 +1349,18 @@ static int axi_dmac_probe(struct platform_device *pdev)
> > > if (ret)
> > > return ret;
> > >
> > > - ret = devm_request_irq(&pdev->dev, dmac->irq, axi_dmac_interrupt_handler,
> > > - IRQF_SHARED, dev_name(&pdev->dev), dmac);
> > > + /* So that we can mark the device as unbound and disable it */
> > > + ret = devm_add_action_or_reset(&pdev->dev, axi_dmac_disable, __dmac);
> > > if (ret)
> > > return ret;
> > >
> > > - regmap = devm_regmap_init_mmio(&pdev->dev, dmac->base,
> > > - &axi_dmac_regmap_config);
> > > + ret = devm_request_irq(&pdev->dev, __dmac->irq, axi_dmac_interrupt_handler,
> > > + IRQF_SHARED, dev_name(&pdev->dev), __dmac);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + regmap = devm_regmap_init_mmio(&pdev->dev, __dmac->base,
> > > + &axi_dmac_regmap_config);
> > >
> > > return PTR_ERR_OR_ZERO(regmap);
> > > }
> > >
> > > --
> > > 2.53.0
> > >
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-03-31 14:20 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-29 23:40 [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind kernel test robot
2026-03-30 14:32 ` Nuno Sá
2026-03-30 14:37 ` Dan Carpenter
-- strict thread matches above, loose matches on Subject: below --
2026-03-27 16:58 [PATCH v2 0/4] dmaengine: dma-axi-dmac: Some memory related fixes Nuno Sá
2026-03-27 16:58 ` [PATCH v2 3/4] dmaengine: dma-axi-dmac: fix use-after-free on unbind Nuno Sá
2026-03-27 16:58 ` Nuno Sá via B4 Relay
2026-03-30 15:22 ` Frank Li
2026-03-31 8:46 ` Nuno Sá
2026-03-31 14:20 ` Frank Li
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.