From nobody Thu Apr 2 20:21:31 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 41F882BEC27; Thu, 26 Mar 2026 13:37:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774532251; cv=none; b=enhrB/MBntJRLDcHyTdzo/Keplxywg0AnRHVFXqF3xZcZXSl0WrRzTG4RcZ67HSt9c8Hx0Y/Ahhx8uhviS6wPoJe1Gx4BQRA575f5rTPKJy9+jcjDuqGgcMiIEyAtW7v3/0+jacmsEuDDY8C+qAgk4NubtN3lzFOQAJizlIVxqk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774532251; c=relaxed/simple; bh=csxxfuxXyFqO8HSnCmquBiGzNzo25bMe5Y2fqsAa6RY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kHDN7M882UdmqqcbgFxIGULCjVEsZgW8kCwHFlhje7F0XpvXrW86HcdnoKfgru7fqwj0jmUcYgKB5AEoa7RZN9lginhErHVOa7HksRtNNC67E/mPqHV4a5PJ1KZdd0R6mZ/yHPSqFH017dEW2L+XW7ZDF09svwV5TLbdBDkI6tM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=r06Smy0l; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="r06Smy0l" Received: by smtp.kernel.org (Postfix) with ESMTPS id 16ED1C19423; Thu, 26 Mar 2026 13:37:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774532251; bh=csxxfuxXyFqO8HSnCmquBiGzNzo25bMe5Y2fqsAa6RY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=r06Smy0ljxs1ytPf70XnpvTZ9HVKP6NsuDl+4jcscmX4PeSGYc9qILLsx4nQPfSgE tVgyQOJo3Act2x6rfeW1h41xmBpuaQ18+GnRWId8Ncqu23Gdz18ZTOBLkCaKti87SH 6p8M5c4y0+B5t8RIg8ePz9/kZwfHlEy0VtQQK7Hw6kOW3gUJKZtbk6YRi6TLj6XrVv wLWU77N4EJZiomQM86KXqMWXvHWWwof4ab7tb2PRe2XUXxTyI5w2cAA+Gt90PKQ5Nu jq6mRrm35jfExT60JNSnpMMMA88oImeSSFEQXqBu7GzNbjykzNEebL1J7EZk41tbjr Lx/pZUTNLcccw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0724210A62D5; Thu, 26 Mar 2026 13:37:31 +0000 (UTC) From: =?utf-8?q?Nuno_S=C3=A1_via_B4_Relay?= Date: Thu, 26 Mar 2026 13:37:35 +0000 Subject: [PATCH 1/2] dmaengine: dma-axi-dmac: Defer freeing DMA descriptors Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260326-dma-dmac-handle-vunmap-v1-1-be3e46ffaf69@analog.com> References: <20260326-dma-dmac-handle-vunmap-v1-0-be3e46ffaf69@analog.com> In-Reply-To: <20260326-dma-dmac-handle-vunmap-v1-0-be3e46ffaf69@analog.com> To: dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Lars-Peter Clausen , Vinod Koul , Frank Li , Eliza Balas X-Mailer: b4 0.15.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1774532297; l=3532; i=nuno.sa@analog.com; s=20231116; h=from:subject:message-id; bh=ArkyMHkLTrvJR+/V/Q2CkIHL6iheUix3HHIOIkA3Mpw=; b=RUyqB7WtWiRAvwOa3jT05D/5y5137Gb3P0NL5ENxEpMuzze+6+r/4BdTFbRQ2ND6M5rR1LsQW 6SyB6L1uTAZClrXYQ3skmlzDtaGx+bm3L/kEHqqSOQlVQLqsn1nsyvx X-Developer-Key: i=nuno.sa@analog.com; a=ed25519; pk=3NQwYA013OUYZsmDFBf8rmyyr5iQlxV/9H4/Df83o1E= X-Endpoint-Received: by B4 Relay for nuno.sa@analog.com/20231116 with auth_id=100 X-Original-From: =?utf-8?q?Nuno_S=C3=A1?= Reply-To: nuno.sa@analog.com From: Eliza Balas This IP core can be used in architectures (like Microblaze) where DMA descriptors are allocated with vmalloc(). Hence, given that freeing the descriptors happen in softirq context, vunmpap() will BUG(). To solve the above, we setup a work item during allocation of the descriptors and schedule in softirq context. Hence, the actual freeing happens in threaded context. Signed-off-by: Eliza Balas Signed-off-by: Nuno S=C3=A1 --- drivers/dma/dma-axi-dmac.c | 48 +++++++++++++++++++++++++++++++++---------= ---- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 45c2c8e4bc45..df2668064ea2 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -133,6 +133,8 @@ struct axi_dmac_desc { struct virt_dma_desc vdesc; struct axi_dmac_chan *chan; =20 + struct work_struct sched_work; + bool cyclic; bool cyclic_eot; bool have_partial_xfer; @@ -650,6 +652,26 @@ static void axi_dmac_issue_pending(struct dma_chan *c) spin_unlock_irqrestore(&chan->vchan.lock, flags); } =20 +static void axi_dmac_free_desc(struct axi_dmac_desc *desc) +{ + struct axi_dmac *dmac =3D chan_to_axi_dmac(desc->chan); + struct device *dev =3D dmac->dma_dev.dev; + struct axi_dmac_hw_desc *hw =3D desc->sg[0].hw; + dma_addr_t hw_phys =3D desc->sg[0].hw_phys; + + dma_free_coherent(dev, PAGE_ALIGN(desc->num_sgs * sizeof(*hw)), + hw, hw_phys); + kfree(desc); +} + +static void axi_dmac_free_desc_schedule_work(struct work_struct *work) +{ + struct axi_dmac_desc *desc =3D container_of(work, struct axi_dmac_desc, + sched_work); + + axi_dmac_free_desc(desc); +} + static struct axi_dmac_desc * axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs) { @@ -687,21 +709,18 @@ axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsig= ned int num_sgs) /* The last hardware descriptor will trigger an interrupt */ desc->sg[num_sgs - 1].hw->flags =3D AXI_DMAC_HW_FLAG_LAST | AXI_DMAC_HW_F= LAG_IRQ; =20 + /* + * We need to setup a work item because this IP can be used on archs + * that rely on vmalloced memory for descriptors. And given that freeing + * the descriptors happens in softirq context, vunmpap() will BUG(). + * Hence, setup the worker so that we can queue it and free the + * descriptor in threaded context. + */ + INIT_WORK(&desc->sched_work, axi_dmac_free_desc_schedule_work); + return desc; } =20 -static void axi_dmac_free_desc(struct axi_dmac_desc *desc) -{ - struct axi_dmac *dmac =3D chan_to_axi_dmac(desc->chan); - struct device *dev =3D dmac->dma_dev.dev; - struct axi_dmac_hw_desc *hw =3D desc->sg[0].hw; - dma_addr_t hw_phys =3D desc->sg[0].hw_phys; - - dma_free_coherent(dev, PAGE_ALIGN(desc->num_sgs * sizeof(*hw)), - hw, hw_phys); - kfree(desc); -} - static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *c= han, enum dma_transfer_direction direction, dma_addr_t addr, unsigned int num_periods, unsigned int period_len, @@ -942,7 +961,10 @@ static void axi_dmac_free_chan_resources(struct dma_ch= an *c) =20 static void axi_dmac_desc_free(struct virt_dma_desc *vdesc) { - axi_dmac_free_desc(to_axi_dmac_desc(vdesc)); + struct axi_dmac_desc *desc =3D to_axi_dmac_desc(vdesc); + + /* See the comment in axi_dmac_alloc_desc() for the why! */ + schedule_work(&desc->sched_work); } =20 static bool axi_dmac_regmap_rdwr(struct device *dev, unsigned int reg) --=20 2.53.0 From nobody Thu Apr 2 20:21:31 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 44B192C21C2; Thu, 26 Mar 2026 13:37:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774532251; cv=none; b=IBg1koJr/G/8/5oN1izLiqQkpuwkHiIuxghPR+hYuyO1n1epBi/rpAM8UjhVPpgtKvUlY2iIK/foFWX4i5Wcdkreu3dGZCbf7+HLjq7OgwQBiZDxqT6eAc6Tg+MQEnDvZn4U3YrKPnJc0Tp85b0Fj6wpnOOFMGvT4OmuC4x+zCw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774532251; c=relaxed/simple; bh=6V7xCJfEpHcsI1kelRuOtMsngH7oGbsYsxC25INvh04=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=BhIR2Npn3BHNxOmTuQR6Xrn9olXn3Ua7ztktX8KCvg53BcYkOGKW81Sm5m2iKEgKKG8tFArj4oyrmbtF7FDCSpj8tJiRgEP20yphAfOisQ9iyN0rWH/4QtZzF9XfZT1Yd1XeMF6ClpECQntCswFe3IzboEvZt5oyw9feCX/oaGw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=RHBq9W3O; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="RHBq9W3O" Received: by smtp.kernel.org (Postfix) with ESMTPS id 1FAA3C116C6; Thu, 26 Mar 2026 13:37:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774532251; bh=6V7xCJfEpHcsI1kelRuOtMsngH7oGbsYsxC25INvh04=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=RHBq9W3OCscv5bMzBpdAotxFSWc54Dcwb/1PTX4+vSyKOB0rnaI1KoNDRATFdCnT4 kL49FKoM1EGKsLor3ajGW33XwRyTO6JDdIqDs4IQ1vIYp8SNXvuGQJi+1ASjQd99W8 sBZMvLWwk06vtuXISlWbzk+X5NaUl5ih2jUuC2CLQ6iuwAzTH6djhHEHH5nAa6Dfe5 btPrlEVQwsJ0kUFC6jYeHALR6ckPybNccEy8JQ80NWbbJUrN8xuVFbS5XX6NRQZZdk ysPsOTzlK92hXninJ8+2vegs4s5gJRVMr+IbPsHLOhRK8AilktAMzIQFLeODtbZ/Dp kyLChkV4XlEzQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 15FD210A62CF; Thu, 26 Mar 2026 13:37:31 +0000 (UTC) From: =?utf-8?q?Nuno_S=C3=A1_via_B4_Relay?= Date: Thu, 26 Mar 2026 13:37:36 +0000 Subject: [PATCH 2/2] dmaengine: dma-axi-dmac: fix use-after-free on unbind Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260326-dma-dmac-handle-vunmap-v1-2-be3e46ffaf69@analog.com> References: <20260326-dma-dmac-handle-vunmap-v1-0-be3e46ffaf69@analog.com> In-Reply-To: <20260326-dma-dmac-handle-vunmap-v1-0-be3e46ffaf69@analog.com> To: dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Lars-Peter Clausen , Vinod Koul , Frank Li X-Mailer: b4 0.15.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1774532297; l=4725; i=nuno.sa@analog.com; s=20231116; h=from:subject:message-id; bh=3oOip9GOTG6Og9wQ6FNEHbQ61iic0c2QfMR92xEdP2o=; b=/dP6+C3Zf6eLzl6gGhE28fBFM+vhQkt8yiwTx/pdRhKjPAKIBDlZ7VIyFRbvVpjz1pKBlMqRr QMBcTIqs95RDMJXuuvW5INrDOvaTyr8K6AxCH6rt7KpIpptsHBFS+hH X-Developer-Key: i=nuno.sa@analog.com; a=ed25519; pk=3NQwYA013OUYZsmDFBf8rmyyr5iQlxV/9H4/Df83o1E= X-Endpoint-Received: by B4 Relay for nuno.sa@analog.com/20231116 with auth_id=100 X-Original-From: =?utf-8?q?Nuno_S=C3=A1?= Reply-To: nuno.sa@analog.com From: Nuno S=C3=A1 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. Signed-off-by: Nuno S=C3=A1 --- drivers/dma/dma-axi-dmac.c | 47 ++++++++++++++++++++++++++++++++++++++++++= ---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index df2668064ea2..99454e096588 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -176,6 +176,8 @@ struct axi_dmac { =20 struct dma_device dma_dev; struct axi_dmac_chan chan; + + bool unbound; }; =20 static struct axi_dmac *chan_to_axi_dmac(struct axi_dmac_chan *chan) @@ -184,6 +186,11 @@ static struct axi_dmac *chan_to_axi_dmac(struct axi_dm= ac_chan *chan) dma_dev); } =20 +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); @@ -616,6 +623,11 @@ static int axi_dmac_terminate_all(struct dma_chan *c) LIST_HEAD(head); =20 spin_lock_irqsave(&chan->vchan.lock, flags); + if (dmac->unbound) { + /* We're gone */ + spin_unlock_irqrestore(&chan->vchan.lock, flags); + return -ENODEV; + } axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0); chan->next_desc =3D NULL; vchan_get_all_descriptors(&chan->vchan, &head); @@ -644,9 +656,12 @@ static void axi_dmac_issue_pending(struct dma_chan *c) if (chan->hw_sg) ctrl |=3D AXI_DMAC_CTRL_ENABLE_SG; =20 - 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); @@ -1206,6 +1221,14 @@ static int axi_dmac_detect_caps(struct axi_dmac *dma= c, unsigned int version) return 0; } =20 +static void axi_dmac_release(struct dma_device *dma_dev) +{ + struct axi_dmac *dmac =3D dev_to_axi_dmac(dma_dev); + + put_device(dma_dev->dev); + kfree(dmac); +} + static void axi_dmac_tasklet_kill(void *task) { tasklet_kill(task); @@ -1216,6 +1239,16 @@ static void axi_dmac_free_dma_controller(void *of_no= de) of_dma_controller_free(of_node); } =20 +static void axi_dmac_disable(void *__dmac) +{ + struct axi_dmac *dmac =3D __dmac; + + spin_lock(&dmac->chan.vchan.lock); + dmac->unbound =3D true; + spin_unlock(&dmac->chan.vchan.lock); + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0); +} + static int axi_dmac_probe(struct platform_device *pdev) { struct dma_device *dma_dev; @@ -1225,7 +1258,7 @@ static int axi_dmac_probe(struct platform_device *pde= v) u32 irq_mask =3D 0; int ret; =20 - dmac =3D devm_kzalloc(&pdev->dev, sizeof(*dmac), GFP_KERNEL); + dmac =3D kzalloc_obj(struct axi_dmac); if (!dmac) return -ENOMEM; =20 @@ -1270,9 +1303,10 @@ static int axi_dmac_probe(struct platform_device *pd= ev) dma_dev->device_prep_interleaved_dma =3D axi_dmac_prep_interleaved; dma_dev->device_terminate_all =3D axi_dmac_terminate_all; dma_dev->device_synchronize =3D axi_dmac_synchronize; - dma_dev->dev =3D &pdev->dev; + dma_dev->dev =3D get_device(&pdev->dev); dma_dev->src_addr_widths =3D BIT(dmac->chan.src_width); dma_dev->dst_addr_widths =3D BIT(dmac->chan.dest_width); + dma_dev->device_release =3D axi_dmac_release; dma_dev->directions =3D BIT(dmac->chan.direction); dma_dev->residue_granularity =3D DMA_RESIDUE_GRANULARITY_DESCRIPTOR; dma_dev->max_sg_burst =3D 31; /* 31 SGs maximum in one burst */ @@ -1326,6 +1360,11 @@ static int axi_dmac_probe(struct platform_device *pd= ev) if (ret) return ret; =20 + /* So that we can mark the device as unbound and disable it */ + ret =3D devm_add_action_or_reset(&pdev->dev, axi_dmac_disable, dmac); + if (ret) + return ret; + ret =3D devm_request_irq(&pdev->dev, dmac->irq, axi_dmac_interrupt_handle= r, IRQF_SHARED, dev_name(&pdev->dev), dmac); if (ret) --=20 2.53.0