[PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown
sashiko-bot at kernel.org
sashiko-bot at kernel.org
Mon Jul 13 05:42:34 PDT 2026
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Unconditional nullification of `core->cur_sess` in `vdec_close()` allows concurrent sessions to clobber hardware ownership, triggering interrupt storms and severe memory leaks.
- [High] AB-BA deadlock between `vdec_stop_streaming()` and `esparser_queue_all_src()` workqueue due to `cancel_work_sync()`.
- [High] Inverted loop logic in `esparser_queue_all_src()` causes infinite CPU busy-wait on hardware backpressure and premature exit on success.
- [Medium] Memory leak of `sess->priv` on the newly added `vdec_start_streaming()` error path.
--
--- Patch [8]: [PATCH v7 08/19] media: meson: vdec: Fix concurrent execution races and unsafe teardown ---
commit 4587e6bbd091d6645be624dc185c0fadded8649b
Author: Anand Moon <linux.amoon at gmail.com>
media: meson: vdec: Fix concurrent execution races and unsafe teardown
Address data races involving 'should_stop' and prevent multi-session
hardware clobbering by enforcing atomic tracking and strict owner
validation during device teardown.
> diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
> index e5d3d817b9b2b..959673742e699 100644
> --- a/drivers/staging/media/meson/vdec/esparser.c
> +++ b/drivers/staging/media/meson/vdec/esparser.c
> @@ -379,7 +379,7 @@ void esparser_queue_all_src(struct work_struct *work)
>
> scoped_guard(mutex, &sess->lock) {
> /* Safe atomic tracking check: exit loop if session is shutting down */
> - if (sess->should_stop)
> + if (READ_ONCE(sess->should_stop))
> return;
[Severity: High]
Could the workqueue get stuck in an infinite loop due to how scoped_guard
expands?
Because scoped_guard(mutex, &sess->lock) expands to a for loop, if the
inner code hits an empty queue or -EAGAIN and calls break, it will only
exit the scoped_guard loop, leaving processed_frame as false.
The code then evaluates if (processed_frame) break; which fails, executes
cond_resched(), and loops the outer while (1) infinitely.
Conversely, if a frame is successfully processed (processed_frame is true),
does the loop break entirely, causing the workqueue to exit after processing
just a single frame?
[ ... ]
> diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
> index 6fe9722577179..83a9b1238972a 100644
> --- a/drivers/staging/media/meson/vdec/vdec.c
> +++ b/drivers/staging/media/meson/vdec/vdec.c
[ ... ]
> @@ -388,10 +392,12 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
> sess->streamon_cap = 0;
>
> mutex_lock(&core->lock);
> - if (core->cur_sess == sess)
> - core->cur_sess = NULL;
> - if (sess->status != STATUS_NEEDS_RESUME)
> - sess->status = STATUS_STOPPED;
> + if (core->cur_sess == sess) {
> + /* Safely clear hardware ownership since we were confirmed as the owner */
> + smp_store_release(&core->cur_sess, NULL);
> + if (sess->status != STATUS_NEEDS_RESUME)
> + sess->status = STATUS_STOPPED;
> + }
> mutex_unlock(&core->lock);
> err_unlock_no_hw:
[Severity: Medium]
Is there a memory leak of sess->priv on the error paths in
vdec_start_streaming()?
If vdec_poweron() succeeds (which dynamically allocates sess->priv), but
a later step like kthread_run() fails, the code jumps to err_poweroff.
While the hardware state and vififo_vaddr are rolled back, kfree(sess->priv)
appears to be missing.
If VIDIOC_STREAMON repeatedly fails, could this leak kernel memory?
[ ... ]
> @@ -493,26 +502,33 @@ static void vdec_stop_streaming(struct vb2_queue *q)
> cancel_work_sync(&sess->esparser_queue_work);
[Severity: High]
Can this cause an AB-BA deadlock between the V4L2 core and the workqueue?
When userspace calls VIDIOC_STREAMOFF, the V4L2 core acquires the queue lock
(sess->lock), then calls vdec_stop_streaming(), which calls
cancel_work_sync() here.
Meanwhile, the esparser_queue_all_src() workqueue loop uses
scoped_guard(mutex, &sess->lock) to acquire the exact same lock. If the
workqueue is active, it will block waiting for sess->lock, while
vdec_stop_streaming() blocks indefinitely waiting for the workqueue to exit.
> mutex_lock(&core->lock);
>
> - vdec_poweroff(sess);
> - vdec_free_canvas(sess);
[ ... ]
> @@ -1012,21 +1031,30 @@ static int vdec_close(struct file *file)
[ ... ]
> kfree(sess->priv);
> sess->priv = NULL;
>
> + /* Unconditionally set our local status to stopped */
> + sess->status = STATUS_STOPPED;
> + /* Safely clear hardware ownership since we were confirmed as the owner */
> + smp_store_release(&core->cur_sess, NULL);
> +
[Severity: Critical]
Does this unconditionally clear the hardware ownership, even if this session
doesn't own it?
If session A is streaming and session B is closed, vdec_close() for session
B will execute smp_store_release(&core->cur_sess, NULL) outside of the
if (core->cur_sess == sess) check.
If core->cur_sess becomes NULL while session A's hardware is active, could
the next hardware interrupt trigger vdec_isr() which sees !core->cur_sess
and returns IRQ_HANDLED without clearing the hardware interrupt status?
Since the interrupt is level-triggered, could this result in an immediate
interrupt storm that hangs the CPU, and permanently leak session A's
vififo_vaddr DMA buffers when session A is eventually closed?
> mutex_unlock(&core->lock);
>
> v4l2_m2m_ctx_release(sess->m2m_ctx);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260713120840.17427-1-linux.amoon@gmail.com?part=8
More information about the linux-amlogic
mailing list