From: Doruk Tan Ozturk <doruk@0sec.ai>
To: neil.armstrong@linaro.org, mchehab@kernel.org,
gregkh@linuxfoundation.org, khilman@baylibre.com
Cc: jbrunet@baylibre.com, martin.blumenstingl@googlemail.com,
hverkuil@kernel.org, linux-media@vger.kernel.org,
linux-amlogic@lists.infradead.org, linux-staging@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Doruk Tan Ozturk <doruk@0sec.ai>,
stable@vger.kernel.org
Subject: [PATCH v2] media: meson: vdec: fix use-after-free of decode work in stop/close path
Date: Tue, 16 Jun 2026 09:49:52 +0200 [thread overview]
Message-ID: <20260616074952.93076-1-doruk@0sec.ai> (raw)
The ESPARSER worker (esparser_queue_all_src(), scheduled via
sess->esparser_queue_work) dereferences sess->m2m_ctx and accesses the
ESPARSER/DOS registers. On the stop_streaming()/release() paths the
buffers and, on close, the m2m context are torn down while this worker
may still be pending or running, leading to a use-after-free of the
freed session state.
vdec_poweroff() previously only called vdec_ops->stop() and disabled the
clocks; it never synchronized against the worker. Two problems follow:
- The decode (VDEC) interrupt is threaded
(devm_request_threaded_irq(.., vdec_isr, vdec_threaded_isr, ..)) and
its threaded handler re-arms the worker through amvdec_dst_buf_done()
-> schedule_work(&sess->esparser_queue_work). A handler that is still
in flight can therefore queue the worker again after teardown has
begun.
- The worker touches ESPARSER/DOS registers, so it must not run after
the clocks have been disabled.
Quiesce everything in vdec_poweroff(), in order, before disabling the
clocks: vdec_ops->stop() masks the VDEC interrupt in hardware so no new
IRQ can be raised; synchronize_irq() on the VDEC line then drains any
threaded handler still in flight (the only context that re-arms the
worker); cancel_work_sync() finally cancels/waits for the worker. After
this nothing can re-arm the work, and the worker can no longer run with
clocks disabled or against a freed m2m context.
Only the VDEC interrupt is synchronized: the ESPARSER interrupt handler
(esparser_isr()) only acknowledges the start-code-found status and wakes
the internal wait queue used by esparser_write_data(); it never touches
the session or schedules the worker, so it cannot re-arm it.
stop_streaming()/release() run under the video device lock, not under
sess->lock (the mutex the worker takes), so cancel_work_sync() here
cannot deadlock against the worker.
The VDEC IRQ number is now stored in struct amvdec_core so it is
available to synchronize_irq() at teardown.
Fixes: 3e7f51bd9607 ("media: meson: add v4l2 m2m video decoder driver")
Cc: stable@vger.kernel.org
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
v2: also synchronize_irq() before cancelling the work, so a delayed
threaded IRQ cannot re-arm it after cancel_work_sync() (raised by
automated review). Only the VDEC IRQ is synchronized, as the
ESPARSER IRQ handler does not touch the session or schedule the work.
v1: https://lore.kernel.org/linux-media/20260615140529.52653-1-doruk@0sec.ai/
drivers/staging/media/meson/vdec/vdec.c | 21 +++++++++++++++++++++
drivers/staging/media/meson/vdec/vdec.h | 3 +++
2 files changed, 24 insertions(+)
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 4b77ec1af5a7..5304987546fa 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -123,6 +123,25 @@ static void vdec_poweroff(struct amvdec_session *sess)
codec_ops->drain(sess);
vdec_ops->stop(sess);
+
+ /*
+ * vdec_ops->stop() masks the VDEC interrupt at the hardware level, so
+ * no new IRQ can be raised past this point. The threaded ISR re-arms
+ * the ESPARSER worker via amvdec_dst_buf_done() (schedule_work()), so
+ * drain any in-flight handler before cancelling the worker, otherwise
+ * a late threaded IRQ could schedule it again after the cancel.
+ *
+ * The worker dereferences sess->m2m_ctx and touches the ESPARSER/DOS
+ * registers, so it must be cancelled while m2m_ctx is still valid and
+ * the clocks are still enabled, i.e. before the clk_disable below.
+ *
+ * This runs from the stop_streaming()/release() paths, which are
+ * serialized by the video device lock, not by sess->lock (the lock the
+ * worker takes), so cancel_work_sync() cannot deadlock here.
+ */
+ synchronize_irq(sess->core->vdec_irq);
+ cancel_work_sync(&sess->esparser_queue_work);
+
clk_disable_unprepare(sess->core->dos_clk);
clk_disable_unprepare(sess->core->dos_parser_clk);
}
@@ -1053,6 +1072,8 @@ static int vdec_probe(struct platform_device *pdev)
if (ret)
return ret;
+ core->vdec_irq = irq;
+
ret = esparser_init(pdev, core);
if (ret)
return ret;
diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h
index 7a5d8e871d70..9a50116a2665 100644
--- a/drivers/staging/media/meson/vdec/vdec.h
+++ b/drivers/staging/media/meson/vdec/vdec.h
@@ -66,6 +66,8 @@ struct amvdec_session;
* @v4l2_dev: v4l2 device
* @cur_sess: current decoding session
* @lock: video device lock
+ * @vdec_irq: IRQ line of the VDEC, used to synchronize the threaded ISR
+ * against teardown
*/
struct amvdec_core {
void __iomem *dos_base;
@@ -91,6 +93,7 @@ struct amvdec_core {
struct amvdec_session *cur_sess;
struct mutex lock;
+ int vdec_irq;
};
/**
--
2.43.0
next reply other threads:[~2026-06-16 7:50 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-16 7:49 Doruk Tan Ozturk [this message]
2026-06-17 7:41 ` [PATCH v2] media: meson: vdec: fix use-after-free of decode work in stop/close path Doruk Tan Ozturk
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260616074952.93076-1-doruk@0sec.ai \
--to=doruk@0sec.ai \
--cc=gregkh@linuxfoundation.org \
--cc=hverkuil@kernel.org \
--cc=jbrunet@baylibre.com \
--cc=khilman@baylibre.com \
--cc=linux-amlogic@lists.infradead.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-staging@lists.linux.dev \
--cc=martin.blumenstingl@googlemail.com \
--cc=mchehab@kernel.org \
--cc=neil.armstrong@linaro.org \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox