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]

Reply via email to