On Fri Aug 7 05:35:53 2026 +0000, Fan Wu wrote:
> 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]>
> Signed-off-by: Hans Verkuil <[email protected]>
Patch committed.
Thanks,
Hans Verkuil
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 c1e8a2a3012d..2d0b8b60c509 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);
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]