[PATCH] media: marvell: mcam: stop DMA and cancel s_bh_work before freeing DMA buffers
Fan Wu <[email protected]> Fri, 7 Aug 2026 05:35:53 +0000
| Newsgroups | org.kernel.vger.linux-media,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
In vmalloc mode the frame-completion IRQ queues mcam_frame_work() on
cam->s_bh_work, which memcpy()s from the dma_bufs[] that
mcam_free_dma_bufs() frees with dma_free_coherent(). mccic_shutdown()
frees those buffers without stopping the controller, so while streaming
a late frame IRQ can re-arm the work after the buffers are gone, causing a
use-after-free.
Stop the controller in mccic_shutdown() when streaming is still active
(gated on an open fd, which holds a runtime-PM reference, so the device
is powered), cancel s_bh_work in mcam_free_dma_bufs(), and move
INIT_WORK() into mccic_register() before the device can be published.
Fixes: 67a8dbbc4e04 ("[media] marvell-cam: Basic working MMP camera driver")
Cc: [email protected]
Signed-off-by: Fan Wu <[email protected]>
---
drivers/media/platform/marvell/mcam-core.c | 14 ++++++++++++--
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/marvell/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c
index b8360d37000a..c9605317e8a7 100644
--- a/drivers/media/platform/marvell/mcam-core.c
+++ b/drivers/media/platform/marvell/mcam-core.c
@@ -406,6 +406,8 @@ static void mcam_free_dma_bufs(struct mcam_camera *cam)
{
int i;
+ cancel_work_sync(&cam->s_bh_work);
+
for (i = 0; i < cam->nbufs; i++) {
dma_free_coherent(cam->dev, cam->dma_buf_size,
cam->dma_bufs[i], cam->dma_handles[i]);
@@ -1306,7 +1308,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
break;
case B_vmalloc:
#ifdef MCAM_MODE_VMALLOC
- INIT_WORK(&cam->s_bh_work, mcam_frame_work);
vq->ops = &mcam_vb2_ops;
vq->mem_ops = &vb2_vmalloc_memops;
cam->dma_setup = mcam_ctlr_dma_vmalloc;
@@ -1864,6 +1865,12 @@ int mccic_register(struct mcam_camera *cam)
goto out;
}
+#ifdef MCAM_MODE_VMALLOC
+ /* Init before sensor bind: armed by IRQ, cancelled on probe-error paths. */
+ if (cam->buffer_mode == B_vmalloc)
+ INIT_WORK(&cam->s_bh_work, mcam_frame_work);
+#endif
+
mutex_init(&cam->s_mutex);
cam->state = S_NOTREADY;
mcam_set_config_needed(cam, 1);
@@ -1922,10 +1929,15 @@ void mccic_shutdown(struct mcam_camera *cam)
* take it down again will wedge the machine, which is frowned
* upon.
*/
+ mutex_lock(&cam->s_mutex);
if (!list_empty(&cam->vdev.fh_list)) {
cam_warn(cam, "Removing a device with users!\n");
+ /* Stop so the IRQ can't re-arm s_bh_work after the buffers are freed. */
+ if (cam->state == S_STREAMING)
+ mcam_ctlr_stop_dma(cam);
sensor_call(cam, core, s_power, 0);
}
+ mutex_unlock(&cam->s_mutex);
if (cam->buffer_mode == B_vmalloc)
mcam_free_dma_bufs(cam);
v4l2_ctrl_handler_free(&cam->ctrl_handler);
--
2.34.1